started adding copay shell to copay

This commit is contained in:
Gordon Hall 2014-06-06 14:23:40 -04:00
parent 45ffa94bbe
commit e7c5addfab
19 changed files with 781 additions and 3 deletions

5
.gitignore vendored
View File

@ -51,5 +51,8 @@ version.js
android/package
android/*.apk
coverage/
shell/bin/linux
shell/bin/darwin
shell/bin/win32

5
app.js
View File

@ -13,3 +13,8 @@ app.start = function(port, callback) {
};
module.exports = app;
// if we are running in the copay shell context, initialize the shell bindings
if (process.versions && process.versions['atom-shell']) {
require('./shell')(app);
}

View File

@ -21,7 +21,9 @@
"scripts": {
"start": "node server.js",
"test": "node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter spec test"
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter spec test",
"shell": "node shell/scripts/launch.js",
"setup-shell": "node shell/scripts/download-atom-shell.js"
},
"homepage": "https://github.com/bitpay/copay",
"devDependencies": {
@ -50,7 +52,9 @@
"sinon": "~1.9.1",
"soop": "~0.1.5",
"travis-cov": "^0.2.5",
"uglifyify": "~1.2.3"
"uglifyify": "~1.2.3",
"github-releases": "~0.2.0",
"cli-color": "~0.3.2"
},
"dependencies": {
"mocha": "^1.18.2"

22
shell/README.md Normal file
View File

@ -0,0 +1,22 @@
Copay Shell
===========
Native application wrapper for [Copay](https://bitpay.github.io/copay)
using [Atom Shell](https://github.com/atom/atom-shell).
![copay-shell](https://cloud.githubusercontent.com/assets/1188875/3153630/ccaacbae-ea9d-11e3-85d6-ac0ec2820ae2.png)
## Setup
Clone the repository and install dependencies.
```
~# git clone https://github.com/gordonwritescode/copay-shell.git
~# cd copay-shell && npm install
```
To build a distributable package for your platform, run:
```
~# npm run-script build
```

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>12F45</string>
<key>CFBundleDisplayName</key>
<string>Copay</string>
<key>CFBundleExecutable</key>
<string>Copay</string>
<key>CFBundleIconFile</key>
<string>copay.icns</string>
<key>CFBundleIdentifier</key>
<string>com.bitpay.copay</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Copay</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>0.0.1</string>
<key>DTSDKBuild</key>
<string>13C64</string>
<key>DTSDKName</key>
<string>macosx</string>
<key>DTXcode</key>
<string>0511</string>
<key>DTXcodeBuild</key>
<string>5B1008</string>
<key>LSMinimumSystemVersion</key>
<string>10.8.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>AtomApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>

Binary file not shown.

View File

@ -0,0 +1,7 @@
[Desktop Entry]
Encoding=UTF-8
Name=Copay
Exec=/opt/Copay/copay
Icon=/opt/Copay/resources/app/node_modules/copay/android/icon.png
Type=Application
Categories=Bitcoin;

13
shell/assets/linux/install.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
cd /tmp
# download pre-built pacakge to tmp
wget https://github.com/bitpay/copay/releases/download/v0.1.0/Copay-linux-x64.tar.gz
# extract archive
tar -xvf /tmp/Copay-linux-x64.tar.gz
# move the package to opt
mv /tmp/Copay /opt/Copay
# symlink `copay` to user path
ln -s /opt/Copay/copay /usr/local/bin/copay
cd /usr/share/applications
# download desktop entry
wget https://raw.githubusercontent.com/bitpay/copay-shell/master/assets/linux/Copay.desktop

View File

@ -0,0 +1,148 @@
# This installs two files, app.exe and logo.ico, creates a start menu shortcut, builds an uninstaller, and
# adds uninstall information to the registry for Add/Remove Programs
# To get started, put this script into a folder with the two files (app.exe, logo.ico, and license.rtf -
# You'll have to create these yourself) and run makensis on it
# If you change the names "app.exe", "logo.ico", or "license.rtf" you should do a search and replace - they
# show up in a few places.
# All the other settings can be tweaked by editing the !defines at the top of this script
!define APPNAME "Copay"
!define COMPANYNAME "BitPay, Inc"
!define DESCRIPTION "Multisignature Wallet"
# These three must be integers
!define VERSIONMAJOR 0
!define VERSIONMINOR 0
!define VERSIONBUILD 7
# These will be displayed by the "Click here for support information" link in "Add/Remove Programs"
# It is possible to use "mailto:" links in here to open the email client
!define HELPURL "http://github.com/bitpay/copay/issues" # "Support Information" link
!define UPDATEURL "http://github.com/bitpay/copay" # "Product Updates" link
!define ABOUTURL "http://bitpay.github.io/copay" # "Publisher" link
# This is the size (in kB) of all the files copied into "Program Files"
!define INSTALLSIZE 7233
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
InstallDir "$PROGRAMFILES\${COMPANYNAME}\${APPNAME}"
# rtf or txt file - remember if it is txt, it must be in the DOS text format (\r\n)
# This will be in the installer/uninstaller's title bar
Name "${APPNAME} - Multisignature Wallet"
Icon "logo.ico"
outFile "copay-setup.exe"
!include LogicLib.nsh
# Just three pages - license agreement, install location, and installation
# page license
page directory
Page instfiles
!macro VerifyUserIsAdmin
UserInfo::GetAccountType
pop $0
${If} $0 != "admin" ;Require admin rights on NT4+
messageBox mb_iconstop "Administrator rights required!"
setErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
quit
${EndIf}
!macroend
function .onInit
setShellVarContext all
!insertmacro VerifyUserIsAdmin
functionEnd
section "install"
# Files for the install directory - to build the installer, these should be in the same directory as the install script (this file)
setOutPath $INSTDIR
# Files added here should be removed by the uninstaller (see section "uninstall")
file "Copay.exe"
file "logo.ico"
# Add any other files for the install directory (license files, app data, etc) here
file /r "locales"
file /r "resources"
file "chromiumcontent.dll"
file "content_shell.pak"
file "d3dcompiler_43.dll"
file "ffmpegsumo.dll"
file "icudt.dll"
file "libEGL.dll"
file "libGLESv2.dll"
file "LICENSE"
file "version"
file "xinput1_3.dll"
# Uninstaller - See function un.onInit and section "uninstall" for configuration
writeUninstaller "$INSTDIR\uninstall.exe"
# Start Menu
createDirectory "$SMPROGRAMS\${COMPANYNAME}"
createShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\Copay.exe" "" "$INSTDIR\logo.ico"
# Registry information for add/remove programs
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${COMPANYNAME} - ${APPNAME} - ${DESCRIPTION}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\logo.ico$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "$\"${COMPANYNAME}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "HelpLink" "$\"${HELPURL}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLUpdateInfo" "$\"${UPDATEURL}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\""
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR}
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR}
# There is no option for modifying or repairing the install
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1
# Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE}
sectionEnd
# Uninstaller
function un.onInit
SetShellVarContext all
#Verify the uninstaller - last chance to back out
MessageBox MB_OKCANCEL "Permanantly remove ${APPNAME}?" IDOK next
Abort
next:
!insertmacro VerifyUserIsAdmin
functionEnd
section "uninstall"
# Remove Start Menu launcher
delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk"
# Try to remove the Start Menu folder - this will only happen if it is empty
rmDir "$SMPROGRAMS\${COMPANYNAME}"
# Remove files
delete $INSTDIR\*.pak
delete $INSTDIR\logo.ico
rmDir /r $INSTDIR\locales
rmDir /r $INSTDIR\resources
delete $INSTDIR\chromiumcontent.dll
delete $INSTDIR\content_shell.pak
delete $INSTDIR\d3dcompiler_43.dll
delete $INSTDIR\ffmpegsumo.dll
delete $INSTDIR\icudt.dll
delete $INSTDIR\libEGL.dll
delete $INSTDIR\libGLESv2.dll
delete $INSTDIR\LICENSE
delete $INSTDIR\version
delete $INSTDIR\xinput1_3.dll
delete $INSTDIR\Copay.exe
# Always delete uninstaller as the last action
delete $INSTDIR\uninstall.exe
# Try to remove the install directory - this will only happen if it is empty
rmDir $INSTDIR
# Remove uninstaller information from the registry
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}"
sectionEnd

BIN
shell/assets/win32/logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

8
shell/bin/README.md Normal file
View File

@ -0,0 +1,8 @@
# Atom Shell Binaries
This directory is a placeholder for local copies of the Atom Shell package. The
package for your platform will be installed to this directory under a directory
named for the value of your `process.platform`.
Running `npm run shell` will launch Copay using the installed Atom Shell
package.

10
shell/config.json Normal file
View File

@ -0,0 +1,10 @@
{
"window": {
"height": 720,
"width": 880
},
"copay": {
"port": 3000
},
"debug": true
}

63
shell/index.js Normal file
View File

@ -0,0 +1,63 @@
/*
** copay-shell - initilization
*/
var config = require('./config');
var app = require('app');
var BrowserWindow = require('browser-window');
var Menu = require('menu');
var mainWindow = null;
module.exports = function(copay) {
// quit when all windows are closed
app.on('window-all-closed', function() {
if (process.platform !== 'darwin') app.quit();
});
// initilization when ready
app.on('ready', function() {
// start up the copay server
copay.start(config.copay.port, function(loc) {
// create the main window
mainWindow = new BrowserWindow({
width: config.window.width,
height: config.window.height
});
// hide the empty window
mainWindow.hide();
// setup the native application menu
Menu.setApplicationMenu(
require('./lib/app-menu')(app, mainWindow.webContents)
);
// setup the message handler
require('./lib/message-handler')(mainWindow);
// setup the native system tray integration
// require('./lib/system-tray')(app, mainWindow.webContents);
// load our local copay server
mainWindow.loadUrl(loc);
// kind of hacky - but let's avoid the white "flash" before rendering
setTimeout(mainWindow.show.bind(mainWindow), 1000);
// deref the browser window when we close it so it can be GC'ed
mainWindow.on('closed', function() {
mainWindow = null;
});
// mainWindow.toggleDevTools();
});
});
return app;
};

