UNIX Command Line as a Chatbot
Unleash the power and flexibility of the UNIX command line with a chat bot! Orbital Frame supports commands, options, variables, pipes, asynchronous chaining, inline logical operators (via commands), functions, jobs, signals, plugins, and a custom language based on the venerable bash. For full details, visit the core README.
This project is organized as a monorepo consisting of the following:
- @orbital-frame/core The core framework.
- @orbital-frame/jehuty A reference implementation using core.
- @orbital-frame/parser PEG grammar and parser which outputs an AST.
- @orbital-frame/core-commands A collection of optional starter commands to load into your bot.
@orbital-frame/jehuty
uses these. - @orbital-frame/plugin-* Optional plugins that can be installed and loaded into your bot.
- @orbital-frame/command-* Optional commands that can be installed and loaded into your bot.
- @orbital-frame/adapter-* Adapters orbital frame can run on.
@orbital-frame/core
does not include an adapter by default so you must include one of these to use orbital-frame in your chat service.
The examples below are using @orbital-frame/jehuty
, a reference implementation
which is ready right out of the box.
@jehuty echo "hello, world!"
@jehuty NUM=10; calc $NUM + 2
@jehuty NUM=$(calc 9 + $(calc 1 + 1)); echo $NUM | calc + 2
@jehuty echo hi $(echo hello $(echo there))
@jehuty echo Counting $(calc 1 + 0) $(calc 1 + 1) $(calc 1 + 2) yay
@jehuty if $(equal 1 1) "they're equal" "they're not equal"
@jehuty alias test-alias 'if $(not $(equal 1 1)) "they're not equal" "they are equal"'
@jehuty test-alias
The bot command line is mostly a subset of Bash with a few exceptions. The main features will be covered here. Although not included for the sake of brevity and because it will vary based on your bot's configuration, every example below must start by hailing your bot so it understands the message is intended for it. For parser details and the full grammar, see @orbital-frame/parser.
Commands can be called with arguments and options. The options and option types a command accepts are provided by the command's author in the command definition. A detailed explanation for command authors is provided here.
some_command --some_option option_value arg1 arg2
Command options can either be valued or boolean, which is defined in the command itself.Command options can be either short form or long form:
some_command --long_option option_value
# or
some_command -s option_value
Unlike long options, short options can be chained:
some_command -abcd arg # here, a, b, c, and d are all options. a, b, and c are boolean options while d is being passed the argument "arg"
Commands can be immediately evaluated for use as arguments, option values, etc.
by surrounding the command or pipeline with $()
:
echo "three plus two is " $(calc 3 + 2)
Some commands start an interactive session where the command can receive
nonblocking input throughout its lifespan. The interaction character is
configurable based on your bot but defaults to >
. For full details, see
documentation in the @orbital-frame/core README:
interactive_command # some command that starts an interaction
some_other_command # the interaction will not intercept this command
> interactive_input # because this command is prefixed with the interaction character, it will be sent to the interactive command
Pipes are pipelines of commands (or functions) who pass their output as input into the next pipe.
whoami | uppercase
Variables are key/value pairs:
MY_KEY="some value" # set a variable
echo $MY_KEY # retrieve a value
The following variables are built-in:
0
The name of the currently executing command1 .. n
Positional arguments (used for getting arguments within a function)#
The number of command line arguments@
All command line arguments as an array!
The PID of the current command?
The exit status of the most recently executed command
See documentation in the @orbital-frame/core README
Like Bash, the Orbital Frame command line supports two forms of functions which are equivalent in the AST so it's a matter of your personal style:
function my_function {
echo "This is form 1"
}
my_function () {
echo "this is form 2"
}
Like Bash, function arguments are given through positional environment variables $1 through $n, so even in the second form where parentheses are used, no parameters can be listed.
function say_hello {
echo Hello, $1
}
say_hello konapun # displays "Hello, konapun"
To keep the syntax simple, the Orbital Frame grammar does not include any
control structures such as if statements or loops but these can be easily
replicated using commands. Here is an additional example using commands loaded
into jehuty which demonstrates how to do branching using the if
and and
commands rather than dedicated control structures:
@jehuty function analyze_length {
local WORD=$1
local LOWER=$2
local UPPER=$3
local WORD_LENGTH=$(split -d '' $WORD | length)
if $(and $(greater-than $WORD_LENGTH $LOWER) $(less-than $WORD_LENGTH $UPPER)) "String is valid" "String is invalid"
}
@jehuty analyze_length "fits" 1 5 # displays "String is valid"
By default, all variables are declared in the global scope. If you want to
instead use lexical scoping, use the local
keyword inside your function block:
MY_VAR=outer
function set_var {
local MY_VAR=inner
echo $MY_VAR
}
set_var # echoes "inner"
echo $MY_VAR # echoes "outer"
This project uses yarn. See yarn's installation guide for installation instructions. You can bootstrap the monorepo by running
yarn dev