(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (process){
var __makeProgressPromise = function(promise) {
    var callbacks = [];

    promise.progress = function(f) {
        callbacks.push(f);
        return this;
    };

    promise.onProgress = function(data) {
        callbacks.forEach(function(f) { f(data); });
    };

    var fixThen = function(promise) {
        if (typeof Promise.prototype.then === "undefined") {
            var oldThen = promise.then;
        }
        else {
            var oldThen = Promise.prototype.then.bind(promise);
        }
        promise.then = function(f, g) {
            var result = oldThen(f, g);
            result.progress = promise.progress.bind(result);
            fixThen(result);
            return result;
        };
    };

    fixThen(promise);

    return promise;
};


// Browser API
function apiContext() {
    var __parse = function(flags, positional) {
        if (typeof flags === "undefined") {
            flags = {};
        }
        if (typeof positional === "undefined") {
            positional = [];
        }

        var data = flags;
        data.positional = positional;

        return data;
    }

    var rpcApi = function(command, data) {
        // URL structure: /api/command
        // Flags are GET query string or POST body
        // Positional is in query string or POST body

        // Translation of JS flag camelCase to command line flag
        // dashed-version occurs server-side

        var method = 'post';
        if (['info', 'list', 'search'].indexOf(command) !== -1 ||
            command === 'config' && data.get) {
            method = 'get';
        }

        var contentType = '';
        if (method === 'post') {
            contentType = 'application/json';
            data = JSON.stringify(data);
        }

        return Promise.resolve($.ajax({
            contentType: contentType,
            data: data,
            dataType: 'json',
            type: method,
            url: context.conda.API_ROOT + "/" + command
        }));
    };

    var restApi = function(command, data) {
        // URL structure is same as RPC API, except commands involving an
        // environment are structured more RESTfully - additionally, we use
        // GET/POST/PUT/DELETE based on the subcommand.
        // Commands involving --name and --prefix are translated to
        // /api/env/name/<name>/subcommand<? other args>
        var url = '';

        if (typeof data.name !== "undefined") {
            url += '/env/name/' + encodeURIComponent(data.name);
        }
        else if (typeof data.prefix !== "undefined") {
            // Double-encode so URL routers aren't confused by slashes if
            // they decode before routing
            url += '/env/prefix/' + encodeURIComponent(encodeURIComponent(data.prefix));
        }

        delete data['name'];
        delete data['prefix'];

        if (['install', 'update', 'remove'].indexOf(command) > -1) {
            if (data.positional.length > 1) {
                throw new context.conda.CondaError('conda: REST API supports only manipulating one package at a time');
            }
            if (data.positional.length === 1) {
                url += '/' + data.positional[0];
            }
            data.positional = [];
        }
        else if (command === 'run' && url.slice(0, 4) === '/env') {
            url += '/' + data.positional[0] + '/run';
            data.positional = [];
        }
        else if (command === 'create' || command === 'list') {
            // Ignore these - don't append the command to the URL
        }
        else {
            url += '/' + command;
        }

        var method = {
            'install': 'post',
            'create': 'post',
            'update': 'put',
            'remove': 'delete'
        }[command];
        if (typeof method === "undefined") {
            method = 'get';
        }

        if (command === 'config') {
            if (typeof data.add !== "undefined") {
                method = 'put';
                url += '/' + data.add[0] + '/' + data.add[1];
            }
            else if (typeof data.set !== "undefined") {
                method = 'put';
                data.value = data.set[1];
                url += '/' + data.set[0];
            }
            else if (typeof data.remove !== "undefined") {
                method = 'delete';
                url += '/' + data.remove[0] + '/' + data.remove[1];
            }
            else if (typeof data.removeKey !== "undefined") {
                method = 'delete';
                url += '/' + data.removeKey;
            }
            else if (typeof data.get !== "undefined" && data.get !== true) {
                url += '/' + data.get;
            }
            delete data['get'];
            delete data['add'];
            delete data['set'];
            delete data['remove'];
            delete data['removeKey'];
        }

        if (typeof data.positional !== "undefined" && data.positional.length > 0) {
            data.q = data.positional;
        }
        delete data.positional;

        if (method !== 'get') {
            data = JSON.stringify(data);
        }
        return Promise.resolve($.ajax({
            contentType: 'application/json',
            data: data,
            dataType: 'json',
            type: method,
            url: context.conda.API_ROOT + url
        }));
    };

    var api = function(command, flags, positional) {
        var data = __parse(flags, positional);

        if (flags && typeof flags.quiet !== "undefined" && flags.quiet === false) {
            // Handle progress bars
            return progressApi(command, flags, positional);
        }

        if (context.conda.API_METHOD === "RPC") {
            return rpcApi(command, data);
        }
        else if (context.conda.API_METHOD === "REST") {
            return restApi(command, data);
        }
        else {
            throw new context.conda.CondaError("conda: Unrecognized API_METHOD " + context.conda.API_METHOD);
        }
    };
    var context = api;

    // Returns Promise like api(), but this object has additional callbacks
    // for progress bars. Retrieves data via websocket.
    var progressApi = function(command, flags, positional) {
        var promise = new Promise(function(fulfill, reject) {
            var data = __parse(flags, positional);
            positional = data.positional;
            delete data.positional;

            var socket = new SockJS(context.conda.API_ROOT + '_ws/');
            socket.onopen = function() {
                socket.send(JSON.stringify({
                    subcommand: command,
                    flags: data,
                    positional: positional
                }));
            };
            socket.onmessage = function(e) {
                var data = JSON.parse(e.data);
                if (typeof data.progress !== "undefined") {
                    promise.onProgress(data.progress);
                }
                else if (typeof data.finished !== "undefined") {
                    fulfill(data.finished);
                }
            };
        });

        return __makeProgressPromise(promise);
    };

    return api;
}

var newContext = function() {
    var api = apiContext();
    var conda = factory(api);
    api.conda = conda;
    return conda;
};

// Set up module to run in browser and in Node.js
// Based loosely on https://github.com/umdjs/umd/blob/master/nodeAdapter.js
if ((typeof module === 'object' && typeof define !== 'function') || (window && window.nodeRequire)) {
    // We are in Node.js or Node-webkit/Atom Shell

    // We don't want to redefine require - in the browser it confuses AMD
    // apps and in Node it confuses browserify.
    if (typeof window !== "undefined" && window.nodeRequire) {
        var ChildProcess = window.nodeRequire('child_process');
        // We assume the Promise polyfill has been included.
        var Promise = window.Promise;
    }
    else {
        var ChildProcess = require('child_process');
        var Promise = require('promise');
    }

    // converts a name like useIndexCache to --use-index-cache
    var __convert = function(f) {
        return "--" + f.replace(/([A-Z])/g, function(a, b) { return "-" + b.toLocaleLowerCase(); });
    };

    var __parse = function(command, flags, positional) {
        if (typeof flags === "undefined") { flags = {}; }
        if (typeof positional === "undefined") { positional = []; }

        var cmdList = [command];

        for (var key in flags) {
            if (flags.hasOwnProperty(key)) {
                var value = flags[key];
                if (value !== false && value !== null) {
                    cmdList.push(__convert(key));

                    if (Array.isArray(value)) {
                        cmdList = cmdList.concat(value);
                    }
                    else if (value !== true) {
                        cmdList.push(value);
                    }
                }
            }
        }

        cmdList = cmdList.concat(positional);
        cmdList.push('--json');

        return cmdList;
    }

    var __spawn = function(cmdList) {
        var conda = ChildProcess.spawn('conda', cmdList, { env: process.env });
        conda.stdout.setEncoding('utf8');
        return conda;
    };

    var api = function(command, flags, positional) {
        var cmdList = __parse(command, flags, positional);

        if (flags && typeof flags.quiet !== "undefined" && flags.quiet === false) {
            // Handle progress bars
            return progressApi(command, flags, positional);
        }

        var promise = new Promise(function(fulfill, reject) {
            try {
                var conda = __spawn(cmdList);
            }
            catch (ex) {
                reject({
                    'exception': ex
                });
                return;
            }

            var buffer = [];
            conda.stdout.on('data', function(data) {
                buffer.push(data);
            });

            conda.on('close', function() {
                try {
                    fulfill(JSON.parse(buffer.join('')));
                }
                catch (ex) {
                    reject({
                        'exception': ex,
                        'result': buffer.join('')
                    });
                }
            });
        });
        return promise;
    };

    // Returns Promise like api(), but this object has additional callbacks
    // for progress bars. Retrieves data via ChildProcess.
    var progressApi = function(command, flags, positional) {
        var cmdList = __parse(command, flags, positional);
        var promise = new Promise(function(fulfill, reject) {
            try {
                var conda = __spawn(cmdList);
            }
            catch (ex) {
                reject({
                    'exception': ex
                });
                return;
            }

            var progressing = true;
            var buffer = [];
            conda.stdout.on('data', function(data) {
                var rest = data;
                if (rest.indexOf('\0') == -1) {
                    progressing = false;
                }
                else {
                    // Handles multiple progress bars (e.g. fetch then install)
                    progressing = true;
                }

                if (!progressing) {
                    buffer.push(data);
                    return;
                }
                while (rest.indexOf('\0') > -1 && progressing) {
                    var dataEnd = rest.indexOf('\0');
                    var first = rest.slice(0, dataEnd);
                    rest = rest.slice(dataEnd + 1);
                    buffer.push(first);
                    try {
                        var json = JSON.parse(buffer.join(''));
                        buffer = [];
                        promise.onProgress(json);
                    }
                    catch(ex) {
                        console.log(ex)
                    }

                    if (json.finished === true && rest.indexOf('\0') === -1) {
                        progressing = false;
                    }
                }
                if (rest) {
                    buffer.push(rest);
                }
            });

            conda.on('close', function() {
                try {
                    fulfill(JSON.parse(buffer.join('')));
                }
                catch(ex) {
                    reject({
                        'exception': ex,
                        'result': buffer.join('')
                    });
                }
            });
        });
        return __makeProgressPromise(promise);
    };

    if (typeof window !== "undefined" && typeof window.nodeRequire !== "undefined") {
        window.conda = factory(api);
        window.conda.api = api;
        window.conda.progressApi = api;
        // For node-webkit/Atom Shell we provide the browser API as well, as
        // these environments are a mix of Node and browser
        window.conda.newContext = newContext;
    }
    if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
        module.exports = factory(api);
        module.exports.api = api;
        module.exports.progressApi = progressApi;
    }
}
else {
    // We are in the browser
    window.conda = newContext();
    window.conda.newContext = newContext;
}

function factory(api) {
    "use strict";

    // TODO make this context-dependent
    var PATH_SEP = '/';
    if (typeof process !== "undefined") {
        if (process.platform === 'win32') {
            PATH_SEP = '\\';
        }
    }
    else if (typeof navigator !== "undefined") {
        if (/windows/.test(navigator.userAgent.toLowerCase())) {
            PATH_SEP = '\\';
        }
    }

    var defaultOptions = function(options, defaults) {
        if (typeof options === "undefined" || options === null) {
            return defaults;
        }
        for (var key in defaults) {
            if (defaults.hasOwnProperty(key)) {
                if (typeof options[key] === "undefined") {
                    options[key] = defaults[key];
                }
            }
        }

        return options;
    };

    var nameOrPrefixOptions = function(name, options, defaults) {
        defaults.name = null;
        defaults.prefix = null;

        options = defaultOptions(options, defaults);
        if (!(options.name || options.prefix)) {
            throw new CondaError(name + ": either name or prefix required");
        }
        if (options.name && options.prefix) {
            throw new CondaError(name + ": exactly one of name or prefix allowed");
        }

        return options;
    };

    var CondaError = (function() {
        function CondaError(message) {
            this.message = message;
        }

        CondaError.prototype.__proto__ = new Error;

        CondaError.prototype.toString = function() {
            return "CondaError: " + this.message;
        };

        return CondaError;
    })();

    var Env = (function() {
        function Env(name, prefix) {
            this.name = name;
            this.prefix = prefix;

            this.is_default = false;
            this.is_root = false;

            this.installed = {};
            this.history = [];
        }

        Env.prototype.linked = function(options) {
            options = defaultOptions(options, { simple: false });

            return api('list', { prefix: this.prefix }).then(function(fns) {
                if (options.simple) {
                    return fns;
                }

                var promises = [];
                for (var i = 0; i < fns.length; i++) {
                    promises.push(Package.load(fns[i]));
                }
                return Promise.all(promises).then(function(pkgs) {
                    this.installed = {};
                    pkgs.forEach(function(pkg) {
                        this.installed[pkg.name] = pkg;
                    }.bind(this));
                    return pkgs;
                }.bind(this));
            }.bind(this));
        };

        Env.prototype.revisions = function() {
            return api('list', { prefix: this.prefix, revisions: true })
                .then(function(revisions) {
                    this.history = revisions;
                    return revisions;
                }.bind(this));
        };

        Env.prototype.install = function(options) {
            options = defaultOptions(options, {
                progress: false,
                packages: []
            });

            if (options.packages.length === 0) {
                throw new CondaError("Env.install: must specify at least one package");
            }

            var packages = options.packages;
            delete options.packages;

            options.quiet = !options.progress;
            delete options.progress;
            options.prefix = this.prefix;

            return api('install', options, packages);
        };

        Env.prototype.update = function(options) {
            options = defaultOptions(options, {
                packages: [],
                dryRun: false,
                unknown: false,
                noDeps: false,
                useIndexCache: false,
                useLocal: false,
                noPin: false,
                all: false,
                progress: false
            });

            if (options.packages.length === 0 && !options.all) {
                throw new CondaError("Env.update: must specify packages to update or all");
            }

            var packages = options.packages;
            delete options.packages;

            options.quiet = !options.progress;
            delete options.progress;
            options.prefix = this.prefix;

            return api('update', options, packages);
        };

        Env.prototype.remove = function(options) {
            options = defaultOptions(options, {
                progress: false,
                packages: []
            });

            if (options.packages.length === 0) {
                throw new CondaError("Env.remove: must specify at least one package");
            }

            var packages = options.packages;
            delete options.packages;

            options.quiet = !options.progress;
            delete options.progress;
            options.prefix = this.prefix;

            return api('remove', options, packages);
        };

        Env.prototype.clone = function(options) {
            var options = nameOrPrefixOptions("Env.clone", options, {
                progress: false
            });

            options.clone = this.prefix;
            options.quiet = !options.progress;
            delete options.progress;

            return api('create', options).then(function(data) {
                if (typeof data.success !== "undefined" && data.success) {
                    data.env = new Env(options.name, data.actions.PREFIX);
                    return data;
                }
                else {
                    this.reject(data);
                }
            });
        };

        Env.prototype.run = function(options) {
            var options = defaultOptions(options, {
                name: null,
                pkg: null
            });

            if (!(options.name || options.pkg)) {
                throw new CondaError("Env.run: either name or pkg needed");
            }
            if (options.name && options.pkg) {
                throw new CondaError("Env.run: exactly one of name or pkg allowed");
            }

            var pkg = options.name;
            if (options.pkg) {
                pkg = options.pkg;
            }

            return api('run', { prefix: this.prefix }, [pkg]);
        };

        Env.prototype.removeEnv = function(options) {
            options = defaultOptions(options, {
                progress: false,
                forcePscheck: false
            });

            return api('remove', {
                all: true,
                prefix: this.prefix,
                quiet: !options.progress,
                forcePscheck: options.forcePscheck
            });
        };

        Env.create = function(options) {
            var options = nameOrPrefixOptions("Env.create", options, {
                progress: false,
                packages: [],
                forcePscheck: false
            });

            if (options.packages.length === 0) {
                throw new CondaError("Env.create: at least one package required");
            }

            var packages = options.packages;
            delete options.packages;
            options.quiet = !options.progress;
            delete options.progress;

            var progress = api('create', options, packages);
            var promise = progress.then(function(data) {
                if (typeof data.success !== "undefined" && data.success) {
                    data.env = new Env(options.name, data.actions.PREFIX);
                }
                return data;
            });
            return promise;
        };

        Env.getEnvs = function() {
            return info().then(function(info) {
                var envs = [new Env('root', info.default_prefix)];

                var prefixes = info.envs;
                for (var i = 0; i < prefixes.length; i++) {
                    var prefix = prefixes[i];
                    var name = prefix.split(PATH_SEP);
                    name = name[name.length - 1];
                    envs.push(new Env(name, prefix));
                }

                envs.forEach(function(env) {
                    env.is_default = env.prefix == info.default_prefix;
                    env.is_root = env.prefix == info.root_prefix;
                });
                return envs;
            });
        };

        Env.getRoot = function() {
            return info().then(function(info) {
                var root = new Env('root', info.default_prefix);
                root.isDefault = true;
                root.isRoot = true;

                return root;
            });
        };

        /**
           Sync method for Backbone collections.
         */
        Env.backboneSync = function(method, model, options) {
            switch (method) {
            case "read":
                Env.getEnvs().then(function(envs) {
                    var promises = [];
                    envs.forEach(function(env) {
                        if (typeof options.loadLinked === "undefined" ||
                            options.loadLinked) {
                            promises.push(env.linked());
                        }
                        if (typeof options.loadRevisions === "undefined" ||
                            options.loadRevisions) {
                            promises.push(env.revisions());
                        }
                        env.id = env.prefix ? env.prefix : env.name;
                    });

                    Promise.all(promises).then(function() {
                        options.success(envs);
                    });
                });
                break;

            case "delete":
                var forcePscheck = false;
                if (typeof options.forcePscheck !== "undefined") {
                    forcePscheck = options.forcePscheck;
                }
                return model.attributes.removeEnv({ forcePscheck: forcePscheck }).then(function(result) {
                    options.success(result);
                });
                break;

            default:
                console.log("Env.backboneSync: cannot handle method " + method);
            }
        };

        /**
           Construct an instance of this class from JSON.

           Intended to be used as the model attribute for a Backbone
           Collection when the server is exposing a RESTful API.
         */
        Env.backboneModel = function(data) {
        };

        return Env;
    })();

    var Package = (function() {
        var _cache = {};

        function Package(fn, info) {
            _cache[fn] = this;
            this.fn = fn;
            this.name = info.name;
            this.build = info.build;
            this.build_number = info.build_number;
            this.dist = this.fn;
            this.version = info.version;
            this.info = info;
        }

        Package.prototype.reload = function() {
            return Package.load(this.fn).then(function(pkg) {
                this.info = pkg.info;
            }.bind(this));
        };

        Package.splitFn = function(fn) {
            var parts = fn.split('-');
            return {
                name: parts.slice(0, -2).join('-'),
                build: parts[parts.length - 1],
                version: parts[parts.length - 2]
            };
        };

        /**
           Parse a version string

           Matches 2.1, 2.1.3, 2.1.3a, 2.1.3a2, 2.1.4rc1, 2.1.5.2, ...
           Note: 2.1.3a == 2.1.3a0
         */
        Package.parseVersion = function(version) {
            var matches = version.match(/(\d+)\.(\d+)((?:\.\d+)*)([a-zA-Z]+(?:\d+)?)?/);
            if (!matches) {
                throw new CondaError("Package.parseVersion: Cannot parse version " + version);
            }
            var parts = [parseInt(matches[1], 10), parseInt(matches[2], 10)];
            var extra = matches[3];
            if (typeof extra !== "undefined") {
                extra = extra.split(/\./g).slice(1);
                extra.forEach(function(e) {
                    parts.push(parseInt(e, 10));
                });
            }
            var suffixMatch = matches[4];
            var suffix = null;
            var suffixNumber = null;
            if (typeof suffixMatch !== "undefined") {
                var suffixParts = suffixMatch.split(/(\d+)/);
                suffix = suffixParts[0];
                suffixNumber = 0;
                if (suffixParts.length > 1) {
                    suffixNumber = parseInt(suffixParts[1], 10);
                }
            }
            return {
                parts: parts,
                suffix: suffix,
                suffixNumber: suffixNumber
            };
        };

        /** Is pkg2 newer than pkg1
         */
        Package.isGreater = function(pkg1, pkg2) {
            if (pkg1.version === pkg2.version) {
                return pkg2.build_number > pkg1.build_number;
            }

            try {
                var parts1 = Package.parseVersion(pkg1.version);
                var parts2 = Package.parseVersion(pkg2.version);
            }
            catch (e) {
                return pkg2.version > pkg1.version;
            }
            for (var i = 0, len = Math.max(parts1.parts.length, parts2.parts.length);
                 i < len; i++) {
                var part1 = parts1.parts[i];
                var part2 = parts2.parts[i];
                var part1d = typeof part1 !== "undefined";
                var part2d = typeof part2 !== "undefined";

                if (part1d && !part2d) {
                    return false;
                }
                else if (!part1d && part2d) {
                    return true;
                }

                if (part2 > part1) {
                    return true;
                }
                if (part2 < part1) {
                    return false;
                }
            }

            if (parts1.suffix !== null && parts2.suffix === null) {
                return true;
            }
            else if (parts1.suffix === null && parts2.suffix !== null) {
                return false;
            }
            else if (parts1.suffix !== null && parts2.suffix !== null &&
                     parts1.suffix === parts2.suffix) {
                return parts2.suffixNumber > parts1.suffixNumber;
            }
            else {
                return parts2.suffix > parts1.suffix;
            }
        };

        Package.load = function(fn, reload) {
            // This can get quite expensive. To deal with that:
            // 1. Cache Package objects.
            // 2. Load data from `conda search`'s index.
            // 3. Cache that index.
            // 4. Fall back on `conda info` only if package is not in index
            // (when the package was built/installed locally, for instance)

            if (!_cache.hasOwnProperty(fn) || reload) {
                return index().then(function(search_cache) {
                    var spec = Package.splitFn(fn);
                    var packages = search_cache[spec.name];
                    if (typeof packages === "undefined") {
                        return api('info', {}, fn + '.tar.bz2').then(function(info) {
                            info = info[fn + '.tar.bz2'];
                            var pkg = new Package(fn, info);
                            return pkg;
                        });
                    }

                    var pkgInfo;
                    for (var i = 0; i < packages.length; i++) {
                        var info = packages[i];
                        if (info.build === spec.build && info.version === spec.version) {
                            pkgInfo = info;
                            break;
                        }
                    }

                    var pkg = new Package(fn, info);
                    _cache[fn] = pkg;
                    return pkg;
                });
            }
            else {
                return Promise.resolve(_cache[fn]);
            }
        };

        return Package;
    })();

    var Config = (function() {
        var __warn_result = function(result) {
            if (result.warnings && result.warnings.length) {
                console.log("Warnings for conda config:");
                console.log(result.warnings);
            }
            return result;
        };
        var __merge = function(dest, src) {
            for (var key in src) {
                if (src.hasOwnProperty(key)) {
                    dest[key] = src[key];
                }
            }

            return dest;
        };
        var ALLOWED_KEYS = ['channels', 'disallow', 'create_default_packages',
            'track_features', 'envs_dirs', 'always_yes', 'allow_softlinks', 'changeps1',
            'use_pip', 'binstar_upload', 'binstar_personal', 'show_channel_urls',
            'allow_other_channels', 'ssl_verify'];

        var __check_keys = function(f) {
            return function() {
                var key = arguments[0];
                if (ALLOWED_KEYS.indexOf(key) === -1) {
                    throw new CondaError(
                        "Config.get: key " + key + " not allowed. Key must be one of "
                            + ALLOWED_KEYS.join(', '));
                }
                return f.apply(f, Array.prototype.slice.call(arguments));
            };
        };

        function Config(options) {
            options = defaultOptions(options, {
                system: false,
                file: null
            });
            this.system = options.system;
            this.file = options.file;
            this.options = {};

            if (options.system && options.file !== null) {
                throw new CondaError("Config: at most one of system, file allowed");
            }

            if (options.system) {
                this.options.system = true;
            }
            else if (options.file !== null) {
                this.options.file = options.file;
            }
        }

        Config.prototype.rcPath = function() {
            var call = api('config', __merge({ get: true }, this.options));
            return call.then(function(result) {
                return result.rc_path;
            });
        };

        Config.prototype.get = __check_keys(function(key) {
            var call = api('config', __merge({ get: key }, this.options));
            return call.then(__warn_result).then(function(result) {
                if (typeof result.get[key] !== "undefined") {
                    return {
                        value: result.get[key],
                        set: true
                    };
                }
                else {
                    return {
                        value: undefined,
                        set: false
                    };
                }
            });
        });

        Config.prototype.getAll = function() {
            var call = api('config', __merge({ get: true }, this.options));
            return call.then(function(result) {
                return result.get;
            });
        };

        // TODO disallow non iterable keys
        Config.prototype.add = __check_keys(function(key, value) {
            var call = api('config', __merge({ add: [key, value], force: true }, this.options));
            return call.then(__warn_result);
        });

        Config.prototype.set = __check_keys(function(key, value) {
            var call = api('config', __merge({ set: [key, value], force: true }, this.options));
            return call.then(__warn_result);
        });

        Config.prototype.remove = __check_keys(function(key, value) {
            var call = api('config', __merge({ remove: [key, value], force: true }, this.options));
            return call.then(__warn_result);
        });

        Config.prototype.removeKey = __check_keys(function(key) {
            var call = api('config', __merge({ removeKey: key, force: true }, this.options));
            return call.then(__warn_result);
        });

        return Config;
    })();

    var info = function() {
        return api('info');
    };

    var search = function(options) {
        options = defaultOptions(options, {
            regex: null,
            spec: null,
            useIndexCache: false,
            outdated: false,
            unknown: false
        });

        if (options.regex && options.spec) {
            throw new CondaError("conda.search: only one of regex and spec allowed");
        }

        var positional = [];

        if (options.regex !== null) {
            positional.push(options.regex);
        }
        if (options.spec !== null) {
            positional.push(options.spec);
            options.spec = true;
        }
        else {
            delete options.spec;
        }
        delete options.regex;

        return api('search', options, positional);
    };

    /**
       The package index - conda search --json.

       This method caches the index as it is an expensive call (1-4 seconds).
     */
    var _search_cache = null;
    var index = function(options) {
        options = defaultOptions(options, {
            reload: false
        });

        if (_search_cache === null || options.reload) {
            _search_cache = search({ useIndexCache: !options.reload, unknown: true });
        }

        return _search_cache;
    };

    var run = function(command) {
        return api('run', {}, [command]);
    };

    var clean = function(options) {
        options = defaultOptions(options, {
            dryRun: false,
            indexCache: false,
            lock: false,
            tarballs: false,
            packages: false
        });

        if (!(options.indexCache || options.lock ||
              options.tarballs || options.packages)) {
            throw new CondaError("conda.clean: at least one of indexCache, " +
                                 "lock, tarballs, or packages required");
        }

        return api('clean', options);
    };

    return {
        clean: clean,
        index: index,
        info: info,
        run: run,
        search: search,
        CondaError: CondaError,
        Config: Config,
        Env: Env,
        Package: Package,
        API_ROOT: '/api',
        API_METHOD: 'RPC'
    };
}

}).call(this,require("FWaASH"))
},{"FWaASH":7,"child_process":2,"promise":9}],2:[function(require,module,exports){

},{}],3:[function(require,module,exports){
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
//
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
//
// Originally from narwhal.js (http://narwhaljs.org)
// Copyright (c) 2009 Thomas Robinson <280north.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// when used in node, this will actually load the util module we depend on
// versus loading the builtin util module as happens otherwise
// this is a bug in node module loading as far as I am concerned
var util = require('util/');

var pSlice = Array.prototype.slice;
var hasOwn = Object.prototype.hasOwnProperty;

// 1. The assert module provides functions that throw
// AssertionError's when particular conditions are not met. The
// assert module must conform to the following interface.

var assert = module.exports = ok;

// 2. The AssertionError is defined in assert.
// new assert.AssertionError({ message: message,
//                             actual: actual,
//                             expected: expected })

assert.AssertionError = function AssertionError(options) {
  this.name = 'AssertionError';
  this.actual = options.actual;
  this.expected = options.expected;
  this.operator = options.operator;
  if (options.message) {
    this.message = options.message;
    this.generatedMessage = false;
  } else {
    this.message = getMessage(this);
    this.generatedMessage = true;
  }
  var stackStartFunction = options.stackStartFunction || fail;

  if (Error.captureStackTrace) {
    Error.captureStackTrace(this, stackStartFunction);
  }
  else {
    // non v8 browsers so we can have a stacktrace
    var err = new Error();
    if (err.stack) {
      var out = err.stack;

      // try to strip useless frames
      var fn_name = stackStartFunction.name;
      var idx = out.indexOf('\n' + fn_name);
      if (idx >= 0) {
        // once we have located the function frame
        // we need to strip out everything before it (and its line)
        var next_line = out.indexOf('\n', idx + 1);
        out = out.substring(next_line + 1);
      }

      this.stack = out;
    }
  }
};

// assert.AssertionError instanceof Error
util.inherits(assert.AssertionError, Error);

function replacer(key, value) {
  if (util.isUndefined(value)) {
    return '' + value;
  }
  if (util.isNumber(value) && (isNaN(value) || !isFinite(value))) {
    return value.toString();
  }
  if (util.isFunction(value) || util.isRegExp(value)) {
    return value.toString();
  }
  return value;
}

function truncate(s, n) {
  if (util.isString(s)) {
    return s.length < n ? s : s.slice(0, n);
  } else {
    return s;
  }
}

function getMessage(self) {
  return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' +
         self.operator + ' ' +
         truncate(JSON.stringify(self.expected, replacer), 128);
}

// At present only the three keys mentioned above are used and
// understood by the spec. Implementations or sub modules can pass
// other keys to the AssertionError's constructor - they will be
// ignored.

// 3. All of the following functions must throw an AssertionError
// when a corresponding condition is not met, with a message that
// may be undefined if not provided.  All assertion methods provide
// both the actual and expected values to the assertion error for
// display purposes.

function fail(actual, expected, message, operator, stackStartFunction) {
  throw new assert.AssertionError({
    message: message,
    actual: actual,
    expected: expected,
    operator: operator,
    stackStartFunction: stackStartFunction
  });
}

// EXTENSION! allows for well behaved errors defined elsewhere.
assert.fail = fail;

// 4. Pure assertion tests whether a value is truthy, as determined
// by !!guard.
// assert.ok(guard, message_opt);
// This statement is equivalent to assert.equal(true, !!guard,
// message_opt);. To test strictly for the value true, use
// assert.strictEqual(true, guard, message_opt);.

function ok(value, message) {
  if (!value) fail(value, true, message, '==', assert.ok);
}
assert.ok = ok;

// 5. The equality assertion tests shallow, coercive equality with
// ==.
// assert.equal(actual, expected, message_opt);

assert.equal = function equal(actual, expected, message) {
  if (actual != expected) fail(actual, expected, message, '==', assert.equal);
};

// 6. The non-equality assertion tests for whether two objects are not equal
// with != assert.notEqual(actual, expected, message_opt);

assert.notEqual = function notEqual(actual, expected, message) {
  if (actual == expected) {
    fail(actual, expected, message, '!=', assert.notEqual);
  }
};

// 7. The equivalence assertion tests a deep equality relation.
// assert.deepEqual(actual, expected, message_opt);

assert.deepEqual = function deepEqual(actual, expected, message) {
  if (!_deepEqual(actual, expected)) {
    fail(actual, expected, message, 'deepEqual', assert.deepEqual);
  }
};

function _deepEqual(actual, expected) {
  // 7.1. All identical values are equivalent, as determined by ===.
  if (actual === expected) {
    return true;

  } else if (util.isBuffer(actual) && util.isBuffer(expected)) {
    if (actual.length != expected.length) return false;

    for (var i = 0; i < actual.length; i++) {
      if (actual[i] !== expected[i]) return false;
    }

    return true;

  // 7.2. If the expected value is a Date object, the actual value is
  // equivalent if it is also a Date object that refers to the same time.
  } else if (util.isDate(actual) && util.isDate(expected)) {
    return actual.getTime() === expected.getTime();

  // 7.3 If the expected value is a RegExp object, the actual value is
  // equivalent if it is also a RegExp object with the same source and
  // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
  } else if (util.isRegExp(actual) && util.isRegExp(expected)) {
    return actual.source === expected.source &&
           actual.global === expected.global &&
           actual.multiline === expected.multiline &&
           actual.lastIndex === expected.lastIndex &&
           actual.ignoreCase === expected.ignoreCase;

  // 7.4. Other pairs that do not both pass typeof value == 'object',
  // equivalence is determined by ==.
  } else if (!util.isObject(actual) && !util.isObject(expected)) {
    return actual == expected;

  // 7.5 For all other Object pairs, including Array objects, equivalence is
  // determined by having the same number of owned properties (as verified
  // with Object.prototype.hasOwnProperty.call), the same set of keys
  // (although not necessarily the same order), equivalent values for every
  // corresponding key, and an identical 'prototype' property. Note: this
  // accounts for both named and indexed properties on Arrays.
  } else {
    return objEquiv(actual, expected);
  }
}

function isArguments(object) {
  return Object.prototype.toString.call(object) == '[object Arguments]';
}

function objEquiv(a, b) {
  if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b))
    return false;
  // an identical 'prototype' property.
  if (a.prototype !== b.prototype) return false;
  //~~~I've managed to break Object.keys through screwy arguments passing.
  //   Converting to array solves the problem.
  if (isArguments(a)) {
    if (!isArguments(b)) {
      return false;
    }
    a = pSlice.call(a);
    b = pSlice.call(b);
    return _deepEqual(a, b);
  }
  try {
    var ka = objectKeys(a),
        kb = objectKeys(b),
        key, i;
  } catch (e) {//happens when one is a string literal and the other isn't
    return false;
  }
  // having the same number of owned properties (keys incorporates
  // hasOwnProperty)
  if (ka.length != kb.length)
    return false;
  //the same set of keys (although not necessarily the same order),
  ka.sort();
  kb.sort();
  //~~~cheap key test
  for (i = ka.length - 1; i >= 0; i--) {
    if (ka[i] != kb[i])
      return false;
  }
  //equivalent values for every corresponding key, and
  //~~~possibly expensive deep test
  for (i = ka.length - 1; i >= 0; i--) {
    key = ka[i];
    if (!_deepEqual(a[key], b[key])) return false;
  }
  return true;
}