143
shell/lib/app-menu.js Normal file
View File

@ -0,0 +1,143 @@
/*
** copay-shell - native app menu
*/
module.exports = function(app, web) {
var Menu = require('menu');
var menu = []
// add the mac osx app menu entry
if (process.platform === 'darwin') {
menu.push({
label: 'Copay',
submenu: [
{
label: 'About Copay',
selector: 'orderFrontStandardAboutPanel:'
},
{
type: 'separator'
},
{
label: 'Hide Copay',
accelerator: 'Command+H',
selector: 'hide:'
},
{
label: 'Hide Others',
accelerator: 'Command+Shift+H',
selector: 'hideOtherApplications:'
},
{
label: 'Show All',
selector: 'unhideAllApplications:'
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Command+Q',
click: function() {
app.quit();
}
}
]
});
}
menu.push({
label: 'Addresses',
submenu: [
{
label: 'Create New',
click: function() {
web.send('address:create');
}
}
]
});
menu.push({
label: 'Transactions',
submenu: [
{
label: 'Send Money',
click: function() {
web.send('transactions:send');
}
},
{
type: 'separator'
},
{
label: 'Pending',
click: function() {
web.send('transactions:pending');
}
},
{
label: 'All',
click: function() {
web.send('transactions:all');
}
}
]
});
menu.push({
label: 'Backup',
submenu: [
{
label: 'Download File',
click: function() {
web.send('backup:download');
}
},
{
label: 'Backup to Email',
click: function() {
web.send('backup:email');
}
},
{
type: 'separator'
},
{
label: 'Import a Backup',
click: function() {
web.send('backup:import');
}
}
]
});
if (process.platform === 'darwin') {
menu.push({
label: 'Window',
submenu: [
{
label: 'Minimize',
accelerator: 'Command+M',
selector: 'performMiniaturize:'
},
{
label: 'Close',
accelerator: 'Command+W',
selector: 'performClose:'
},
{
type: 'separator'
},
{
label: 'Bring All to Front',
selector: 'arrangeInFront:'
}
]
});
}
return Menu.buildFromTemplate(menu);
};

