The Cacher Run Server is a standalone Socket.io-based server that allows clients to run shell commands via Websocket messages. It is used primarily to run code snippets on the user's local machine via Cacher.
Demo of a Cacher snippet file executing on a Run Server:
The Cacher Desktop Client comes with the Run Server built-in. Open the Run Server dialog and click Start Server:
Start the Run Server in standalone mode if you require more control over its configuration.
Start server locally
npm i -g @cacherapp/cli
cacher run-server:start
Starting the server on a remote machine
Running a remote server ensures all your developers are able to run snippet file commands against the same environment.
Note: Since the commands will be run using the shell account from which you launch the CLI, we recommend you use only machines which are for testing or are ephemeral (i.e. Docker instances).
Example of launching with a secure tunnel (ngrok).
npm i -g @cacherapp/cli
cacher run-server:start -p 39135 -t 4D5dzRGliafhGg~btNlR9 -o file:// -v
ngrok http 39135
Install run-server
package for your Node project.
npm i --save @cacherapp/run-server
Examples of starting the Run Server:
// examples.js
const RunServer = require('@cacherapp/run-server').RunServer;
// Let server pick random port and token
(new RunServer(
{ origin: 'https://app.cacher.io' }
)).start();
// Specify port and token, log verbose output to file
(new RunServer(
{
origin: 'file://',
port: 43193,
token: 'secret_token',
verbose: true,
logToFile: true
}
)).start();
See server.model.ts for details on options.
Connecting to the standalone server
You'll need either the Cacher Web App or the Desktop Client in order to connect to a standalone Run Server.
Once you've logged in, open the Run Server dialog. Input your server's port and token and press Connect.
The Run Server uses two Javascript-based config files to define the set of rules which govern how different file extensions are mapped to shell commands:
-
config.default.js - The default set of file types -> shell command mappings. While this config is copied to the Cacher directory as
~/.cacher/run-server.config.js
, you should never edit this file. If you would like to add a file handler to this config (and let everyone use it), please submit a pull request instead. See the Contributing section below. -
~/.cacher/run-server.user-config.js
- Edit this file to add or overwrite file extension handlers on your local machine. Here are some examples of functions you might add to handle new file types:
Add command for .awesome
file extension.
// ~/.cacher/run-server.user-config.js
return {
rules: [
{
pattern: '\.awesome$',
run: (command, filepath) => `awesome-compiler "${filepath}"`
},
...
]
}
Compile .md
(Markdown) file into HTML, then display in default browser.
// ~/.cacher/run-server.user-config.js
// Requires `npm -g markdown` to be run first.
return {
rules: [
{
pattern: '\.md$',
run: (command, filepath, args) => {
const outputHtmlFile = `${args.runDir}/${args.baseFilename}.html`;
childProcess.execSync(`md2html ${filepath} > ${outputHtmlFile}`);
opn(outputHtmlFile);
// Must return a shell command
return `echo "Generated '${outputHtmlFile}'" and opened in default browser`;
}
},
...
]
}
Match Javascript files by .js
extension and file content. Calls nvm use [version]
before execution.
// ~/.cacher/run-server.user-config.js
return {
rules: [
{
pattern: (command) => {
return /\.js$/.test(command.file.filename)
&& command.file.content.indexOf('//nvm:') >= 0;
},
run: (command, filepath) => {
let nvmVersion = command.file.content.match(/(\/\/nvm: v(.+))/)[0]
.replace('//nvm: ', '');
return `export NVM_DIR=~/.nvm ` +
`&& source ~/.nvm/nvm.sh` +
`&& nvm use ${nvmVersion} ` +
`&& node "${filepath}"`;
}
},
...
]
}
pattern: (String|Function)
Indicates whether to execute the run:
callback for the given command.
Pass it either:
- Regex string that matches against the filename.
- Function that takes a
command
object and returns a boolean to indicate whether the rule should match.
Examples:
// Match files which end in .rb
pattern: '\.rb$'
// Match command which has "foobar" in the file's content
pattern: (command) => command.file.content.indexOf('foobar') >= 0
run: (Function)
Takes as input command
, filepath
and args
and returns a string to be executed on the user's shell. Rules matched
against interpreted (non-compiled) languages generally return [interpreter] "${filepath}"
. Rules matched with compiled
languages will compile the source file first before executing the binary.
Examples:
// Run .dart file with interpreter
run: (command, filepath) => `dart "${filepath}"`
// Compile C++ file and run binary
run: (command, filepath, args) => {
const outputFile = `${args.runDir}/${args.baseFilename}.out`;
return `g++ "${filepath}" -o "${outputFile}" && cd "${args.runDir}" && ./"${args.baseFilename}.out"`;
}
timestamp: (Boolean)
Set to true
to append an epoch timestamp to the end of the filename. This helps to prevent files from being overwritten
in the ~/.cacher/run
folder. Set to false
for languages which require a strict match between the filename and the
compiled binary. (i.e. Java, Haxe)
command (Object)
The object that is sent to the pattern:
and run:
callback functions.
Properties:
channel
- WebSocket channel for the commandfile.filename
- Filename with extension (i.e.example_code.cs
)file.filetype
- Type of the file (i.e.markdown
,lisp
)file.content
- The file's content which will be executed
filepath (String)
Passed to the run:
callback. The full path for the file to be run (in the ~/.cacher/run
folder).
args (Object)
Additional arguments sent to the run:
callback function.
Properties:
runDir
- The fully-resolved path to~/.cacher/run
.baseFilename
- The filename without file extension. This is sometimes necessary to execute the compiled binary. (i.e.java "${args.baseFilename}"
)
- Stop the built-in server (if running).
-
Make your changes to
~/.cacher/run-server.config.js
. -
Start the built-in server again.
If you run into issues with the server not starting or if your file handler does not perform as expected, debug the
problem via the log file: ~/.cacher/logs/run-server.log
.
- Install the Cacher CLI globally:
npm i -g @cacherapp/cli
-
Make your changes to
~/.cacher/run-server.config.js
. -
Start the server:
cacher run-server:start --verbose
Make note of the server port and token, then connect with the Cacher client.
Grab the source code for this repo:
git clone git@github.com:CacherApp/cacher-run-server.git
Install Typescript globally:
npm i -g typescript
Install packages and start the development server:
npm i
npm start https://app.cacher.io
We are happy to review any code changes, especially for new file rules added to config.default.js. Please adhere to contribution guidelines while drafting your pull request.
- socket.io - Websocket server implementation
- nanoid - Unique string ID generator
- winston - Logger for everything
- shelljs - Portable Unix shell commands
- opn - Better node-open
- chalk - Terminal string styling
- get-port - Get available TCP port
Released under the MIT License by Rui Jiang of Cacher.