zshrs 0.10.9

The first compiled Unix shell — bytecode VM, worker pool, AOP intercept, SQLite caching
Documentation
# daemon-shell.elv — elvish client for zshrs-daemon's HTTP service.
#
# Source from your ~/.config/elvish/rc.elv:
#
#     use ./daemon-shell.elv
#     daemon-shell:ping
#     daemon-shell:record-alias gst 'git status'
#     daemon-shell:defs-query &kind=alias &shell-id=elvish
#
# Or load into the global namespace:
#
#     eval (slurp < /path/to/daemon-shell.elv)
#
# Endpoint and auth from env (in your rc.elv):
#
#     set-env DAEMON_URL       http://127.0.0.1:7733
#     set-env DAEMON_TOKEN     <bearer-token>             # optional
#     set-env DAEMON_SHELL_ID  elvish                     # see docs/SHELL_IDS.md

use str

if (not (has-env DAEMON_URL))      { set-env DAEMON_URL      'http://127.0.0.1:7733' }
if (not (has-env DAEMON_TOKEN))    { set-env DAEMON_TOKEN    '' }
if (not (has-env DAEMON_SHELL_ID)) { set-env DAEMON_SHELL_ID 'elvish' }

# ---- Internal helpers -----------------------------------------------------

# Run curl with the right auth header. Elvish doesn't ship an HTTP
# client, so we shell out — same as zsh/bash/fish wrappers.
fn _curl {|@args|
    if (not-eq $E:DAEMON_TOKEN '') {
        curl -sS -f -H 'Authorization: Bearer '$E:DAEMON_TOKEN $@args
    } else {
        curl -sS -f $@args
    }
}

# POST /op/<name> with raw JSON body.
fn _post {|op body|
    _curl -H 'Content-Type: application/json' --data-raw $body $E:DAEMON_URL'/op/'$op
}

fn _get {|endpoint|
    _curl $E:DAEMON_URL$endpoint
}

# JSON-string escape: backslash, quote, tab, CR, LF — anything else
# passes through. Uses elvish's str module so we don't shell out for
# a per-call hot path. `str:replace` already replaces all occurrences
# (the `-all` suffix is implied; there's no separate `str:replace-all`).
fn _json-str {|s|
    var out = $s
    set out = (str:replace '\' '\\' $out)
    set out = (str:replace '"' '\"' $out)
    set out = (str:replace "\t" '\t' $out)
    set out = (str:replace "\r" '\r' $out)
    set out = (str:replace "\n" '\n' $out)
    put $out
}

# Build a JSON object from a [&key=val ...] map. Skip empty-string
# values so optional fields drop out cleanly.
fn _json-obj {|m|
    var parts = [(keys $m | each {|k|
        var v = $m[$k]
        if (not-eq $v '') {
            put '"'$k'":"'(_json-str $v)'"'
        }
    })]
    put '{'(str:join ',' $parts)'}'
}

# ---- Public commands ------------------------------------------------------

fn health { _get /health }
fn ops    { _get /ops }
fn info   { _post info '{}' }

fn ping {|@echo|
    if (== (count $echo) 0) {
        _post ping '{}'
    } else {
        _post ping '{"echo":"'(_json-str (str:join ' ' $echo))'"}'
    }
}

fn call {|op &body='{}'|
    _post $op $body
}

# ---- Federated recorder (definitions.*) ----------------------------------

fn _emit {|kind name &value='' &file='' &line='' &fn-chain=''|
    var body = '{"shell_id":"'$E:DAEMON_SHELL_ID'","kind":"'$kind'","name":"'(_json-str $name)'"'
    if (not-eq $value '') { set body = $body',"value":"'(_json-str $value)'"' }
    if (not-eq $file '')  { set body = $body',"file":"'(_json-str $file)'"' }
    if (not-eq $line '')  { set body = $body',"line":'$line }
    if (not-eq $fn-chain '') { set body = $body',"fn_chain":"'(_json-str $fn-chain)'"' }
    set body = $body'}'
    _post definitions_emit $body
}

fn record-alias       {|name body|       _emit alias    $name &value=$body }
fn record-galias      {|name body|       _emit galias   $name &value=$body }
fn record-salias      {|name body|       _emit salias   $name &value=$body }
fn record-function    {|name body|       _emit function $name &value=$body }
fn record-export      {|name value|      _emit env      $name &value=$value }
fn record-param       {|name value|      _emit params   $name &value=$value }
fn record-bindkey     {|seq widget|      _emit bindkey  $seq  &value=$widget }
fn record-compdef     {|cmd completer|   _emit compdef  $cmd  &value=$completer }
fn record-zstyle      {|pattern style|   _emit zstyle   $pattern &value=$style }
fn record-zmodload    {|module|          _emit zmodload $module }
fn record-setopt      {|opt|             _emit setopt   $opt   &value=on }
fn record-unsetopt    {|opt|             _emit setopt   $opt   &value=off }
fn record-source      {|path|            _emit source   $path }
fn record-path        {|dir|             _emit path     $dir }
fn record-fpath       {|dir|             _emit fpath    $dir }
fn record-zle         {|widget &body=''| _emit zle      $widget &value=$body }
fn record-trap        {|signal handler|  _emit trap     $signal &value=$handler }
fn record-named-dir   {|name path|       _emit named_dir $name &value=$path }
fn record-completion  {|cmd &path=''|    _emit completion $cmd &value=$path }

# ---- Federated catalog query / diff --------------------------------------

# `daemon-shell:defs-query &kind=K &name=N &prefix=P &shell-id=S &limit=N`
fn defs-query {|&kind='' &name='' &prefix='' &shell-id='' &limit=''|
    var parts = []
    if (not-eq $kind '')     { set parts = [$@parts '"kind":"'$kind'"'] }
    if (not-eq $name '')     { set parts = [$@parts '"name":"'$name'"'] }
    if (not-eq $prefix '')   { set parts = [$@parts '"prefix":"'$prefix'"'] }
    if (not-eq $shell-id '') { set parts = [$@parts '"shell_id":"'$shell-id'"'] }
    if (not-eq $limit '')    { set parts = [$@parts '"limit":'$limit] }
    _post definitions_query '{'(str:join ',' $parts)'}'
}

fn defs-kinds { _post definitions_kinds '{}' }

# `daemon-shell:defs-diff SHELL_A SHELL_B [&kind=K]`
fn defs-diff {|shell-a shell-b &kind=''|
    var body = '{"shell_a":"'$shell-a'","shell_b":"'$shell-b'"'
    if (not-eq $kind '') { set body = $body',"kind":"'$kind'"' }
    set body = $body'}'
    _post definitions_diff $body
}

# ---- Pubsub --------------------------------------------------------------

fn watch {|dir &recursive=$false|
    var rec = (if $recursive { put true } else { put false })
    _curl -N $E:DAEMON_URL'/stream/watch?path='$dir'&recursive='$rec
}

fn events {|&channel='*.*'|
    _curl -N $E:DAEMON_URL'/stream/events?channel='$channel
}

fn publish {|topic data|
    _post publish '{"topic":"'$topic'","data":'$data'}'
}