// 8. The non-equivalence assertion tests for any deep inequality.
// assert.notDeepEqual(actual, expected, message_opt);

assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
  if (_deepEqual(actual, expected)) {
    fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
  }
};

// 9. The strict equality assertion tests strict equality, as determined by ===.
// assert.strictEqual(actual, expected, message_opt);

assert.strictEqual = function strictEqual(actual, expected, message) {
  if (actual !== expected) {
    fail(actual, expected, message, '===', assert.strictEqual);
  }
};

// 10. The strict non-equality assertion tests for strict inequality, as
// determined by !==.  assert.notStrictEqual(actual, expected, message_opt);

assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
  if (actual === expected) {
    fail(actual, expected, message, '!==', assert.notStrictEqual);
  }
};

function expectedException(actual, expected) {
  if (!actual || !expected) {
    return false;
  }

  if (Object.prototype.toString.call(expected) == '[object RegExp]') {
    return expected.test(actual);
  } else if (actual instanceof expected) {
    return true;
  } else if (expected.call({}, actual) === true) {
    return true;
  }

  return false;
}

function _throws(shouldThrow, block, expected, message) {
  var actual;

  if (util.isString(expected)) {
    message = expected;
    expected = null;
  }

  try {
    block();
  } catch (e) {
    actual = e;
  }

  message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
            (message ? ' ' + message : '.');

  if (shouldThrow && !actual) {
    fail(actual, expected, 'Missing expected exception' + message);
  }

  if (!shouldThrow && expectedException(actual, expected)) {
    fail(actual, expected, 'Got unwanted exception' + message);
  }

  if ((shouldThrow && actual && expected &&
      !expectedException(actual, expected)) || (!shouldThrow && actual)) {
    throw actual;
  }
}

