improved javascript bridge
/ mirror_to_github (push) Successful in 20s Details
/ test (push) Successful in 4s Details

This commit is contained in:
Leon van Kammen 2024-08-29 15:18:14 +00:00
parent 7bfa591536
commit 69a1760a11
9 changed files with 226 additions and 67 deletions

View File

@ -150,7 +150,6 @@ AFRAME.registerComponent('isoterminal', {
'still faster than Windows update', 'still faster than Windows update',
'loading a microlinux', 'loading a microlinux',
'figuring out meaning of life', 'figuring out meaning of life',
'asking LLM why men have nipples',
'Aligning your chakras now', 'Aligning your chakras now',
'Breathing in good vibes', 'Breathing in good vibes',
'Finding inner peace soon', 'Finding inner peace soon',
@ -181,11 +180,41 @@ AFRAME.registerComponent('isoterminal', {
"com/isoterminal/mnt/prompt", "com/isoterminal/mnt/prompt",
"com/isoterminal/mnt/alert", "com/isoterminal/mnt/alert",
"com/isoterminal/mnt/hook", "com/isoterminal/mnt/hook",
"com/isoterminal/mnt/xrsh",
"com/isoterminal/mnt/profile", "com/isoterminal/mnt/profile",
"com/isoterminal/mnt/profile.xrsh",
"com/isoterminal/mnt/profile.js", "com/isoterminal/mnt/profile.js",
"com/isoterminal/mnt/motd", "com/isoterminal/mnt/motd",
"com/isoterminal/mnt/v86pipe"
] ]
const redirectConsole = (handler) => {
const log = console.log;
const dir = console.dir;
const err = console.error;
const warn = console.warn;
console.log = (...args)=>{
const textArg = args[0];
handler(textArg+'\n');
log.apply(log, args);
};
console.error = (...args)=>{
const textArg = args[0].message?args[0].message:args[0];
handler( textArg+'\n', '\x1b[31merror\x1b[0m');
err.apply(log, args);
};
console.dir = (...args)=>{
const textArg = args[0].message?args[0].message:args[0];
handler( JSON.stringify(textArg,null,2)+'\n');
dir.apply(log, args);
};
console.warn = (...args)=>{
const textArg = args[0].message?args[0].message:args[0];
handler(textArg+'\n','\x1b[38;5;208mwarn\x1b[0m');
err.apply(log, args);
};
}
emulator.bus.register("emulator-started", async () => { emulator.bus.register("emulator-started", async () => {
emulator.serial_adapter.term.element.querySelector('.xterm-viewport').style.background = 'transparent' emulator.serial_adapter.term.element.querySelector('.xterm-viewport').style.background = 'transparent'
emulator.serial_adapter.term.clear() emulator.serial_adapter.term.clear()
@ -197,6 +226,17 @@ AFRAME.registerComponent('isoterminal', {
cat > /dev/null cat > /dev/null
`)) `))
redirectConsole( (str,prefix) => {
if( emulator.log_to_tty ){
prefix = prefix ? prefix+' ' : ' '
str.trim().split("\n").map( (line) => {
emulator.serial_adapter.term.write( '\r\x1b[38;5;165m/dev/browser: \x1b[0m'+prefix+line+'\n' )
})
emulator.serial_adapter.term.write( '\r' )
}
emulator.create_file( "console", this.toUint8Array( str ) )
})
let p = files.map( (f) => fetch(f) ) let p = files.map( (f) => fetch(f) )
Promise.all(p) Promise.all(p)
.then( (files) => { .then( (files) => {
@ -267,16 +307,24 @@ AFRAME.registerComponent('isoterminal', {
const buf = await emulator.read_file("dev/browser/js") const buf = await emulator.read_file("dev/browser/js")
const script = decoder.decode(buf) const script = decoder.decode(buf)
try{ try{
let res = (new Function(`return ${script}`))() let res = (new Function(`${script}`))()
if( res && typeof res != 'string' ) res = JSON.stringify(res,null,2) if( res && typeof res != 'string' ) res = JSON.stringify(res,null,2)
emulator.create_file( "dev/browser/js", this.toUint8Array( res || '' ) )
}catch(e){ }catch(e){
console.dir(e) console.error(e)
emulator.create_file("dev/browser/js", this.toUint8Array( `[e] `+String(e.stack) ) )
} }
} }
}) })
// enable/disable logging file (echo 1 > mnt/console.tty)
emulator.add_listener("9p-write-end", async (opts) => {
const decoder = new TextDecoder('utf-8');
if ( opts[0] == 'console.tty' ){
const buf = await emulator.read_file("console.tty")
const val = decoder.decode(buf)
emulator.log_to_tty = ( String(val).trim() == '1')
}
})
}); });
}, },

View File

