This commit is contained in:
2025-08-24 15:10:03 +02:00
parent d07aec8434
commit 810cbda176
8 changed files with 233 additions and 35 deletions

View File

@@ -10,5 +10,11 @@ if !t.is_running()! {
if t.session_exist('main') {
t.session_delete('main')!
}
t.window_new(name: 'test', cmd: 'mc', reset: true)!
// Create session first, then create window
mut session := t.session_create(name: 'main')!
session.window_new(name: 'test', cmd: 'mc', reset: true)!
// Or use the convenience method
// t.window_new(session_name: 'main', name: 'test', cmd: 'mc', reset: true)!
println(t)

29
lib/osal/linux/factory.v Normal file
View File

@@ -0,0 +1,29 @@
module linux
// import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.core.texttools
// import freeflowuniverse.herolib.screen
import os
import time
// import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.osal.core as osal
@[heap]
pub struct LinuxFactory {
pub mut:
username string
}
@[params]
pub struct LinuxNewArgs {
pub:
username string
}
// return screen instance
pub fn new(args LinuxNewArgs) !LinuxFactory {
mut t := LinuxFactory{
username: args.username
}
return t
}

View File

@@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -euo pipefail
if [ "$(id -u)" -ne 0 ]; then
echo "❌ Must be run as root"
exit 1
fi
# --- ask for username ---
read -rp "Enter username to create: " NEWUSER
# --- ask for SSH public key ---
read -rp "Enter SSH public key (or path to pubkey file): " PUBKEYINPUT
if [ -f "$PUBKEYINPUT" ]; then
PUBKEY="$(cat "$PUBKEYINPUT")"
else
PUBKEY="$PUBKEYINPUT"
fi
# --- ensure user exists ---
if id "$NEWUSER" >/dev/null 2>&1; then
echo "✅ User $NEWUSER already exists"
else
echo " Creating user $NEWUSER"
useradd -m -s /bin/bash "$NEWUSER"
fi
USERHOME=$(eval echo "~$NEWUSER")
# --- setup SSH authorized_keys ---
mkdir -p "$USERHOME/.ssh"
chmod 700 "$USERHOME/.ssh"
echo "$PUBKEY" > "$USERHOME/.ssh/authorized_keys"
chmod 600 "$USERHOME/.ssh/authorized_keys"
chown -R "$NEWUSER":"$NEWUSER" "$USERHOME/.ssh"
echo "✅ SSH key installed for $NEWUSER"
# --- ensure ourworld group exists ---
if getent group ourworld >/dev/null 2>&1; then
echo "✅ Group 'ourworld' exists"
else
echo " Creating group 'ourworld'"
groupadd ourworld
fi
# --- add user to group ---
if id -nG "$NEWUSER" | grep -qw ourworld; then
echo "$NEWUSER already in 'ourworld'"
else
usermod -aG ourworld "$NEWUSER"
echo "✅ Added $NEWUSER to 'ourworld' group"
fi
# --- setup /code ---
mkdir -p /code
chown root:ourworld /code
chmod 2775 /code # rwx for user+group, SGID bit so new files inherit group
echo "✅ /code prepared (group=ourworld, rwx for group, SGID bit set)"
# --- create login helper script for gpg-agent ---
PROFILE_SCRIPT="$USERHOME/.profile_gpgagent"
cat > "$PROFILE_SCRIPT" <<'EOF'
# Auto-start gpg-agent with SSH support if not running
mkdir -p "$HOME/.gnupg"
chmod 700 "$HOME/.gnupg"
# Always overwrite gpg-agent.conf with required config
cat > "$HOME/.gnupg/gpg-agent.conf" <<CONF
enable-ssh-support
default-cache-ttl 7200
max-cache-ttl 7200
CONF
# Kill old agent if any (so config is applied)
gpgconf --kill gpg-agent 2>/dev/null || true
# Launch gpg-agent
gpgconf --launch gpg-agent
# Export socket path so ssh-add works
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
# Load all private keys found in ~/.ssh
if [ -d "$HOME/.ssh" ]; then
for KEY in "$HOME"/.ssh/*; do
if [ -f "$KEY" ] && grep -q "PRIVATE KEY" "$KEY" 2>/dev/null; then
ssh-add "$KEY" >/dev/null 2>&1 && echo "🔑 Loaded key: $KEY"
fi
done
fi
# For interactive shells
if [[ $- == *i* ]]; then
echo "🔑 GPG Agent ready at \$SSH_AUTH_SOCK"
fi
EOF
chown "$NEWUSER":"$NEWUSER" "$PROFILE_SCRIPT"
chmod 644 "$PROFILE_SCRIPT"
# --- source it on login ---
if ! grep -q ".profile_gpgagent" "$USERHOME/.bashrc"; then
echo "[ -f ~/.profile_gpgagent ] && source ~/.profile_gpgagent" >> "$USERHOME/.bashrc"
fi
echo "🎉 Setup complete for user $NEWUSER"

View File

@@ -0,0 +1,29 @@
module linux
// import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.core.texttools
// import freeflowuniverse.herolib.screen
import os
import time
// import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.osal.core as osal
@[heap]
pub struct LinuxFactory {
pub mut:
username string
}
@[params]
pub struct LinuxNewArgs {
pub:
username string
}
// return screen instance
pub fn new(args LinuxNewArgs) !LinuxFactory {
mut t := LinuxFactory{
username: args.username
}
return t
}

View File

@@ -1,6 +1,7 @@
module tmux
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.core.texttools
// import freeflowuniverse.herolib.session
import os
import time
@@ -98,6 +99,33 @@ pub fn new(args TmuxNewArgs) !Tmux {
return t
}
@[params]
pub struct WindowNewArgs {
pub mut:
session_name string = 'main'
name string
cmd string
env map[string]string
reset bool
}
pub fn (mut t Tmux) window_new(args WindowNewArgs) !&Window {
// Get or create session
mut session := if t.session_exist(args.session_name) {
t.session_get(args.session_name)!
} else {
t.session_create(name: args.session_name)!
}
// Create window in session
return session.window_new(
name: args.name
cmd: args.cmd
env: args.env
reset: args.reset
)!
}
pub fn (mut t Tmux) stop() ! {
$if debug {
@@ -128,19 +156,6 @@ pub fn (mut t Tmux) start() ! {
t.scan()!
}
enum ProcessStatus {
running
finished_ok
finished_error
not_found
}
pub struct LogEntry {
pub mut:
content string
timestamp time.Time
offset int
}
// print list of tmux sessions
pub fn (mut t Tmux) list_print() {

View File

@@ -1,9 +1,10 @@
module tmux
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.data.ourtime
import time
// import freeflowuniverse.herolib.session
import os
import time
import freeflowuniverse.herolib.ui.console
@[heap]
@@ -48,14 +49,14 @@ pub fn (mut p Pane) stats() !ProcessStats {
}
pub struct LogEntry {
pub struct TMuxLogEntry {
pub mut:
content string
timestamp time.Time
offset int
}
pub fn (mut p Pane) logs_get_new(reset bool) ![]LogEntry {
pub fn (mut p Pane) logs_get_new(reset bool) ![]TMuxLogEntry {
if reset{
p.last_output_offset = 0
@@ -64,15 +65,15 @@ pub fn (mut p Pane) logs_get_new(reset bool) ![]LogEntry {
cmd := 'tmux capture-pane -t ${p.window.session.name}:@${p.window.id}.%${p.id} -S ${p.last_output_offset} -p'
result := osal.execute_silent(cmd) or {
return error('Cannot capture pane output: ${err}')
}
}
lines := result.split_into_lines()
mut entries := []LogEntry{}
mut entries := []TMuxLogEntry{}
mut i:= 0
for line in lines {
if line.trim_space() != '' {
entries << LogEntry{
entries << TMuxLogEntry{
content: line
timestamp: time.now()
offset: p.last_output_offset + i + 1
@@ -83,7 +84,7 @@ pub fn (mut p Pane) logs_get_new(reset bool) ![]LogEntry {
if entries.len > 0 {
p.last_output_offset = entries.last().offset
}
return entries
return entries
}
pub fn (mut p Pane) exit_status() !ProcessStatus {

View File

@@ -3,6 +3,7 @@ module tmux
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.ui.console
import time
fn (mut t Tmux) scan_add(line string) !&Pane {
// Parse the line to get session, window, and pane info

View File

@@ -17,6 +17,8 @@ pub mut:
pub struct WindowArgs {
pub mut:
name string
cmd string
env map[string]string
reset bool
}
@[params]
@@ -27,6 +29,25 @@ pub mut:
}
pub fn (mut s Session) create() ! {
// Check if session already exists
cmd_check := "tmux has-session -t ${s.name}"
check_result := osal.exec(cmd: cmd_check, stdout: false, ignore_error: true) or {
// Session doesn't exist, this is expected
osal.Job{}
}
if check_result.exit_code == 0 {
return error('duplicate session: ${s.name}')
}
// Create new session
cmd := "tmux new-session -d -s ${s.name}"
osal.exec(cmd: cmd, stdout: false, name: 'tmux_session_create') or {
return error("Can't create session ${s.name}: ${err}")
}
}
//load info from reality
pub fn (mut s Session) scan() ! {
// Get current windows from tmux for this session
@@ -115,21 +136,10 @@ pub fn (mut s Session) window_new(args WindowArgs) !Window {
}
s.windows << &w
res_opt := "-P -F '#\{window_id\}'"
cmd := "tmux new-session ${res_opt} -d -s ${s.name} 'sh'"
window_id_ := osal.execute_silent(cmd) or {
return error("Can't create tmux session ${s.name} \n${cmd}\n${err}")
}
cmd3 := 'tmux set-option remain-on-exit on'
osal.execute_silent(cmd3) or { return error("Can't execute ${cmd3}\n${err}") }
window_id := window_id_.trim(' \n')
cmd2 := "tmux rename-window -t ${window_id} 'notused'"
osal.execute_silent(cmd2) or {
return error("Can't rename window ${window_id} to notused \n${cmd2}\n${err}")
}
// Create the window with the specified command
w.create(args.cmd)!
s.scan()!
return w
}