// 11. Expected to throw an error:
// assert.throws(block, Error_opt, message_opt);

assert.throws = function(block, /*optional*/error, /*optional*/message) {
  _throws.apply(this, [true].concat(pSlice.call(arguments)));
};

// EXTENSION! This is annoying to write outside this module.
assert.doesNotThrow = function(block, /*optional*/message) {
  _throws.apply(this, [false].concat(pSlice.call(arguments)));
};

assert.ifError = function(err) { if (err) {throw err;}};

var objectKeys = Object.keys || function (obj) {
  var keys = [];
  for (var key in obj) {
    if (hasOwn.call(obj, key)) keys.push(key);
  }
  return keys;
};

},{"util/":5}],4:[function(require,module,exports){
module.exports = function isBuffer(arg) {
  return arg && typeof arg === 'object'
    && typeof arg.copy === 'function'
    && typeof arg.fill === 'function'
    && typeof arg.readUInt8 === 'function';
}
},{}],5:[function(require,module,exports){
(function (process,global){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var formatRegExp = /%[sdj%]/g;
exports.format = function(f) {
  if (!isString(f)) {
    var objects = [];
    for (var i = 0; i < arguments.length; i++) {
      objects.push(inspect(arguments[i]));
    }
    return objects.join(' ');
  }

  var i = 1;
  var args = arguments;
  var len = args.length;
  var str = String(f).replace(formatRegExp, function(x) {
    if (x === '%%') return '%';
    if (i >= len) return x;
    switch (x) {
      case '%s': return String(args[i++]);
      case '%d': return Number(args[i++]);
      case '%j':
        try {
          return JSON.stringify(args[i++]);
        } catch (_) {
          return '[Circular]';
        }
      default:
        return x;
    }
  });
  for (var x = args[i]; i < len; x = args[++i]) {
    if (isNull(x) || !isObject(x)) {
      str += ' ' + x;
    } else {
      str += ' ' + inspect(x);
    }
  }
  return str;
};


// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function(fn, msg) {
  // Allow for deprecating things in the process of starting up.
  if (isUndefined(global.process)) {
    return function() {
      return exports.deprecate(fn, msg).apply(this, arguments);
    };
  }

  if (process.noDeprecation === true) {
    return fn;
  }

  var warned = false;
  function deprecated() {
    if (!warned) {
      if (process.throwDeprecation) {
        throw new Error(msg);
      } else if (process.traceDeprecation) {
        console.trace(msg);
      } else {
        console.error(msg);
      }
      warned = true;
    }
    return fn.apply(this, arguments);
  }

  return deprecated;
};


var debugs = {};
var debugEnviron;
exports.debuglog = function(set) {
  if (isUndefined(debugEnviron))
    debugEnviron = process.env.NODE_DEBUG || '';
  set = set.toUpperCase();
  if (!debugs[set]) {
    if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
      var pid = process.pid;
      debugs[set] = function() {
        var msg = exports.format.apply(exports, arguments);
        console.error('%s %d: %s', set, pid, msg);
      };
    } else {
      debugs[set] = function() {};
    }
  }
  return debugs[set];
};


/**
 * Echos the value of a value. Trys to print the value out
 * in the best way possible given the different types.
 *
 * @param {Object} obj The object to print out.
 * @param {Object} opts Optional options object that alters the output.
 */
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
  // default options
  var ctx = {
    seen: [],
    stylize: stylizeNoColor
  };
  // legacy...
  if (arguments.length >= 3) ctx.depth = arguments[2];
  if (arguments.length >= 4) ctx.colors = arguments[3];
  if (isBoolean(opts)) {
    // legacy...
    ctx.showHidden = opts;
  } else if (opts) {
    // got an "options" object
    exports._extend(ctx, opts);
  }
  // set default options
  if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
  if (isUndefined(ctx.depth)) ctx.depth = 2;
  if (isUndefined(ctx.colors)) ctx.colors = false;
  if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
  if (ctx.colors) ctx.stylize = stylizeWithColor;
  return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
  'bold' : [1, 22],
  'italic' : [3, 23],
  'underline' : [4, 24],
  'inverse' : [7, 27],
  'white' : [37, 39],
  'grey' : [90, 39],
  'black' : [30, 39],
  'blue' : [34, 39],
  'cyan' : [36, 39],
  'green' : [32, 39],
  'magenta' : [35, 39],
  'red' : [31, 39],
  'yellow' : [33, 39]
};

// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
  'special': 'cyan',
  'number': 'yellow',
  'boolean': 'yellow',
  'undefined': 'grey',
  'null': 'bold',
  'string': 'green',
  'date': 'magenta',
  // "name": intentionally not styling
  'regexp': 'red'
};