View File

@ -0,0 +1,81 @@
/*
** copay-shell - message handler
*/
var ipc = require('ipc');
var dialog = require('dialog');
var config = require('../config');
var windows = (process.platform === 'win32');
var HOME = process.env[windows ? 'USERPROFILE' : 'HOME'];
var fs = require('fs');
var shell = require('shell');
module.exports = function(renderer) {
// handle alerts sent from renderer (browser window)
ipc.on('alert', function(e, type, message) {
dialog.showMessageBox(renderer, {
type: type || 'info',
buttons: ['Okay'],
title: 'Copay',
message: message
});
});
// handle saving a wallet backup
ipc.on('backup:download', function(e, data) {
var backup = new Buffer(data.wallet);
var filename = data.name + '-' + (+(new Date)) + '.json.aes';
// open save dialog
dialog.showSaveDialog(renderer, {
title: 'Backup Wallet',
defaultPath: HOME + '/' + filename
}, function(path) {
if (!path) return;
fs.writeFile(path, backup, function(err) {
dialog.showMessageBox(renderer, {
type: err ? 'warning' : 'info',
buttons: ['Okay'],
title: 'Copay',
message: err ? err.message : 'Wallet backup saved!'
});
});
});
});
// handle emailing a wallet backup
ipc.on('backup:email', function(e, href) {
// open email client
shell.openExternal(href)
});
// handle importing a wallet backup
ipc.on('backup:import', function(e, data) {
// open file dialog
dialog.showOpenDialog(renderer, {
title: 'Import Wallet Backup',
defaultPath: HOME,
properties: ['openFile']
}, function(path) {
if (!path) return;
fs.readFile(path[0], function(err, contents) {
if (err) {
return dialog.showMessageBox(renderer, {
type: 'warning',
buttons: ['Okay'],
title: 'Copay',
message: err.message
});
}
renderer.send('backup:import:data', contents.toString());
});
});
});
// if we get an error, let's pop open the console for the user
ipc.on('error', function() {
if (config.debug) renderer.toggleDevTools();
});
};