@ -1,2 +1,6 @@
#!/bin/sh #!/bin/sh
echo "$(printf "\033[0m")[i] $1 $(printf "\033[0m")" title=$1
shift
msg="$*"
echo "$title $(printf "\033[0m")$msg"
hook alert $title "$msg"

View File

@ -1,15 +1,11 @@
#!/bin/sh #!/bin/sh
test -z $1 && { echo "usage: hook <cmd_or_jsfunction> [args]"; exit 0; } test -z $1 && { echo "usage: hook <cmd_or_jsfunction> [args]"; exit 0; }
cmd=$1 cmd=$1
shift shift
test -d ~/hook.d/$cmd && { test -d ~/hook.d/$cmd && {
chmod +x ~/hook.d/$cmd/*
find ~/hook.d/$cmd/ -type f -executable | while read hook; do find ~/hook.d/$cmd/ -type f -executable | while read hook; do
$hook "$@" || true { $hook "$@" || true; } | awk '{ gsub(/\/root\/\//,"",$1); $1 = sprintf("%-40s", $1)} 1'
done done
} }
#test $BROWSER = 1 && {

View File

@ -1,16 +1,19 @@
#!/bin/sh #!/bin/sh
test -z $1 && { echo "Usage: js 'somefunction(1)'"; exit 0; } test -z "$1" && { echo "Usage: js 'somefunction(1)'"; exit 0; }
test -n "$BROWSER" || { alert warning "/dev/browser not active (are you running outside of v86?)"; }
javascript="$*" javascript="$*"
# if we are run as shebang, use the file as input # if we are run as shebang, use the file as input
case "$1" in case "$1" in
*/*) javascript="$(cat $1 | tail +2)" */*) javascript="args = String('$*').split(' '); $(cat $1 | tail +2)"
;; ;;
esac esac
echo -n "$javascript" > /dev/browser/js
flock /dev/browser/js -c "echo '$javascript' > /dev/browser/js; sleep 0.5; cat /dev/browser/js" # should we use flock, an awesome way to make processes read/write the same file
# while preventing 1001 concurrency issues?
# we use flock, an awesome way to make processes read/write the same file # attempt:
# while preventing 1001 concurrency issues #
# flock /dev/browser/js -c "echo \"$javascript\" > /dev/browser/js"

View File

@ -20,18 +20,23 @@ to_js(){
} }
# run argument as js # run argument as js
test -z $1 || { test -z "$1" || {
func=$(to_js "$@") func=$(to_js "$@")
func=${func/,)/)} func=${func/,)/)}
js "$func" js "$func"
hook "$@"
exit 0
} }
# otherwise start repl # otherwise start repl
echo "jsh> type 'exit' or CTRL-C to quit"
echo "jsh> HINT: to run alert('foo') outside this REPL, run 'jsh alert foo'"
echo "jsh>"
while true; do while true; do
echo -n "$(printf "\033[0m")jsh> $(printf "\033[0m")" echo -n -e "\r$(printf "\033[0m")jsh> $(printf "\033[0m")"
read input read line
test $input = exit && exit test "$line" = exit && exit
js "$input" js "$line"
done done

View File

@ -1,50 +1,15 @@
install_xrsh(){ # install xrsh env
source /mnt/profile.xrsh
setup_binaries(){
for bin in /mnt/prompt /mnt/alert /mnt/confirm /mnt/hook /mnt/js*; do
chmod +x $bin
ln -s $bin /bin/.
done
}
setup_browser_dev(){
mkdir -p /mnt/dev/browser
touch /mnt/dev/browser/js
touch /mnt/dev/browser/html
touch /mnt/dev/browser/console
ln -s /mnt/dev/browser /dev/browser
test -f /etc/profile && rm /etc/profile
ln -s /mnt/profile /etc/profile
}
setup_hook_dirs(){ # see /mnt/hook for usage
mkdir -p ~/hook.d/alert
mkdir -p ~/hook.d/confirm
mkdir -p ~/hook.d/prompt
echo -e "#!/bin/sh\necho yo" > ~/hook.d/alert/yo
echo -e "#!/bin/js\nalert('yo')" > ~/hook.d/alert/yo.js
echo -e "#!/usr/bin/lua\nalert('yo')" > ~/hook.d/alert/yo.lua
}
setup_binaries
setup_browser_dev
setup_hook_dirs
}
test -d /dev/browser || install_xrsh
test -f /mnt/V86 && {
mount -a
udhcpc 1>>/var/log/network.log 2>>/var/log/network.log &
echo 0 > /proc/sys/kernel/printk
}
## forward not-found commands to javascript (via jsh) ## forward not-found commands to javascript (via jsh)
command_not_found_handle(){ command_not_found_handle(){
echo "$1 not found" echo "$1 not found, did you mean $1(...) (javascript?)"
alert "did you mean $1(...) (javascript?)" alert '[XRSH TIPS]'
alert "TIP: run 'jsh $1 hello' to run $1('hello')" alert 'js console: ' "type 'jsh'"
alert " or simply 'jsh' for a js console" alert 'js shellfunction:' "type 'alias $1=\"jsh $1\"' to run '$1 yo' as $1('yo')"
alert 'js logging: ' "type 'echo 0 > /dev/browser/console.tty' to disable"
alert 'js capture log: ' "type 'tail -f /dev/browser/console'"
alert 'jsh<->sh hooks: ' "type 'chmod +x ~/hook.d/alert/* && alert helloworld'"
} }
# source javascript functions # source javascript functions
@ -55,4 +20,3 @@ resize
cat /mnt/motd cat /mnt/motd
export PATH=$PATH:/mnt export PATH=$PATH:/mnt
export PS1="\nxrsh # \033[0m" export PS1="\nxrsh # \033[0m"
export BROWSER=0 # running inside v86 (wasm) will set this to 1

View File

@ -0,0 +1,47 @@
#!/bin/sh
test -d /dev/browser || {
setup_binaries(){
for bin in /mnt/prompt /mnt/alert /mnt/confirm /mnt/hook /mnt/js* /mnt/v86pipe /mnt/xrsh; do
chmod +x $bin
ln -s $bin /bin/.
done
}
setup_browser_dev(){
mkdir -p /mnt/dev/browser
touch /mnt/dev/browser/js
touch /mnt/dev/browser/html
touch /mnt/console.tty
ln -s /mnt/dev/browser /dev/browser
# setup console goodies
ln -s /mnt/console.tty /dev/browser/console.tty # emulator.write_file() only writes to /mnt/. :(
echo 1 > /dev/browser/console.tty # should be in /proc, but v86 gives 'no such file or dir' when creating it there
v86pipe /mnt/console /dev/browser/console &
test -f /etc/profile && rm /etc/profile
ln -s /mnt/profile /etc/profile
}
setup_hook_dirs(){ # see /mnt/hook for usage
mkdir -p ~/hook.d/alert
mkdir -p ~/hook.d/confirm
mkdir -p ~/hook.d/prompt
echo -e "#!/bin/sh\necho hook.d/alert/yo: yo \$*" > ~/hook.d/alert/yo
echo -e "#!/bin/js\nalert(\"hook.d/alert/yo.js \"+args.slice(1).join(' '))" > ~/hook.d/alert/yo.js
echo -e "#!/usr/bin/lua\nprint(\"hook.d/alert/yo.lua: yo \" .. arg[1])" > ~/hook.d/alert/yo.lua
}
setup_network(){
test -n "$BROWSER" || return 0
mount -a
udhcpc 1>>/var/log/network.log 2>>/var/log/network.log &
echo 0 > /proc/sys/kernel/printk
}
setup_binaries
setup_browser_dev
setup_hook_dirs
}

30
com/isoterminal/mnt/v86pipe Executable file
View File

@ -0,0 +1,30 @@
#!/bin/sh
#
# this daemon allows 'tail -f' on v86 files (which don't persist inode when updated
# via javascript)
# more info see: https://github.com/copy/v86/issues/1140
#
# Hopefully as V86 (or my understanding of it) matures, this will be no longer needed
test -z $2 && { echo "usage: v86pipe <logfile> <namedpipe>"; exit 0; }
# Start reading from the last line in the log file
last_size=0
LOG_FILE=$1
LOG_PIPE=$2
test -f $LOG_FILE || touch $LOG_FILE
test -p $LOG_PIPE || mkfifo $LOG_PIPE
while true; do
# Get the current size of the file using wc -c (count bytes)
current_size=$(wc -c < $LOG_FILE)
test $current_size = $last_size || {
cat $LOG_FILE > $LOG_PIPE
truncate -s 0 $LOG_FILE
}
last_size=$current_size
# Sleep for a moment to avoid excessive CPU usage
sleep 0.2
done

62
com/isoterminal/mnt/xrsh Executable file
View File

@ -0,0 +1,62 @@
#!/bin/sh
#
# a minimalistic terminal muxer
# Save the original stdout and stderr file descriptors for later restoration
exec 3>&1 4>&2
# Function to check if a session is already running on the given VT
is_session_running() {
vt_number=$1
# Check if any process is running on /dev/tty<vt_number>
fuser /dev/tty"$vt_number" >/dev/null 2>&1
return $?
}
# Function to mute the output of a session
mute_session() {
vt_number=$1
if is_session_running "$vt_number"; then
# Redirect stdout and stderr of the session to /dev/null
exec > /dev/null 2>&1
fi
}
# Function to unmute the current session (restore stdout and stderr)
unmute_session() {
exec 1>&3 2>&4 # Restore stdout and stderr from file descriptors 3 and 4
}
# Function to start a new session if not already running
start_or_switch_session() {
vt_number=$1
# Mute all other sessions except the one we're switching to
for vt in $(seq 1 12); do # Assuming you have up to 12 VTs, adjust as needed
if [ "$vt" != "$vt_number" ]; then
mute_session "$vt"
fi
done
if is_session_running "$vt_number"; then
echo "Switching to existing session on VT$vt_number"
unmute_session # Unmute the session we're switching to
chvt "$vt_number"
else
echo "Starting a new session on VT$vt_number"
openvt -c "$vt_number" -- /bin/sh &
sleep 1 # Give the session a moment to start
unmute_session # Unmute the new session
chvt "$vt_number"
fi
}
# Ensure a session number is provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <session_number>"
exit 1
fi
# Start or switch to the session
start_or_switch_session $1