function stylizeWithColor(str, styleType) {
  var style = inspect.styles[styleType];

  if (style) {
    return '\u001b[' + inspect.colors[style][0] + 'm' + str +
           '\u001b[' + inspect.colors[style][1] + 'm';
  } else {
    return str;
  }
}


function stylizeNoColor(str, styleType) {
  return str;
}


function arrayToHash(array) {
  var hash = {};

  array.forEach(function(val, idx) {
    hash[val] = true;
  });

  return hash;
}


function formatValue(ctx, value, recurseTimes) {
  // Provide a hook for user-specified inspect functions.
  // Check that value is an object with an inspect function on it
  if (ctx.customInspect &&
      value &&
      isFunction(value.inspect) &&
      // Filter out the util module, it's inspect function is special
      value.inspect !== exports.inspect &&
      // Also filter out any prototype objects using the circular check.
      !(value.constructor && value.constructor.prototype === value)) {
    var ret = value.inspect(recurseTimes, ctx);
    if (!isString(ret)) {
      ret = formatValue(ctx, ret, recurseTimes);
    }
    return ret;
  }

  // Primitive types cannot have properties
  var primitive = formatPrimitive(ctx, value);
  if (primitive) {
    return primitive;
  }

  // Look up the keys of the object.
  var keys = Object.keys(value);
  var visibleKeys = arrayToHash(keys);

  if (ctx.showHidden) {
    keys = Object.getOwnPropertyNames(value);
  }

  // IE doesn't make error fields non-enumerable
  // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
  if (isError(value)
      && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
    return formatError(value);
  }

  // Some type of object without properties can be shortcutted.
  if (keys.length === 0) {
    if (isFunction(value)) {
      var name = value.name ? ': ' + value.name : '';
      return ctx.stylize('[Function' + name + ']', 'special');
    }
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    }
    if (isDate(value)) {
      return ctx.stylize(Date.prototype.toString.call(value), 'date');
    }
    if (isError(value)) {
      return formatError(value);
    }
  }

  var base = '', array = false, braces = ['{', '}'];

  // Make Array say that they are Array
  if (isArray(value)) {
    array = true;
    braces = ['[', ']'];
  }

  // Make functions say that they are functions
  if (isFunction(value)) {
    var n = value.name ? ': ' + value.name : '';
    base = ' [Function' + n + ']';
  }

  // Make RegExps say that they are RegExps
  if (isRegExp(value)) {
    base = ' ' + RegExp.prototype.toString.call(value);
  }

  // Make dates with properties first say the date
  if (isDate(value)) {
    base = ' ' + Date.prototype.toUTCString.call(value);
  }

  // Make error with message first say the error
  if (isError(value)) {
    base = ' ' + formatError(value);
  }

  if (keys.length === 0 && (!array || value.length == 0)) {
    return braces[0] + base + braces[1];
  }

  if (recurseTimes < 0) {
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    } else {
      return ctx.stylize('[Object]', 'special');
    }
  }

  ctx.seen.push(value);

  var output;
  if (array) {
    output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
  } else {
    output = keys.map(function(key) {
      return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
    });
  }

  ctx.seen.pop();

  return reduceToSingleString(output, base, braces);
}