16
shell/lib/system-tray.js Normal file
View File

@ -0,0 +1,16 @@
/*
** copay-shell - system tray integration
*/
var Menu = require('menu');
var Tray = require('tray');
var tray = new Tray('../assets/copay.icns');
module.exports = function(app, web) {
var menu = Menu.buildFromTemplate([]);
tray.setToolTip('Copay');
tray.setContextMenu(menu);
};

View File

@ -0,0 +1,70 @@
/*
** copay-shell - linux builder
*/
var os = require('os');
var downloadAtom = require('./download-atom-shell');
var async = require('async');
var fs = require('fs');
var path = require('path');
var color = require('cli-color');
var exec = require('child_process').exec;
var shell_target = path.normalize(__dirname + '/../build/linux/Copay')
var app_target = path.normalize(__dirname + '/../build/linux/Copay/resources/app')
async.series(
[
function getBinary(done) {
downloadAtom(done);
},
function copyBuildFiles(done) {
console.log(color.blue('{copay}'), 'copying app files');
var ignore = ['.git','assets','build','scripts'].map(function(dir) {
return '--exclude ' + dir
}).join(' ');
var appDir = path.normalize(__dirname + '/../');
exec('rsync -av --progress ' + appDir + ' ' + app_target + ' ' + ignore, {
maxBuffer: Infinity // LOL
}, function(err, stdout, stderr) {
done(err || stderr);
});
},
function removeDefaultApp(done) {
console.log(color.blue('{copay}'), 'removing default application');
rmdir(path.normalize(__dirname + '/../build/linux/Copay/resources/default_app'));
done();
},
function renameExecutable(done) {
console.log(color.blue('{copay}'), 'renaming executable');
fs.rename(shell_target + '/atom', shell_target + '/Copay', done);
},
function zipBuild(done) {
console.log(color.blue('{copay}'), 'zipping distributable package');
exec('zip -r ' + path.normalize(shell_target, '/../Copay-' + process.platform) + ' ' + shell_target, {
maxBuffer: Infinity // LOL x 2
},function(err, stdout, stderr) {
done(err || stderr);
});
}
],
function(err, results) {
if (err) return console.log(color.blue('{copay}'), color.red(err));
console.log(color.blue('{copay}'), color.green('success!'));
}
);
function rmdir(dir) {
if (fs.existsSync(dir)) {
var list = fs.readdirSync(dir);
for(var i = 0; i < list.length; i++) {
var filename = path.join(dir, list[i]);
var stat = fs.statSync(filename);
if (filename == '.' || filename == '..') null;
else if (stat.isDirectory()) rmdir(filename);
else fs.unlinkSync(filename);
}
fs.rmdirSync(dir);
}
};

