A web based command line interface (Beaker)
MIT License
The Web Terminal provides power users with a familiar toolset for examining the system. For more background information, read this blog post.
This is a work in progress.
The "CLI" refers to the command-line interface (the UI and environment).
"Commands" refer to the userspace programs which are executed by the CLI.
npm install -g {command}
but with better management).wget <url> | bash
).WebTerm is designed to be conservative in what it can express or compute. It should only invoke Javascript commands, which then execute against the browser's Web APIs.
We define elegance as simplicity, not power.
WebTerm's invocation syntax should be simple and obvious, and limit the number of edge-cases possible. Any "programming" should occur inside of a Javascript command. (The exact definition of "simple" will evolve as we understand the environment better. If conditionals or function definitions arrive in the shell language, then something has gone wrong.)
Here is a minimal hello world example:
// hello-world.js
function main () {
return 'Hello, world!'
}
Invocation:
> hello-world
Hello, world!
Arguments
All command scripts define a main() which accepts an options object and positional arguments, and returns a JS value or object. Here's an example which echos the arguments:
// echo.js
function main (opts, ...args) {
return JSON.stringify(opts) + args.join(' ')
}
Invocation:
> echo -abc --hello world this is my echo
{"a": true, "b": true, "c": true, "hello": "world"} this is my echo
Globals
A globals
object provides information about the environment.
// pwd.js
function main () {
return globals.cwd
}
Invocation:
> pwd
dat://beakerbrowser.com/docs/
Rendering
The command may specify a toHTML
function on the response object. This method will be invoked if/when the output is rendered to the cli.
// hello-big-world.js
function main () {
return {
toHTML: () => '<h1>HELLO WORLD!</h1>'
}
}
The output can not include custom CSS. The CLI will provide a set of HTML constructs which it themes, and may even provided limited interactivity for exploring the output.
Sub-invocations
Commands can be composed by sub-invocations. Sub-invocations are command-invocations which are wrapped in parenthesis: '(' invocation ')'
. They will be evaluated, and their value will be substituted in-place for their parent's invocation.
// change-case.js
function main (opts, str) {
str = str.toString()
if (opts.u) return str.toUpperCase()
if (opts.l) return str.toLowerCase()
return str
}
Invocation:
> change-case -u (hello-world)
HELLO, WORLD!
# this is equivalent to running change-case -u "Hello, world!"
This can be used multiple times in an invocation, and nested arbitrarily:
// concat.js
function main (opts, left, right) {
return left.toString() + ' ' + right.toString()
}
Invocation:
> concat (hello-world) (hello-world)
Hello, world! Hello, world!
> concat (change-case -u (hello-world)) (change-case -l (hello-world))
HELLO, WORLD! hello, world!
> change-case -u (change-case -l (change-case -u (hello-world)))
HELLO, WORLD!
Sub-invocations are evaluated sequentially, to avoid potential races from side-effects (eg multiple sub-invocations using interactivity features).
Async
Commands may be async.
// wait1s.js
async function main (opts) {
await new Promise(resolve => setTimeout(resolve, 1e3))
}
Interactivity
Commands may use the terminal
API to provide interactivity during their invocation.
// new-profile.js
async function main () {
var name = await terminal.prompt('What is your name?')
var bio = await terminal.prompt('What is your bio?')
terminal.output(`You are ${name}. Bio: ${bio}`)
var shouldPublish = await terminal.confirm('Publish?', true)
if (shouldPublish) {
await ...
terminal.output('Published')
}
}