function formatPrimitive(ctx, value) {
  if (isUndefined(value))
    return ctx.stylize('undefined', 'undefined');
  if (isString(value)) {
    var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
                                             .replace(/'/g, "\\'")
                                             .replace(/\\"/g, '"') + '\'';
    return ctx.stylize(simple, 'string');
  }
  if (isNumber(value))
    return ctx.stylize('' + value, 'number');
  if (isBoolean(value))
    return ctx.stylize('' + value, 'boolean');
  // For some reason typeof null is "object", so special case here.
  if (isNull(value))
    return ctx.stylize('null', 'null');
}


function formatError(value) {
  return '[' + Error.prototype.toString.call(value) + ']';
}


function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  var output = [];
  for (var i = 0, l = value.length; i < l; ++i) {
    if (hasOwnProperty(value, String(i))) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          String(i), true));
    } else {
      output.push('');
    }
  }
  keys.forEach(function(key) {
    if (!key.match(/^\d+$/)) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          key, true));
    }
  });
  return output;
}


function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
  var name, str, desc;
  desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
  if (desc.get) {
    if (desc.set) {
      str = ctx.stylize('[Getter/Setter]', 'special');
    } else {
      str = ctx.stylize('[Getter]', 'special');
    }
  } else {
    if (desc.set) {
      str = ctx.stylize('[Setter]', 'special');
    }
  }
  if (!hasOwnProperty(visibleKeys, key)) {
    name = '[' + key + ']';
  }
  if (!str) {
    if (ctx.seen.indexOf(desc.value) < 0) {
      if (isNull(recurseTimes)) {
        str = formatValue(ctx, desc.value, null);
      } else {
        str = formatValue(ctx, desc.value, recurseTimes - 1);
      }
      if (str.indexOf('\n') > -1) {
        if (array) {
          str = str.split('\n').map(function(line) {
            return '  ' + line;
          }).join('\n').substr(2);
        } else {
          str = '\n' + str.split('\n').map(function(line) {
            return '   ' + line;
          }).join('\n');
        }
      }
    } else {
      str = ctx.stylize('[Circular]', 'special');
    }
  }
  if (isUndefined(name)) {
    if (array && key.match(/^\d+$/)) {
      return str;
    }
    name = JSON.stringify('' + key);
    if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
      name = name.substr(1, name.length - 2);
      name = ctx.stylize(name, 'name');
    } else {
      name = name.replace(/'/g, "\\'")
                 .replace(/\\"/g, '"')
                 .replace(/(^"|"$)/g, "'");
      name = ctx.stylize(name, 'string');
    }
  }

  return name + ': ' + str;
}