View File

@ -0,0 +1,106 @@
/*
** copay-shell - atom shell downloader
*/
var path = require('path');
var fs = require('fs');
var GitHub = require('github-releases');
var async = require('async');
var readl = require('readline');
var color = require('cli-color');
var github = new GitHub({ repo: 'atom/atom-shell' });
var exec = require('child_process').exec;
var os = require('os');
var version = 'v0.13.0';
var target = path.normalize(__dirname + '/../bin/' + process.platform);
console.log(color.blue('{copay}'), 'ensuring existence of output directory');
ensureOutputTargets();
console.log(color.blue('{copay}'), 'getting atom-shell release ' + version);
github.getReleases({ tag_name: version }, function(err, releases) {
if (err || !releases.length) {
return console.log('Release not found');
}
switch (process.platform) {
case 'linux':
var filename = 'atom-shell-' + version + '-' + process.platform + '-x64.zip';
break;
case 'darwin':
var filename = 'atom-shell-' + version + '-' + process.platform + '-x64.zip';
break;
case 'win32':
var filename = 'atom-shell-' + version + '-' + process.platform + '-ia32.zip';
break;
default:
console.log('platform ' + process.platform + ' not supported');
process.exit();
}
console.log(color.blue('{copay}'), 'looking for prebuilt binary ' + filename);
for (var a = 0; a < releases[0].assets.length; a++) {
var asset = releases[0].assets[a];
if (asset.name === filename) {
console.log(color.blue('{copay}'), 'downloading ' + asset.name);
var rl = readl.createInterface({
input: process.stdin,
output: process.stdout
});
rl.write(' bytes received: 0');
return github.downloadAsset(asset, function(err, inStream) {
if (err) {
console.log(err);
process.exit();
}
var bytes = 0;
inStream.on('data', function(chunk) {
rl.write(null, { ctrl: true, name: 'u' });
rl.write(' bytes received: ' + (bytes + chunk.length));
bytes += chunk.length;
});
inStream.on('end', function() {
rl.close();
console.log('');
console.log(color.blue('{copay}'), 'downloaded!');
});
var out = target;
var tmp = os.tmpDir() + '/atom-shell.zip';
var outStream = fs.createWriteStream(tmp);
outStream.on('finish', function() {
console.log(color.blue('{copay}'), 'unzipping archive');
exec('unzip -o ' + tmp + ' -d ' + out, function(err, stdout, stderr) {
console.log(err || stderr || (color.blue('{copay}') + ' done!'))
});
});
inStream.pipe(outStream);
});
}
}
});
function ensureOutputTargets() {
if (!fs.existsSync(target)) fs.mkdirSync(target);
// if (!fs.existsSync(target + process.platform)) {
// fs.mkdirSync(target + process.platform);
// }
};

39
shell/scripts/launch.js Normal file
View File

@ -0,0 +1,39 @@
/*
** copay-shell - launch
*/
var color = require('cli-color');
var path = require('path');
var appPath = path.normalize(__dirname + '/../../');
var execPath = path.normalize(__dirname + '/../bin/' + process.platform);
var spawn = require('child_process').spawn;
// update execPath with platform specific binary locations
switch (process.platform) {
case 'linux':
execPath += '/atom-shell/atom';
break;
case 'darwin':
execPath += '/Atom.app/Contents/MacOS/Atom';
break;
case 'win32':
execPath += '\\atom-shell\\atom.exe'
break;
default:
console.log('Platform not supported.');
process.exit();
}
var copay = spawn(execPath, [appPath]);
copay.stdout.on('data', function (data) {
console.log(data);
});
copay.stderr.on('data', function (data) {
console.log(color.red(data));
});
copay.on('close', function (code) {
console.log('child process exited with code ' + code);
});