2015-02-10 19:41:24 +00:00
|
|
|
define(function(require, exports, module) {
|
|
|
|
main.consumes = ["vfs", "Plugin"];
|
|
|
|
main.provides = ["proc"];
|
|
|
|
return main;
|
|
|
|
|
|
|
|
function main(options, imports, register) {
|
|
|
|
var vfs = imports.vfs;
|
|
|
|
var Plugin = imports.Plugin;
|
|
|
|
|
|
|
|
/***** Initialization *****/
|
|
|
|
|
|
|
|
var plugin = new Plugin("Ajax.org", main.consumes);
|
|
|
|
var emit = plugin.getEmitter();
|
|
|
|
|
|
|
|
var tmuxName = options.tmuxName;
|
|
|
|
|
|
|
|
/***** Register and define API *****/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is a proxy for a running process in the workspace. This object exposes
|
|
|
|
* the stdin, stdout and stderr streams, which you can interact with to
|
|
|
|
* control the running process.
|
|
|
|
*
|
|
|
|
* See {@link proc#spawn}
|
|
|
|
*
|
|
|
|
* @class proc.Process
|
|
|
|
* @extends Object
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* the process ID of the running process.
|
|
|
|
* @property {Number} pid
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* the stdin (Standard Input) stream of this process.
|
|
|
|
* @property {proc.Stream} stdin
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* the stdout (Standard Output) stream of this process.
|
|
|
|
* @property {proc.Stream} stdout
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* the stderr (Standard Error) stream of this process.
|
|
|
|
* @property {proc.Stream} stderr
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Fires after the process ends. If the process terminated normally,
|
|
|
|
* code is the final exit code of the process; otherwise, it's null.
|
|
|
|
* If the process terminated due to receipt of a signal, signal is the string
|
|
|
|
* name of the signal; otherwise, it's null.
|
|
|
|
*
|
|
|
|
* Note that the child process stdio streams might still be open.
|
|
|
|
*
|
|
|
|
* @event exit
|
|
|
|
* @param {Number} code
|
|
|
|
* @param {String} signal
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Sends a signal to the child process. See [signal(7)](http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html)
|
|
|
|
* for a list of available signals.
|
|
|
|
*
|
|
|
|
* Note that while the function is called kill, the signal delivered to the
|
|
|
|
* child process may not actually kill it. kill really just sends a signal to
|
|
|
|
* a process.
|
|
|
|
*
|
|
|
|
* @method kill
|
|
|
|
* @param {String} signal
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Detaches the given child process.
|
|
|
|
*
|
|
|
|
* @method unref
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* A stream is an abstract interface implemented by various objects in
|
|
|
|
* Cloud9. Streams are readable, writable, or both.
|
|
|
|
* @class proc.Stream
|
|
|
|
* @extends Object
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* A boolean that is true for writable streams, but turns false after an error
|
|
|
|
* event occurs, the stream comes to an 'end', or if destroy() was called.
|
|
|
|
* @property {Boolean} writable
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Fires when data is received on the stream.
|
|
|
|
* @event data
|
|
|
|
* @param {String} chunk
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Fires when the stream has received an EOF. Indicates that no more data
|
|
|
|
* events will happen. If the stream is also writable, it may be possible to
|
|
|
|
* continue writing.
|
|
|
|
* @event end
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Fires if there was an error receiving data.
|
|
|
|
* @event error
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Emitted when the underlying resource (for example, the backing file
|
|
|
|
* descriptor) has been closed. Not all streams emit this.
|
|
|
|
* @event close
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Writes string with to the stream.
|
|
|
|
* @method write
|
|
|
|
* @param {String} data
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Terminates the stream with EOF or FIN. This call send queued write data
|
|
|
|
* before closing the stream.
|
|
|
|
* @method end
|
|
|
|
* @param {String} data
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Closes the underlying file descriptor.
|
|
|
|
* @method destroy
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* This is the Stream.prototype() method available on all Stream objects.
|
|
|
|
* It connects this read stream to a destination. Incoming data on this stream
|
|
|
|
* is then written to destination. The destination and source streams are kept
|
|
|
|
* in sync by Cloud9.
|
|
|
|
* @method pipe
|
|
|
|
* @param destination
|
|
|
|
* @param [options]
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Interface for the special stream object, passed by {@link proc#pty}. This
|
|
|
|
* object acts both like a stream and like a process at the same time.
|
|
|
|
* @class proc.PtyStream
|
|
|
|
* @extends Object
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Fires when data is received on the stream.
|
|
|
|
* @event data
|
|
|
|
* @param {String} chunk
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Fires after the process ends. If the process terminated normally,
|
|
|
|
* code is the final exit code of the process; otherwise, it's null.
|
|
|
|
* If the process terminated due to receipt of a signal, signal is the string
|
|
|
|
* name of the signal; otherwise, it's null.
|
|
|
|
*
|
|
|
|
* Note that the child process stdio streams might still be open.
|
|
|
|
*
|
|
|
|
* @event exit
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Writes string with to the stream.
|
|
|
|
* @method write
|
|
|
|
* @param {String} data
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Terminates the stream with EOF or FIN. This call send queued write data
|
|
|
|
* before closing the stream.
|
|
|
|
* @method end
|
|
|
|
* @param {String} data
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Terminates the PTY
|
|
|
|
* @method destroy
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* @method pipe
|
|
|
|
* @param destination
|
|
|
|
* @param [options]
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Provides access to process control in the workspace
|
|
|
|
* @singleton
|
|
|
|
**/
|
|
|
|
plugin.freezePublicAPI({
|
|
|
|
_events: [
|
|
|
|
/**
|
|
|
|
* @event beforeSpawn Fires right before a file is executed
|
|
|
|
* @cancellable
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.path the path to the file to execute
|
|
|
|
* @param {Object} e.options the options passed to the spawn function
|
|
|
|
*/
|
|
|
|
"beforeSpawn",
|
|
|
|
/**
|
|
|
|
* @event afterSpawn Fires right after a file is executed
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.path the path to the file to execute
|
|
|
|
* @param {proc.Process} e.process the process object returned by spawn
|
|
|
|
*/
|
|
|
|
"afterSpawn",
|
|
|
|
/**
|
|
|
|
* @event beforePty Fires right before a process is spawned
|
|
|
|
* @cancellable
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.path the path to the file to execute
|
|
|
|
* @param {Object} e.options the options passed to the spawn function
|
|
|
|
*/
|
|
|
|
"beforePty",
|
|
|
|
/** @event afterPty Fires right after a process is spawned
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.path the path to the file to execute
|
|
|
|
* @param {proc.Process} e.process the process object returned by spawn
|
|
|
|
*/
|
|
|
|
"afterPty",
|
|
|
|
/**
|
|
|
|
* @event beforeTmux Fires right before a process is spawned
|
|
|
|
* @cancellable
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.path the path to the file to execute
|
|
|
|
* @param {Object} e.options the options passed to the spawn function
|
|
|
|
*/
|
|
|
|
"beforeTmux",
|
|
|
|
/** @event afterTmux Fires right after a process is spawned
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.path the path to the file to execute
|
|
|
|
* @param {proc.Process} e.process the process object returned by spawn
|
|
|
|
*/
|
|
|
|
"afterTmux",
|
|
|
|
/** @event beforeExecFile Fires right before a file is executed
|
|
|
|
* @cancellable
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.path the path to the file to execute
|
|
|
|
* @param {Object} e.options the options passed to the spawn function
|
|
|
|
*/
|
|
|
|
"beforeExecFile",
|
|
|
|
/** @event afterExecFile Fires right after a file is executed
|
|
|
|
* @param {Object} e
|
|
|
|
* @param {String} e.path the path to the file to execute
|
|
|
|
* @param {proc.Stream} e.stdout The stdout stream
|
|
|
|
* @param {proc.Stream} e.stderr The stderr stream
|
|
|
|
* @param {Error} e.error the error if any
|
|
|
|
*/
|
|
|
|
"afterExecFile"
|
|
|
|
],
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Spawns a child process and returns a process object complete
|
|
|
|
* with three stdio streams.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* proc.spawn("ls", function(err, process) {
|
|
|
|
* if (err) throw err;
|
|
|
|
*
|
|
|
|
* process.stdout.on("data", function(chunk) {
|
|
|
|
* console.log(chunk);
|
|
|
|
* });
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* @param {String} path the path to the file to execute
|
|
|
|
* @param {Object} [options]
|
|
|
|
* @param {Array} [options.args] An array of args to pass to the executable.
|
|
|
|
* @param {String} [options.stdoutEncoding="utf8"] The encoding to use on the stdout stream.
|
|
|
|
* @param {String} [options.stderrEncoding="utf8"] The encoding to use on the stderr stream.
|
|
|
|
* @param {String} [options.cwd] Current working directory of the child process
|
|
|
|
* @param {Object} [options.stdio] Child's stdio configuration.
|
|
|
|
* @param {Object} [options.env] Environment key-value pairs
|
|
|
|
* @param {Boolean} [options.detached] The child will be a process group leader. (See below)
|
|
|
|
* @param {Number} [options.uid] Sets the user identity of the process. (See setuid(2).)
|
|
|
|
* @param {Number} [options.gid] Sets the group identity of the process. (See setgid(2).)
|
|
|
|
* @param {Boolean} [options.resumeStdin] Start reading from stdin, so the process doesn't exit
|
|
|
|
* @param {Boolean} [options.resolve] Resolve the path to the VFS root before spawning process
|
|
|
|
* @param {Function} callback
|
|
|
|
* @param {Error} callback.err The error object if one has occured.
|
|
|
|
* @param {proc.Process} callback.process The child process
|
|
|
|
* @fires beforeSpawn
|
|
|
|
* @fires afterSpawn
|
|
|
|
*/
|
|
|
|
spawn: function(path, options, callback) {
|
|
|
|
emit("beforeSpawn", {path: path, options: options});
|
|
|
|
|
|
|
|
if (!callback) { // Handle optional argument
|
|
|
|
callback = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
!options.stdoutEncoding && (options.stdoutEncoding = "utf8");
|
|
|
|
!options.stderrEncoding && (options.stderrEncoding = "utf8");
|
|
|
|
!options.stdinEncoding && (options.stdinEncoding = "utf8");
|
|
|
|
|
|
|
|
//@todo this can be optimized to resolve locally
|
|
|
|
if (options.resolve)
|
|
|
|
vfs.resolve(path, {}, exec);
|
|
|
|
else
|
|
|
|
exec(null, {path: path});
|
|
|
|
|
|
|
|
function exec(err, data) {
|
|
|
|
vfs.spawn(data.path, options, function(err, meta) {
|
|
|
|
callback(err, meta && meta.process);
|
|
|
|
|
|
|
|
emit("afterSpawn", {
|
|
|
|
path: path,
|
|
|
|
process: meta && meta.process
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Spawns a child process in a PTY and returns a stream object.
|
|
|
|
* Use this method if the process you wish to start requires a
|
|
|
|
* terminal (for instance VI).
|
|
|
|
*
|
|
|
|
* Note that, unless you know what you are doing, you generally
|
|
|
|
* want to use spawn, instead of pty. If you are looking for a way
|
|
|
|
* to run a process in an output window in Cloud9, then see
|
|
|
|
* {@link run#run}.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* proc.pty("bash", {
|
|
|
|
* args: ["-c", "vi", "helloworld"]
|
|
|
|
* }, function(err, pty) {
|
|
|
|
* if (err) throw err;
|
|
|
|
*
|
|
|
|
* pty.on("data", function(chunk) {
|
|
|
|
* console.log(chunk);
|
|
|
|
* });
|
|
|
|
* pty.write("ihello world\x27:wq");
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* @param {String} path the path to the file to execute
|
|
|
|
* @param {Object} [options]
|
|
|
|
* @param {Array} [options.args] An array of args to pass to the executable.
|
|
|
|
* @param {String} [options.name="xterm-color"] The terminal emulator name.
|
|
|
|
* @param {Number} [options.cols="80"] Number of cols in characters
|
|
|
|
* @param {Number} [options.rows="24"] Number of rows in characters
|
|
|
|
* @param {String} [options.cwd] Current working directory of the child process
|
|
|
|
* @param {Object} [options.env] Environment key-value pairs
|
|
|
|
* @param {Boolean} [options.resolve] Resolve the path to the VFS root before executing file
|
|
|
|
* @param {Function} callback
|
|
|
|
* @param {Error} callback.err The error object, if any
|
|
|
|
* @param {proc.PtyStream} callback.pty The stdout stream
|
|
|
|
* @fires beforePty
|
|
|
|
* @fires afterPty
|
|
|
|
*/
|
|
|
|
pty: function(path, options, callback) {
|
|
|
|
emit("beforePty", {path: path, options: options});
|
|
|
|
|
|
|
|
if (!options.encoding)
|
|
|
|
options.encoding = "utf8";
|
|
|
|
if (!options.name)
|
|
|
|
options.name = "xterm-color";
|
|
|
|
|
|
|
|
//@todo this can be optimized to resolve locally
|
|
|
|
if (options.resolve)
|
|
|
|
vfs.resolve(path, {}, exec);
|
|
|
|
else
|
|
|
|
exec(null, {path: path});
|
|
|
|
|
|
|
|
function exec(err, data) {
|
|
|
|
vfs.pty(data.path, options, function(err, meta) {
|
|
|
|
callback(err, meta && meta.pty);
|
|
|
|
|
|
|
|
emit("afterPty", {path: path, pty: meta && meta.pty});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2015-02-11 17:00:19 +00:00
|
|
|
* Spawns a child process in a TMUX session and returns a stream object.
|
2015-02-10 19:41:24 +00:00
|
|
|
* Use this method if the process you wish to start requires a
|
|
|
|
* terminal (for instance VI).
|
|
|
|
*
|
|
|
|
* Note that, unless you know what you are doing, you generally
|
|
|
|
* want to use spawn, instead of tmux. If you are looking for a way
|
|
|
|
* to run a process in an output window in Cloud9, then see
|
|
|
|
* {@link run#run}.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* proc.tmux("bash", {
|
|
|
|
* args: ["-c", "vi", "helloworld"]
|
|
|
|
* }, function(err, tmux) {
|
|
|
|
* if (err) throw err;
|
|
|
|
*
|
|
|
|
* tmux.on("data", function(chunk) {
|
|
|
|
* console.log(chunk);
|
|
|
|
* });
|
|
|
|
* tmux.write("ihello world\x27:wq");
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* @param {String} command The command to execute in the tmux session. Pass empty string to start the default shell and make sure kill is not set.
|
|
|
|
* @param {Object} [options]
|
|
|
|
* @param {String} [options.session] The name of the tmux session
|
|
|
|
* @param {Boolean} [options.kill] First kill an existing session
|
|
|
|
* @param {Boolean} [options.attach] Attach if the session exists
|
|
|
|
* @param {Boolean} [options.detach] Detach immediately after starting the process
|
|
|
|
* @param {Boolean} [options.detachOthers] Detach other clients immediately after starting the process
|
|
|
|
* @param {Boolean} [options.output] Act like an output pane
|
|
|
|
* @param {Boolean} [options.base] The base path to store the watch files
|
|
|
|
* @param {String} [options.name="xterm-color"] The terminal emulator name.
|
|
|
|
* @param {Number} [options.cols="80"] Number of cols in characters
|
|
|
|
* @param {Number} [options.rows="24"] Number of rows in characters
|
|
|
|
* @param {String} [options.cwd] Current working directory of the child process
|
|
|
|
* @param {Object} [options.env] Environment key-value pairs
|
|
|
|
* @param {Function} callback
|
|
|
|
* @param {Error} callback.err The error object, if any
|
|
|
|
* @param {proc.PtyStream} callback.pty The stdout stream
|
|
|
|
* @fires beforeTmux
|
|
|
|
* @fires afterTmux
|
|
|
|
*/
|
|
|
|
tmux: function(command, options, callback) {
|
|
|
|
emit("beforeTmux", {command: command, options: options});
|
|
|
|
|
|
|
|
if (!options.encoding)
|
|
|
|
options.encoding = "utf8";
|
|
|
|
if (!options.name)
|
|
|
|
options.name = "xterm-color";
|
|
|
|
if (tmuxName)
|
|
|
|
options.tmuxName = tmuxName;
|
|
|
|
|
|
|
|
options.command = command || "";
|
|
|
|
|
|
|
|
vfs.tmux("", options, function(err, meta) {
|
|
|
|
callback(err, meta && meta.pty, meta && meta.pid, meta || {});
|
|
|
|
|
|
|
|
emit("afterTmux", {
|
|
|
|
command: command,
|
|
|
|
pty: meta && meta.pty,
|
|
|
|
pid: meta && meta.pid
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes an executable file in the workspace and buffers the
|
|
|
|
* stdout and stderr until the process is complete.
|
|
|
|
*
|
|
|
|
* Note: The buffer can grow big and slow down the general IDE
|
|
|
|
* operations. Unless you know for certain that the output will be
|
|
|
|
* minimal (10kb or less), use {@link proc#spawn}.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* proc.execFile("find", {
|
|
|
|
* args: ["."]
|
|
|
|
* }, function(err, stdout, stderr) {
|
|
|
|
* console.log(stderr, stdout);
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* @param {String} path the path to the file to execute
|
|
|
|
* @param {Object} [options]
|
|
|
|
* @param {Array} [options.args] An array of args to pass to the executable.
|
|
|
|
* @param {String} [options.stdoutEncoding="utf8"] The encoding to use on the stdout stream. Defaults to .
|
|
|
|
* @param {String} [options.stderrEncoding="utf8"] The encoding to use on the stderr stream. Defaults to "utf8".
|
|
|
|
* @param {String} [options.cwd] Current working directory of the child process
|
|
|
|
* @param {Array} [options.stdio] Child's stdio configuration. (See above)
|
|
|
|
* @param {Object} [options.env] Environment key-value pairs
|
|
|
|
* @param {String} [options.encoding="utf8"]
|
|
|
|
* @param {Number} [options.timeout=0]
|
|
|
|
* @param {Number} [options.maxBuffer=200*1024]
|
|
|
|
* @param {String} [options.killSignal="SIGTERM"]
|
|
|
|
* @param {Boolean} [options.resumeStdin] Start reading from stdin, so the process doesn't exit
|
|
|
|
* @param {Boolean} [options.resolve] Resolve the path to the VFS root before executing file
|
|
|
|
* @param {Function} callback
|
|
|
|
* @param {Error} callback.error The error object if an error occurred.
|
|
|
|
* @param {String} callback.stdout The stdout buffer
|
|
|
|
* @param {String} callback.stderr The stderr buffer
|
|
|
|
* @fires beforeExecFile
|
|
|
|
* @fires afterExecFile
|
|
|
|
*/
|
|
|
|
execFile: function(path, options, callback) {
|
|
|
|
if (!callback)
|
|
|
|
return this.execFile(path, {}, arguments[1]);
|
|
|
|
|
|
|
|
emit("beforeExecFile", {path: path, options: options});
|
|
|
|
|
|
|
|
if (!options.encoding)
|
|
|
|
options.encoding = "utf8";
|
|
|
|
|
|
|
|
//@todo this can be optimized to resolve locally
|
|
|
|
if (options.resolve)
|
|
|
|
vfs.resolve(path, {}, exec);
|
|
|
|
else
|
|
|
|
exec(null, {path: path});
|
|
|
|
|
|
|
|
function exec(err, data) {
|
|
|
|
vfs.execFile(data.path, options, function(err, e) {
|
|
|
|
var stdout = (err || e).stdout;
|
|
|
|
var stderr = (err || e).stderr;
|
|
|
|
|
|
|
|
callback(err, stdout, stderr);
|
|
|
|
|
|
|
|
emit("afterExecFile", {
|
|
|
|
path: path,
|
|
|
|
stdout: stdout,
|
|
|
|
stderr: stderr,
|
|
|
|
error: err
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
register(null, {
|
|
|
|
proc: plugin
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|