function reduceToSingleString(output, base, braces) {
  var numLinesEst = 0;
  var length = output.reduce(function(prev, cur) {
    numLinesEst++;
    if (cur.indexOf('\n') >= 0) numLinesEst++;
    return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
  }, 0);

  if (length > 60) {
    return braces[0] +
           (base === '' ? '' : base + '\n ') +
           ' ' +
           output.join(',\n  ') +
           ' ' +
           braces[1];
  }

  return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}


// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
  return Array.isArray(ar);
}
exports.isArray = isArray;

function isBoolean(arg) {
  return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;

function isNull(arg) {
  return arg === null;
}
exports.isNull = isNull;

function isNullOrUndefined(arg) {
  return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;

function isNumber(arg) {
  return typeof arg === 'number';
}
exports.isNumber = isNumber;

function isString(arg) {
  return typeof arg === 'string';
}
exports.isString = isString;

function isSymbol(arg) {
  return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;

function isUndefined(arg) {
  return arg === void 0;
}
exports.isUndefined = isUndefined;

function isRegExp(re) {
  return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;

function isDate(d) {
  return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;

function isError(e) {
  return isObject(e) &&
      (objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;

function isFunction(arg) {
  return typeof arg === 'function';
}
exports.isFunction = isFunction;

function isPrimitive(arg) {
  return arg === null ||
         typeof arg === 'boolean' ||
         typeof arg === 'number' ||
         typeof arg === 'string' ||
         typeof arg === 'symbol' ||  // ES6 symbol
         typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;

exports.isBuffer = require('./support/isBuffer');

function objectToString(o) {
  return Object.prototype.toString.call(o);
}


function pad(n) {
  return n < 10 ? '0' + n.toString(10) : n.toString(10);
}


var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
              'Oct', 'Nov', 'Dec'];

// 26 Feb 16:19:34
function timestamp() {
  var d = new Date();
  var time = [pad(d.getHours()),
              pad(d.getMinutes()),
              pad(d.getSeconds())].join(':');
  return [d.getDate(), months[d.getMonth()], time].join(' ');
}


// log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function() {
  console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};


/**
 * Inherit the prototype methods from one constructor into another.
 *
 * The Function.prototype.inherits from lang.js rewritten as a standalone
 * function (not on Function.prototype). NOTE: If this file is to be loaded
 * during bootstrapping this function needs to be rewritten using some native
 * functions as prototype setup using normal JavaScript does not work as
 * expected during bootstrapping (see mirror.js in r114903).
 *
 * @param {function} ctor Constructor function which needs to inherit the
 *     prototype.
 * @param {function} superCtor Constructor function to inherit prototype from.
 */
exports.inherits = require('inherits');

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || !isObject(add)) return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

}).call(this,require("FWaASH"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":4,"FWaASH":7,"inherits":6}],6:[function(require,module,exports){
if (typeof Object.create === 'function') {
  // implementation from standard node.js 'util' module
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: {
        value: ctor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  };
} else {
  // old school shim for old browsers
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    var TempCtor = function () {}
    TempCtor.prototype = superCtor.prototype
    ctor.prototype = new TempCtor()
    ctor.prototype.constructor = ctor
  }
}

},{}],7:[function(require,module,exports){
// shim for using process in browser

var process = module.exports = {};

process.nextTick = (function () {
    var canSetImmediate = typeof window !== 'undefined'
    && window.setImmediate;
    var canPost = typeof window !== 'undefined'
    && window.postMessage && window.addEventListener
    ;

    if (canSetImmediate) {
        return function (f) { return window.setImmediate(f) };
    }

    if (canPost) {
        var queue = [];
        window.addEventListener('message', function (ev) {
            var source = ev.source;
            if ((source === window || source === null) && ev.data === 'process-tick') {
                ev.stopPropagation();
                if (queue.length > 0) {
                    var fn = queue.shift();
                    fn();
                }
            }
        }, true);

        return function nextTick(fn) {
            queue.push(fn);
            window.postMessage('process-tick', '*');
        };
    }

    return function nextTick(fn) {
        setTimeout(fn, 0);
    };
})();

process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];

function noop() {}

process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;

process.binding = function (name) {
    throw new Error('process.binding is not supported');
}

// TODO(shtylman)
process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};

},{}],8:[function(require,module,exports){
'use strict';

var asap = require('asap')

module.exports = Promise
function Promise(fn) {
  if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new')
  if (typeof fn !== 'function') throw new TypeError('not a function')
  var state = null
  var value = null
  var deferreds = []
  var self = this

  this.then = function(onFulfilled, onRejected) {
    return new Promise(function(resolve, reject) {
      handle(new Handler(onFulfilled, onRejected, resolve, reject))
    })
  }

  function handle(deferred) {
    if (state === null) {
      deferreds.push(deferred)
      return
    }
    asap(function() {
      var cb = state ? deferred.onFulfilled : deferred.onRejected
      if (cb === null) {
        (state ? deferred.resolve : deferred.reject)(value)
        return
      }
      var ret
      try {
        ret = cb(value)
      }
      catch (e) {
        deferred.reject(e)
        return
      }
      deferred.resolve(ret)
    })
  }

  function resolve(newValue) {
    try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
      if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
      if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
        var then = newValue.then
        if (typeof then === 'function') {
          doResolve(then.bind(newValue), resolve, reject)
          return
        }
      }
      state = true
      value = newValue
      finale()
    } catch (e) { reject(e) }
  }

  function reject(newValue) {
    state = false
    value = newValue
    finale()
  }

  function finale() {
    for (var i = 0, len = deferreds.length; i < len; i++)
      handle(deferreds[i])
    deferreds = null
  }

  doResolve(fn, resolve, reject)
}


function Handler(onFulfilled, onRejected, resolve, reject){
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
  this.onRejected = typeof onRejected === 'function' ? onRejected : null
  this.resolve = resolve
  this.reject = reject
}

/**
 * Take a potentially misbehaving resolver function and make sure
 * onFulfilled and onRejected are only called once.
 *
 * Makes no guarantees about asynchrony.
 */
function doResolve(fn, onFulfilled, onRejected) {
  var done = false;
  try {
    fn(function (value) {
      if (done) return
      done = true
      onFulfilled(value)
    }, function (reason) {
      if (done) return
      done = true
      onRejected(reason)
    })
  } catch (ex) {
    if (done) return
    done = true
    onRejected(ex)
  }
}

},{"asap":10}],9:[function(require,module,exports){
'use strict';

//This file contains then/promise specific extensions to the core promise API

var Promise = require('./core.js')
var asap = require('asap')

module.exports = Promise

/* Static Functions */

function ValuePromise(value) {
  this.then = function (onFulfilled) {
    if (typeof onFulfilled !== 'function') return this
    return new Promise(function (resolve, reject) {
      asap(function () {
        try {
          resolve(onFulfilled(value))
        } catch (ex) {
          reject(ex);
        }
      })
    })
  }
}
ValuePromise.prototype = Object.create(Promise.prototype)

var TRUE = new ValuePromise(true)
var FALSE = new ValuePromise(false)
var NULL = new ValuePromise(null)
var UNDEFINED = new ValuePromise(undefined)
var ZERO = new ValuePromise(0)
var EMPTYSTRING = new ValuePromise('')

Promise.resolve = function (value) {
  if (value instanceof Promise) return value

  if (value === null) return NULL
  if (value === undefined) return UNDEFINED
  if (value === true) return TRUE
  if (value === false) return FALSE
  if (value === 0) return ZERO
  if (value === '') return EMPTYSTRING

  if (typeof value === 'object' || typeof value === 'function') {
    try {
      var then = value.then
      if (typeof then === 'function') {
        return new Promise(then.bind(value))
      }
    } catch (ex) {
      return new Promise(function (resolve, reject) {
        reject(ex)
      })
    }
  }

  return new ValuePromise(value)
}

Promise.from = Promise.cast = function (value) {
  var err = new Error('Promise.from and Promise.cast are deprecated, use Promise.resolve instead')
  err.name = 'Warning'
  console.warn(err.stack)
  return Promise.resolve(value)
}

Promise.denodeify = function (fn, argumentCount) {
  argumentCount = argumentCount || Infinity
  return function () {
    var self = this
    var args = Array.prototype.slice.call(arguments)
    return new Promise(function (resolve, reject) {
      while (args.length && args.length > argumentCount) {
        args.pop()
      }
      args.push(function (err, res) {
        if (err) reject(err)
        else resolve(res)
      })
      fn.apply(self, args)
    })
  }
}
Promise.nodeify = function (fn) {
  return function () {
    var args = Array.prototype.slice.call(arguments)
    var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null
    try {
      return fn.apply(this, arguments).nodeify(callback)
    } catch (ex) {
      if (callback === null || typeof callback == 'undefined') {
        return new Promise(function (resolve, reject) { reject(ex) })
      } else {
        asap(function () {
          callback(ex)
        })
      }
    }
  }
}

Promise.all = function () {
  var calledWithArray = arguments.length === 1 && Array.isArray(arguments[0])
  var args = Array.prototype.slice.call(calledWithArray ? arguments[0] : arguments)

  if (!calledWithArray) {
    var err = new Error('Promise.all should be called with a single array, calling it with multiple arguments is deprecated')
    err.name = 'Warning'
    console.warn(err.stack)
  }

  return new Promise(function (resolve, reject) {
    if (args.length === 0) return resolve([])
    var remaining = args.length
    function res(i, val) {
      try {
        if (val && (typeof val === 'object' || typeof val === 'function')) {
          var then = val.then
          if (typeof then === 'function') {
            then.call(val, function (val) { res(i, val) }, reject)
            return
          }
        }
        args[i] = val
        if (--remaining === 0) {
          resolve(args);
        }
      } catch (ex) {
        reject(ex)
      }
    }
    for (var i = 0; i < args.length; i++) {
      res(i, args[i])
    }
  })
}

Promise.reject = function (value) {
  return new Promise(function (resolve, reject) { 
    reject(value);
  });
}

Promise.race = function (values) {
  return new Promise(function (resolve, reject) { 
    values.forEach(function(value){
      Promise.resolve(value).then(resolve, reject);
    })
  });
}

/* Prototype Methods */

Promise.prototype.done = function (onFulfilled, onRejected) {
  var self = arguments.length ? this.then.apply(this, arguments) : this
  self.then(null, function (err) {
    asap(function () {
      throw err
    })
  })
}

Promise.prototype.nodeify = function (callback) {
  if (typeof callback != 'function') return this

  this.then(function (value) {
    asap(function () {
      callback(null, value)
    })
  }, function (err) {
    asap(function () {
      callback(err)
    })
  })
}

Promise.prototype['catch'] = function (onRejected) {
  return this.then(null, onRejected);
}

},{"./core.js":8,"asap":10}],10:[function(require,module,exports){
(function (process){

// Use the fastest possible means to execute a task in a future turn
// of the event loop.

// linked list of tasks (single, with head node)
var head = {task: void 0, next: null};
var tail = head;
var flushing = false;
var requestFlush = void 0;
var isNodeJS = false;

function flush() {
    /* jshint loopfunc: true */

    while (head.next) {
        head = head.next;
        var task = head.task;
        head.task = void 0;
        var domain = head.domain;

        if (domain) {
            head.domain = void 0;
            domain.enter();
        }

        try {
            task();

        } catch (e) {
            if (isNodeJS) {
                // In node, uncaught exceptions are considered fatal errors.
                // Re-throw them synchronously to interrupt flushing!

                // Ensure continuation if the uncaught exception is suppressed
                // listening "uncaughtException" events (as domains does).
                // Continue in next event to avoid tick recursion.
                if (domain) {
                    domain.exit();
                }
                setTimeout(flush, 0);
                if (domain) {
                    domain.enter();
                }

                throw e;

            } else {
                // In browsers, uncaught exceptions are not fatal.
                // Re-throw them asynchronously to avoid slow-downs.
                setTimeout(function() {
                   throw e;
                }, 0);
            }
        }

        if (domain) {
            domain.exit();
        }
    }

    flushing = false;
}

if (typeof process !== "undefined" && process.nextTick) {
    // Node.js before 0.9. Note that some fake-Node environments, like the
    // Mocha test runner, introduce a `process` global without a `nextTick`.
    isNodeJS = true;

    requestFlush = function () {
        process.nextTick(flush);
    };

} else if (typeof setImmediate === "function") {
    // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate
    if (typeof window !== "undefined") {
        requestFlush = setImmediate.bind(window, flush);
    } else {
        requestFlush = function () {
            setImmediate(flush);
        };
    }

} else if (typeof MessageChannel !== "undefined") {
    // modern browsers
    // http://www.nonblocking.io/2011/06/windownexttick.html
    var channel = new MessageChannel();
    channel.port1.onmessage = flush;
    requestFlush = function () {
        channel.port2.postMessage(0);
    };

} else {
    // old browsers
    requestFlush = function () {
        setTimeout(flush, 0);
    };
}

function asap(task) {
    tail = tail.next = {
        task: task,
        domain: isNodeJS && process.domain,
        next: null
    };

    if (!flushing) {
        flushing = true;
        requestFlush();
    }
};

module.exports = asap;


}).call(this,require("FWaASH"))
},{"FWaASH":7}],11:[function(require,module,exports){
var assert = require('assert');

var conda;
if (typeof window !== "undefined") {
    conda = window.conda;
    mocha.timeout(10000).slow(2000);
}
else {
    conda = require('./conda');
}

function test(conda) {
    function assertSuccess(result) {
        assert.ok((typeof result.success !== "undefined") && result.success);
    }

    function assertError(result) {
        assert.ok(typeof result.error !== "undefined");
    }

    function assertType(type) {
        return function(result) {
            assert.equal(typeof result, type);
        };
    }

    function assertInstance(klass) {
        return function(result) {
            assert.ok(result instanceof klass);
        };
    }

    function assertKey(key) {
        return function(result) {
            assert.ok(typeof result[key] !== "undefined");
        };
    }

    assertError = assertKey("error");

    function assertAll(asserts) {
        return function(result) {
            asserts.forEach(function(assert) {
                assert(result);
            });
        };
    }

    describe('info', function() {
        it('should return a dictionary', function(done) {
            conda.info().then(assertType('object')).then(done);
        });

        it('should contain various keys', function(done) {
            conda.info().then().then(assertAll([
                assertKey('channels'),
                assertKey('conda_version'),
                assertKey('default_prefix'),
                assertKey('envs'),
                assertKey('envs_dirs'),
                assertKey('is_foreign'),
                assertKey('pkgs_dirs'),
                assertKey('platform'),
                assertKey('python_version'),
                assertKey('rc_path'),
                assertKey('root_prefix'),
                assertKey('root_writable'),
            ])).done(done);
        });
    });

    describe('search', function() {
        it('should return a dictionary', function(done) {
            conda.search().then(assertType('object')).then(done);
        });
    });

    describe('run', function() {
        it('should return a dictionary', function(done) {
            conda.run('nonexistent').then(assertType('object')).then(done);
        });

        it('should error for a nonexistent app', function(done) {
            conda.run('nonexistent').then(assertError).then(done);
        });

        it('should error for a package that is not an app', function(done) {
            conda.run('python').then(assertError).then(done);
        });
    });

    describe('Config', function() {
        var config = new conda.Config();
        it("shouldn't accept both config and file", function(done) {
            assert.throws(function() {
                new conda.Config({ system: true, file: 'test' });
            });
            done();
        });

        describe("#get", function() {
            it("should only accept certain keys", function(done) {
                assert.throws(function() {
                    config.get('nonexistent_key').then(done);
                });
                done();
            });

            it("should return a dictionary", function(done) {
                config.get('channels').then(assertType('object')).done(done);
            });
        });

        describe("#getAll", function() {
            it("should return a dictionary", function(done) {
                config.get('channels').then(assertType('object')).done(done);
            });
        });

        describe("#set", function() {
            it("should only accept certain keys", function(done) {
                assert.throws(function() {
                    config.set('nonexistent_key', 'value').then(done);
                });
                done();
            });

            it("should succeed", function(done) {
                config.set('use_pip', true).then(assertSuccess).then(function() {
                    config.set('use_pip', false).then(assertSuccess).done(done);
                });
            });
        });
    });

    describe('Env', function() {
        var envs;
        before(function(done) {
            conda.Env.getEnvs().then(function(result) {
                envs = result;
                done();
            });
        });

        describe('.create', function() {
            it('should support progressbars', function(done) {
                var first = true;
                conda.Env.create({ name: 'testing', packages: ['_license'], progress: true })
                    .progress(function(progress) {
                        if (first) {
                            done();
                            first = false;
                        }
                    }).done(function() {
                        // happens when env was created beforehand
                        if (first) {
                            done();
                        }
                    });
            });
        });

        describe('.getEnvs', function() {
            it('should return a list of Envs', function() {
                assertInstance(conda.Env)(envs[0]);
            });
        });

        describe('#install', function() {
            it('should return a dictionary', function(done) {
                envs[0].install({ packages: ['python'] })
                    .then(assertSuccess).done(done);
            });
        });

        describe('#linked', function() {
            it('should return a list of Packages', function(done) {
                envs[0].linked().then(function(result) {
                    assert.ok(Array.isArray(result));
                    assertInstance(conda.Package)(result[0]);
                }).done(done);
            });

            it('should return a list of strings with simple=true', function(done) {
                envs[0].linked({ simple: true }).then(function(result) {
                    assert.ok(Array.isArray(result));
                    assertType('string')(result[0]);
                }).done(done);
            });
        });

        describe('#revisions', function() {
            it('should return a list of objects', function(done) {
                envs[0].revisions().then(function(result) {
                    assert.ok(Array.isArray(result));
                    assertType('object')(result[0]);
                }).done(done);
            });
        });

        // TODO this is extremely slow, times out
        // describe('#removeEnv', function() {
        //     it('should return a dictionary', function(done) {
        //         envs.forEach(function(env) {
        //             if (env.name === 'testing') {
        //                 env.removeEnv().then(assertType('object')).done(done);
        //             }
        //         });
        //     });
        // });
    });


    describe('Package', function() {
        describe('.parseVersion', function() {
            it('should parse any number of parts', function() {
                assert.deepEqual(conda.Package.parseVersion("1.2.3.4.5").parts, [1, 2, 3, 4, 5]);
            });

            it('should parse RC parts', function() {
                assert.deepEqual(conda.Package.parseVersion("1.2.3rc1").parts, [1, 2, 3]);
                assert.deepEqual(conda.Package.parseVersion("1.2.3rc1").suffixNumber, 1);
                assert.deepEqual(conda.Package.parseVersion("1.2.3").suffix, null);
            });
        });

        describe('.isGreater', function() {
            it('should compare build numbers', function() {
                assert.ok(conda.Package.isGreater({
                    version: "1.2.3",
                    build_number: 2
                }, {
                    version: "1.2.3",
                    build_number: 3
                }));
            });

            it('should compare versions', function() {
                assert.ok(conda.Package.isGreater({
                    version: "1.2.3",
                    build_number: 3
                }, {
                    version: "1.2.6",
                    build_number: 3
                }));
                assert.ok(conda.Package.isGreater({
                    version: "1.2.3",
                    build_number: 3
                }, {
                    version: "1.3.0",
                    build_number: 3
                }));
                assert.ok(conda.Package.isGreater({
                    version: "1.2.3",
                    build_number: 3
                }, {
                    version: "2.0.0",
                    build_number: 3
                }));
                assert.ok(conda.Package.isGreater({
                    version: "1.12.30",
                    build_number: 3
                }, {
                    version: "2.10.0",
                    build_number: 3
                }));
                assert.ok(conda.Package.isGreater({
                    version: "19.12.30",
                    build_number: 3
                }, {
                    version: "21.10.15",
                    build_number: 3
                }));
                assert.ok(conda.Package.isGreater({
                    version: "19.12.3",
                    build_number: 3
                }, {
                    version: "19.12.15",
                    build_number: 3
                }));
            });

            it('should compare versions of unequal length', function() {
                assert.ok(conda.Package.isGreater({
                    version: "1.2",
                    build_number: 3
                }, {
                    version: "1.2.3",
                    build_number: 3
                }));
            });

            it('should compare release candidate numbers', function() {
                assert.ok(conda.Package.isGreater({
                    version: "1.2.3rc1",
                    build_number: 3
                }, {
                    version: "1.2.3",
                    build_number: 3
                }));

                assert.ok(conda.Package.isGreater({
                    version: "1.2.3rc1",
                    build_number: 3
                }, {
                    version: "1.2.3rc2",
                    build_number: 3
                }));
            });

            it('should compare suffixes', function() {
                assert.ok(conda.Package.isGreater({
                    version: "1.2.3a",
                    build_number: 3
                }, {
                    version: "1.2.3",
                    build_number: 3
                }));

                assert.ok(conda.Package.isGreater({
                    version: "1.2.3a2",
                    build_number: 3
                }, {
                    version: "1.2.3a5",
                    build_number: 3
                }));

                assert.ok(conda.Package.isGreater({
                    version: "1.2.3a2",
                    build_number: 3
                }, {
                    version: "1.2.3",
                    build_number: 3
                }));
            });
        });
    });
}

describe('conda-js RPC mode', function() {
    test(conda);
});

if (typeof conda.newContext !== "undefined") {
    describe('conda-js REST mode', function() {
        var context = conda.newContext();
        context.API_METHOD = 'REST';
        context.API_ROOT = 'http://localhost:8001/api';
        test(context);
    });
}

},{"./conda":1,"assert":3}]},{},[11])