This commit is contained in:
2025-08-25 06:28:42 +02:00
parent 5f683ec4a8
commit 836c87fbec
44 changed files with 891 additions and 942 deletions

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run #!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.osal.tmux import freeflowuniverse.herolib.osal.tmux
mut t := tmux.new()! mut t := tmux.new()!

View File

@@ -3,13 +3,16 @@ module livekit
import freeflowuniverse.herolib.data.caching import freeflowuniverse.herolib.data.caching
import os import os
const CACHING_METHOD = caching.CachingMethod.once_per_process // const CACHING_METHOD = caching.CachingMethod.once_per_process
fn _init() ! { fn _init() ! {
if caching.is_set(key: 'livekit_clients') { if caching.is_set(key: 'livekit_clients') {
return return
} }
caching.set[map[string]LivekitClient](key: 'livekit_clients', val: map[string]LivekitClient{}, CachingMethod.once_per_process)! caching.set[map[string]LivekitClient](
key: 'livekit_clients'
val: map[string]LivekitClient{}
)!
} }
fn _get() !map[string]LivekitClient { fn _get() !map[string]LivekitClient {
@@ -25,7 +28,7 @@ pub fn get(name string) !LivekitClient {
pub fn set(client LivekitClient) ! { pub fn set(client LivekitClient) ! {
mut clients := _get()! mut clients := _get()!
clients[client.name] = client clients[client.name] = client
caching.set[map[string]LivekitClient](key: 'livekit_clients', val: clients, CachingMethod.once_per_process)! caching.set[map[string]LivekitClient](key: 'livekit_clients', val: clients)!
} }
pub fn exists(name string) !bool { pub fn exists(name string) !bool {

View File

@@ -60,25 +60,36 @@ pub fn (mut c LivekitClient) start_web_egress(args StartWebEgressArgs) !EgressIn
} }
pub fn (mut c LivekitClient) update_layout(egress_id string, layout string) !EgressInfo { pub fn (mut c LivekitClient) update_layout(egress_id string, layout string) !EgressInfo {
mut resp := c.post('twirp/livekit.Egress/UpdateLayout', {'egress_id': egress_id, 'layout': layout})! mut resp := c.post('twirp/livekit.Egress/UpdateLayout', {
'egress_id': egress_id
'layout': layout
})!
egress_info := json.decode[EgressInfo](resp.body)! egress_info := json.decode[EgressInfo](resp.body)!
return egress_info return egress_info
} }
pub fn (mut c LivekitClient) update_stream(egress_id string, args UpdateStreamArgs) !EgressInfo { pub fn (mut c LivekitClient) update_stream(egress_id string, args UpdateStreamArgs) !EgressInfo {
mut resp := c.post('twirp/livekit.Egress/UpdateStream', {'egress_id': egress_id, 'add_output_urls': args.add_output_urls, 'remove_output_urls': args.remove_output_urls})! mut resp := c.post('twirp/livekit.Egress/UpdateStream', {
'egress_id': egress_id
'add_output_urls': args.add_output_urls
'remove_output_urls': args.remove_output_urls
})!
egress_info := json.decode[EgressInfo](resp.body)! egress_info := json.decode[EgressInfo](resp.body)!
return egress_info return egress_info
} }
pub fn (mut c LivekitClient) list_egress(room_name string) ![]EgressInfo { pub fn (mut c LivekitClient) list_egress(room_name string) ![]EgressInfo {
mut resp := c.post('twirp/livekit.Egress/ListEgress', {'room_name': room_name})! mut resp := c.post('twirp/livekit.Egress/ListEgress', {
'room_name': room_name
})!
egress_infos := json.decode[[]EgressInfo](resp.body)! egress_infos := json.decode[[]EgressInfo](resp.body)!
return egress_infos return egress_infos
} }
pub fn (mut c LivekitClient) stop_egress(egress_id string) !EgressInfo { pub fn (mut c LivekitClient) stop_egress(egress_id string) !EgressInfo {
mut resp := c.post('twirp/livekit.Egress/StopEgress', {'egress_id': egress_id})! mut resp := c.post('twirp/livekit.Egress/StopEgress', {
'egress_id': egress_id
})!
egress_info := json.decode[EgressInfo](resp.body)! egress_info := json.decode[EgressInfo](resp.body)!
return egress_info return egress_info
} }

View File

@@ -93,8 +93,15 @@ pub mut:
video IngressVideoOptions video IngressVideoOptions
} }
pub fn (mut c LivekitClient) create_ingress(args CreateIngressArgs) !IngressInfo {
mut resp := c.post('twirp/livekit.Ingress/CreateIngress', args)!
ingress_info := json.decode[IngressInfo](resp.body)!
return ingress_info
}
pub struct UpdateIngressArgs { pub struct UpdateIngressArgs {
pub mut: pub mut:
ingress_id string
name string name string
room_name string room_name string
participant_identity string participant_identity string
@@ -103,26 +110,32 @@ pub mut:
video IngressVideoOptions video IngressVideoOptions
} }
pub fn (mut c LivekitClient) create_ingress(args CreateIngressArgs) !IngressInfo { pub fn (mut c LivekitClient) update_ingress(args UpdateIngressArgs) !IngressInfo {
mut resp := c.post('twirp/livekit.Ingress/CreateIngress', args)! mut resp := c.post('twirp/livekit.Ingress/UpdateIngress', {
ingress_info := json.decode[IngressInfo](resp.body)! 'ingress_id': args.ingress_id
return ingress_info 'name': args.name
} 'room_name': args.room_name
'participant_identity': args.participant_identity
pub fn (mut c LivekitClient) update_ingress(ingress_id string, args UpdateIngressArgs) !IngressInfo { 'participant_name': args.participant_name
mut resp := c.post('twirp/livekit.Ingress/UpdateIngress', {'ingress_id': ingress_id, ...args})! 'audio': args.audio
'video': args.video
})!
ingress_info := json.decode[IngressInfo](resp.body)! ingress_info := json.decode[IngressInfo](resp.body)!
return ingress_info return ingress_info
} }
pub fn (mut c LivekitClient) list_ingress(room_name string) ![]IngressInfo { pub fn (mut c LivekitClient) list_ingress(room_name string) ![]IngressInfo {
mut resp := c.post('twirp/livekit.Ingress/ListIngress', {'room_name': room_name})! mut resp := c.post('twirp/livekit.Ingress/ListIngress', {
'room_name': room_name
})!
ingress_infos := json.decode[[]IngressInfo](resp.body)! ingress_infos := json.decode[[]IngressInfo](resp.body)!
return ingress_infos return ingress_infos
} }
pub fn (mut c LivekitClient) delete_ingress(ingress_id string) !IngressInfo { pub fn (mut c LivekitClient) delete_ingress(ingress_id string) !IngressInfo {
mut resp := c.post('twirp/livekit.Ingress/DeleteIngress', {'ingress_id': ingress_id})! mut resp := c.post('twirp/livekit.Ingress/DeleteIngress', {
'ingress_id': ingress_id
})!
ingress_info := json.decode[IngressInfo](resp.body)! ingress_info := json.decode[IngressInfo](resp.body)!
return ingress_info return ingress_info
} }

View File

@@ -33,19 +33,27 @@ pub mut:
} }
pub fn (mut c LivekitClient) list_participants(room_name string) ![]ParticipantInfo { pub fn (mut c LivekitClient) list_participants(room_name string) ![]ParticipantInfo {
mut resp := c.post('twirp/livekit.RoomService/ListParticipants', {'room': room_name})! mut resp := c.post('twirp/livekit.RoomService/ListParticipants', {
'room': room_name
})!
participants := json.decode[[]ParticipantInfo](resp.body)! participants := json.decode[[]ParticipantInfo](resp.body)!
return participants return participants
} }
pub fn (mut c LivekitClient) get_participant(room_name string, identity string) !ParticipantInfo { pub fn (mut c LivekitClient) get_participant(room_name string, identity string) !ParticipantInfo {
mut resp := c.post('twirp/livekit.RoomService/GetParticipant', {'room': room_name, 'identity': identity})! mut resp := c.post('twirp/livekit.RoomService/GetParticipant', {
'room': room_name
'identity': identity
})!
participant := json.decode[ParticipantInfo](resp.body)! participant := json.decode[ParticipantInfo](resp.body)!
return participant return participant
} }
pub fn (mut c LivekitClient) remove_participant(room_name string, identity string) ! { pub fn (mut c LivekitClient) remove_participant(room_name string, identity string) ! {
_ = c.post('twirp/livekit.RoomService/RemoveParticipant', {'room': room_name, 'identity': identity})! _ = c.post('twirp/livekit.RoomService/RemoveParticipant', {
'room': room_name
'identity': identity
})!
} }
pub fn (mut c LivekitClient) update_participant(args UpdateParticipantArgs) ! { pub fn (mut c LivekitClient) update_participant(args UpdateParticipantArgs) ! {

View File

@@ -39,7 +39,9 @@ pub fn (mut c LivekitClient) create_room(args CreateRoomArgs) !Room {
} }
pub fn (mut c LivekitClient) delete_room(room_name string) ! { pub fn (mut c LivekitClient) delete_room(room_name string) ! {
_ = c.post('twirp/livekit.RoomService/DeleteRoom', {'room': room_name})! _ = c.post('twirp/livekit.RoomService/DeleteRoom', {
'room': room_name
})!
} }
pub fn (mut c LivekitClient) update_room_metadata(args UpdateRoomMetadataArgs) ! { pub fn (mut c LivekitClient) update_room_metadata(args UpdateRoomMetadataArgs) ! {

View File

@@ -15,19 +15,19 @@ pub:
unix_micro string unix_micro string
} }
pub struct Room { // pub struct Room {
pub: // pub:
active_recording bool // active_recording bool
creation_time string // creation_time string
departure_timeout int // departure_timeout int
empty_timeout int // empty_timeout int
enabled_codecs []Codec // enabled_codecs []Codec
max_participants int // max_participants int
metadata string // metadata string
name string // name string
num_participants int // num_participants int
num_publishers int // num_publishers int
sid string // sid string
turn_password string // turn_password string
version Version // version Version
} // }

View File

@@ -18,60 +18,60 @@ pub mut:
name string name string
} }
// VideoGrant struct placeholder // // VideoGrant struct placeholder
pub struct VideoGrant { // pub struct VideoGrant {
pub mut: // pub mut:
room string // room string
room_join bool @[json: 'roomJoin'] // room_join bool @[json: 'roomJoin']
room_list bool @[json: 'roomList'] // room_list bool @[json: 'roomList']
can_publish bool @[json: 'canPublish'] // can_publish bool @[json: 'canPublish']
can_publish_data bool @[json: 'canPublishData'] // can_publish_data bool @[json: 'canPublishData']
can_subscribe bool @[json: 'canSubscribe'] // can_subscribe bool @[json: 'canSubscribe']
} // }
// SIPGrant struct placeholder // SIPGrant struct placeholder
struct SIPGrant {} struct SIPGrant {}
// AccessToken class // // AccessToken class
pub struct AccessToken { // pub struct AccessToken {
mut: // mut:
api_key string // api_key string
api_secret string // api_secret string
grants ClaimGrants // grants ClaimGrants
identity string // identity string
ttl int // ttl int
} // }
// Method to add a video grant to the token // Method to add a video grant to the token
pub fn (mut token AccessToken) add_video_grant(grant VideoGrant) { // pub fn (mut token AccessToken) add_video_grant(grant VideoGrant) {
token.grants.video = grant // token.grants.video = grant
} // }
// Method to generate a JWT token // // Method to generate a JWT token
pub fn (token AccessToken) to_jwt() !string { // pub fn (token AccessToken) to_jwt() !string {
// Create JWT payload // // Create JWT payload
payload := json.encode(token.grants) // payload := json.encode(token.grants)
println('payload: ${payload}') // println('payload: ${payload}')
// Create JWT header // // Create JWT header
header := '{"alg":"HS256","typ":"JWT"}' // header := '{"alg":"HS256","typ":"JWT"}'
// Encode header and payload in base64 // // Encode header and payload in base64
header_encoded := base64.url_encode_str(header) // header_encoded := base64.url_encode_str(header)
payload_encoded := base64.url_encode_str(payload) // payload_encoded := base64.url_encode_str(payload)
// Create the unsigned token // // Create the unsigned token
unsigned_token := '${header_encoded}.${payload_encoded}' // unsigned_token := '${header_encoded}.${payload_encoded}'
// Create the HMAC-SHA256 signature // // Create the HMAC-SHA256 signature
signature := hmac.new(token.api_secret.bytes(), unsigned_token.bytes(), sha256.sum, // signature := hmac.new(token.api_secret.bytes(), unsigned_token.bytes(), sha256.sum,
sha256.block_size) // sha256.block_size)
// Encode the signature in base64 // // Encode the signature in base64
signature_encoded := base64.url_encode(signature) // signature_encoded := base64.url_encode(signature)
// Create the final JWT // // Create the final JWT
jwt := '${unsigned_token}.${signature_encoded}' // jwt := '${unsigned_token}.${signature_encoded}'
return jwt // return jwt
} // }

View File

@@ -40,12 +40,6 @@ pub fn (key SSHKey) private_key() !string {
return content return content
} }
module core
import freeflowuniverse.herolib.core.pathlib
import os
@[params] @[params]
pub struct SSHConfig { pub struct SSHConfig {
pub: pub:

View File

@@ -0,0 +1,12 @@
# Auto-start ssh-agent if not running
SSH_AGENT_PID_FILE="$HOME/.ssh/agent.pid"
SSH_AUTH_SOCK_FILE="$HOME/.ssh/agent.sock"
chown "$NEWUSER":"$NEWUSER" "$PROFILE_SCRIPT"
chmod 644 "$PROFILE_SCRIPT"
# --- source it on login ---
#TODO should be done in vcode
if ! grep -q ".profile_sshagent" "$USERHOME/.bashrc"; then
echo "[ -f ~/.profile_sshagent ] && source ~/.profile_sshagent" >> "$USERHOME/.bashrc"
fi

View File

@@ -57,19 +57,4 @@ chown root:ourworld /code
chmod 2775 /code # rwx for user+group, SGID bit so new files inherit group 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)" echo "✅ /code prepared (group=ourworld, rwx for group, SGID bit set)"
# --- create login helper script for ssh-agent ---
PROFILE_SCRIPT="$USERHOME/.profile_sshagent"
cat > "$PROFILE_SCRIPT" <<'EOF'
# Auto-start ssh-agent if not running
SSH_AGENT_PID_FILE="$HOME/.ssh/agent.pid"
SSH_AUTH_SOCK_FILE="$HOME/.ssh/agent.sock"
chown "$NEWUSER":"$NEWUSER" "$PROFILE_SCRIPT"
chmod 644 "$PROFILE_SCRIPT"
# --- source it on login ---
if ! grep -q ".profile_sshagent" "$USERHOME/.bashrc"; then
echo "[ -f ~/.profile_sshagent ] && source ~/.profile_sshagent" >> "$USERHOME/.bashrc"
fi
echo "🎉 Setup complete for user $NEWUSER" echo "🎉 Setup complete for user $NEWUSER"

View File

@@ -119,7 +119,9 @@ pub fn (mut lf LinuxFactory) sshkey_create(args SSHKeyCreateArgs) ! {
} else { } else {
// Generate new SSH key (modern ed25519) // Generate new SSH key (modern ed25519)
key_path := '${ssh_dir}/${args.sshkey_name}' key_path := '${ssh_dir}/${args.sshkey_name}'
osal.exec(cmd: 'ssh-keygen -t ed25519 -f ${key_path} -N "" -C "${args.username}@$(hostname)"')! osal.exec(
cmd: 'ssh-keygen -t ed25519 -f ${key_path} -N "" -C "${args.username}@$(hostname)"'
)!
console.print_green(' New SSH key generated for ${args.username}') console.print_green(' New SSH key generated for ${args.username}')
} }
@@ -201,7 +203,7 @@ fn (mut lf LinuxFactory) remove_user_config(username string) ! {
config_path := '${config_dir}/myconfig.json' config_path := '${config_dir}/myconfig.json'
if !os.exists(config_path) { if !os.exists(config_path) {
return // Nothing to remove return
} }
content := osal.file_read(config_path)! content := osal.file_read(config_path)!
@@ -243,7 +245,9 @@ fn (mut lf LinuxFactory) create_user_system(args UserCreateArgs) ! {
// Ensure ourworld group exists // Ensure ourworld group exists
group_check := osal.exec(cmd: 'getent group ourworld', raise_error: false) or { group_check := osal.exec(cmd: 'getent group ourworld', raise_error: false) or {
osal.Job{ exit_code: 1 } osal.Job{
exit_code: 1
}
} }
if group_check.exit_code != 0 { if group_check.exit_code != 0 {
console.print_item(' Creating group ourworld') console.print_item(' Creating group ourworld')
@@ -284,58 +288,9 @@ fn (mut lf LinuxFactory) create_ssh_agent_profile(username string) ! {
user_home := '/home/${username}' user_home := '/home/${username}'
profile_script := '${user_home}/.profile_sshagent' profile_script := '${user_home}/.profile_sshagent'
script_content := '# Auto-start ssh-agent if not running // script_content := ''
SSH_AGENT_PID_FILE="$HOME/.ssh/agent.pid"
SSH_AUTH_SOCK_FILE="$HOME/.ssh/agent.sock"
# Function to start ssh-agent panic('implement')
start_ssh_agent() {
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
# Start ssh-agent and save connection info
ssh-agent -s > "$SSH_AGENT_PID_FILE"
source "$SSH_AGENT_PID_FILE"
# Save socket path for future sessions
echo "$SSH_AUTH_SOCK" > "$SSH_AUTH_SOCK_FILE"
# Load all private keys found in ~/.ssh
if [ -d "$HOME/.ssh" ]; then
for KEY in "$HOME"/.ssh/*; do
if [ -f "$KEY" ] && [ ! "${KEY##*.}" = "pub" ] && grep -q "PRIVATE KEY" "$KEY" 2>/dev/null; then
'ssh-' + 'add "$KEY" >/dev/null 2>&1 && echo "🔑 Loaded key: $(basename $KEY)"'
fi
done
fi
}
# Check if ssh-agent is running
if [ -f "$SSH_AGENT_PID_FILE" ]; then
source "$SSH_AGENT_PID_FILE" >/dev/null 2>&1
# Test if agent is responsive
if ! ('ssh-' + 'add -l >/dev/null 2>&1'); then
start_ssh_agent
else
# Agent is running, restore socket path
if [ -f "$SSH_AUTH_SOCK_FILE" ]; then
export SSH_AUTH_SOCK=$(cat "$SSH_AUTH_SOCK_FILE")
fi
fi
else
start_ssh_agent
fi
# For interactive shells
if [[ $- == *i* ]]; then
echo "🔑 SSH Agent ready at $SSH_AUTH_SOCK"
# Show loaded keys
KEY_COUNT=$('ssh-' + 'add -l 2>/dev/null | wc -l')
if [ "$KEY_COUNT" -gt 0 ]; then
echo "🔑 $KEY_COUNT SSH key(s) loaded"
fi
fi
'
osal.file_write(profile_script, script_content)! osal.file_write(profile_script, script_content)!
osal.exec(cmd: 'chown ${username}:${username} ${profile_script}')! osal.exec(cmd: 'chown ${username}:${username} ${profile_script}')!

View File

@@ -96,9 +96,7 @@ fn sshkey_delete(mut agent SSHAgent, name string) ! {
fn sshkey_load(mut agent SSHAgent, name string) ! { fn sshkey_load(mut agent SSHAgent, name string) ! {
console.print_header('Loading SSH key: ${name}') console.print_header('Loading SSH key: ${name}')
mut key := agent.get(name: name) or { mut key := agent.get(name: name) or { return error('SSH key "${name}" not found') }
return error('SSH key "${name}" not found')
}
if key.loaded { if key.loaded {
console.print_debug('SSH key "${name}" is already loaded') console.print_debug('SSH key "${name}" is already loaded')
@@ -113,18 +111,12 @@ fn sshkey_load(mut agent SSHAgent, name string) ! {
fn sshkey_check(mut agent SSHAgent, name string) ! { fn sshkey_check(mut agent SSHAgent, name string) ! {
console.print_header('Checking SSH key: ${name}') console.print_header('Checking SSH key: ${name}')
mut key := agent.get(name: name) or { mut key := agent.get(name: name) or { return error('SSH key "${name}" not found') }
return error('SSH key "${name}" not found')
}
// Check if key files exist // Check if key files exist
key_path := key.keypath() or { key_path := key.keypath() or { return error('Private key file not found for "${name}"') }
return error('Private key file not found for "${name}"')
}
key_pub_path := key.keypath_pub() or { key_pub_path := key.keypath_pub() or { return error('Public key file not found for "${name}"') }
return error('Public key file not found for "${name}"')
}
if !key_path.exists() { if !key_path.exists() {
return error('Private key file does not exist: ${key_path.path}') return error('Private key file does not exist: ${key_path.path}')
@@ -157,9 +149,7 @@ fn remote_copy(mut agent SSHAgent, node_addr string, key_name string) ! {
console.print_header('Copying SSH key "${key_name}" to ${node_addr}') console.print_header('Copying SSH key "${key_name}" to ${node_addr}')
// Get the key // Get the key
mut key := agent.get(name: key_name) or { mut key := agent.get(name: key_name) or { return error('SSH key "${key_name}" not found') }
return error('SSH key "${key_name}" not found')
}
// Create builder node // Create builder node
mut b := builder.new()! mut b := builder.new()!

View File

@@ -92,9 +92,7 @@ pub fn (mut agent SSHAgent) verify_key_access(mut node builder.Node, key_name st
} }
// Test basic connectivity // Test basic connectivity
result := node.exec_silent('echo "SSH key verification successful"') or { result := node.exec_silent('echo "SSH key verification successful"') or { return false }
return false
}
return result.contains('SSH key verification successful') return result.contains('SSH key verification successful')
} }

View File

@@ -184,7 +184,12 @@ fn play_pane_kill(mut plbook PlayBook, mut tmux_instance Tmux) ! {
// Kill the active pane in the window // Kill the active pane in the window
if pane := window.pane_active() { if pane := window.pane_active() {
tmux_cmd := 'tmux kill-pane -t ${session.name}:@${window.id}.%${pane.id}' tmux_cmd := 'tmux kill-pane -t ${session.name}:@${window.id}.%${pane.id}'
osal.exec(cmd: tmux_cmd, stdout: false, name: 'tmux_pane_kill', ignore_error: true)! osal.exec(
cmd: tmux_cmd
stdout: false
name: 'tmux_pane_kill'
ignore_error: true
)!
} }
} }
} }

View File

@@ -14,7 +14,6 @@ pub mut:
sessionid string // unique link to job sessionid string // unique link to job
} }
// get session (session has windows) . // get session (session has windows) .
// returns none if not found // returns none if not found
pub fn (mut t Tmux) session_get(name_ string) !&Session { pub fn (mut t Tmux) session_get(name_ string) !&Session {
@@ -56,8 +55,6 @@ pub mut:
reset bool reset bool
} }
// create session, if reset will re-create // create session, if reset will re-create
pub fn (mut t Tmux) session_create(args SessionCreateArgs) !&Session { pub fn (mut t Tmux) session_create(args SessionCreateArgs) !&Session {
name := texttools.name_fix(args.name) name := texttools.name_fix(args.name)
@@ -83,7 +80,6 @@ pub fn (mut t Tmux) session_create(args SessionCreateArgs) !&Session {
return s return s
} }
@[params] @[params]
pub struct TmuxNewArgs { pub struct TmuxNewArgs {
sessionid string sessionid string
@@ -126,7 +122,6 @@ pub fn (mut t Tmux) window_new(args WindowNewArgs) !&Window {
)! )!
} }
pub fn (mut t Tmux) stop() ! { pub fn (mut t Tmux) stop() ! {
$if debug { $if debug {
console.print_debug('Stopping tmux...') console.print_debug('Stopping tmux...')
@@ -156,7 +151,6 @@ pub fn (mut t Tmux) start() ! {
t.scan()! t.scan()!
} }
// print list of tmux sessions // print list of tmux sessions
pub fn (mut t Tmux) list_print() { pub fn (mut t Tmux) list_print() {
// os.log('TMUX - Start listing ....') // os.log('TMUX - Start listing ....')

View File

@@ -20,7 +20,6 @@ pub mut:
last_output_offset int // for tracking new logs last_output_offset int // for tracking new logs
} }
pub fn (mut p Pane) stats() !ProcessStats { pub fn (mut p Pane) stats() !ProcessStats {
if p.pid == 0 { if p.pid == 0 {
return ProcessStats{} return ProcessStats{}
@@ -48,7 +47,6 @@ pub fn (mut p Pane) stats() !ProcessStats {
} }
} }
pub struct TMuxLogEntry { pub struct TMuxLogEntry {
pub mut: pub mut:
content string content string
@@ -57,20 +55,17 @@ pub mut:
} }
pub fn (mut p Pane) logs_get_new(reset bool) ![]TMuxLogEntry { pub fn (mut p Pane) logs_get_new(reset bool) ![]TMuxLogEntry {
if reset {
if reset{
p.last_output_offset = 0 p.last_output_offset = 0
} }
// Capture pane content with line numbers // Capture pane content with line numbers
cmd := 'tmux capture-pane -t ${p.window.session.name}:@${p.window.id}.%${p.id} -S ${p.last_output_offset} -p' 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 { result := osal.execute_silent(cmd) or { return error('Cannot capture pane output: ${err}') }
return error('Cannot capture pane output: ${err}')
}
lines := result.split_into_lines() lines := result.split_into_lines()
mut entries := []TMuxLogEntry{} mut entries := []TMuxLogEntry{}
mut i:= 0 mut i := 0
for line in lines { for line in lines {
if line.trim_space() != '' { if line.trim_space() != '' {
entries << TMuxLogEntry{ entries << TMuxLogEntry{
@@ -106,9 +101,7 @@ pub fn (mut p Pane) exit_status() !ProcessStatus {
pub fn (mut p Pane) logs_all() !string { pub fn (mut p Pane) logs_all() !string {
cmd := 'tmux capture-pane -t ${p.window.session.name}:@${p.window.id}.%${p.id} -S -2000 -p' cmd := 'tmux capture-pane -t ${p.window.session.name}:@${p.window.id}.%${p.id} -S -2000 -p'
return osal.execute_silent(cmd) or { return osal.execute_silent(cmd) or { error('Cannot capture pane output: ${err}') }
error('Cannot capture pane output: ${err}')
}
} }
// Fix the output_wait method to use correct method name // Fix the output_wait method to use correct method name

View File

@@ -1,7 +1,5 @@
module tmux module tmux
pub struct ProcessStats { pub struct ProcessStats {
pub mut: pub mut:
cpu_percent f64 cpu_percent f64
@@ -9,13 +7,9 @@ pub mut:
memory_percent f64 memory_percent f64
} }
enum ProcessStatus { enum ProcessStatus {
running running
finished_ok finished_ok
finished_error finished_error
not_found not_found
} }

View File

@@ -65,7 +65,7 @@ fn (mut t Tmux) scan_add(line string) !&Pane {
} }
// scan the system to detect sessions . // scan the system to detect sessions .
//TODO needs to be done differently, here only find the sessions, then per session call the scan() which will find the windows, call scan() there as well ... // TODO needs to be done differently, here only find the sessions, then per session call the scan() which will find the windows, call scan() there as well ...
pub fn (mut t Tmux) scan() ! { pub fn (mut t Tmux) scan() ! {
// os.log('TMUX - Scanning ....') // os.log('TMUX - Scanning ....')

View File

@@ -21,6 +21,7 @@ pub mut:
env map[string]string env map[string]string
reset bool reset bool
} }
@[params] @[params]
pub struct WindowGetArgs { pub struct WindowGetArgs {
pub mut: pub mut:
@@ -28,10 +29,9 @@ pub mut:
id int id int
} }
pub fn (mut s Session) create() ! { pub fn (mut s Session) create() ! {
// Check if session already exists // Check if session already exists
cmd_check := "tmux has-session -t ${s.name}" cmd_check := 'tmux has-session -t ${s.name}'
check_result := osal.exec(cmd: cmd_check, stdout: false, ignore_error: true) or { check_result := osal.exec(cmd: cmd_check, stdout: false, ignore_error: true) or {
// Session doesn't exist, this is expected // Session doesn't exist, this is expected
osal.Job{} osal.Job{}
@@ -42,19 +42,19 @@ pub fn (mut s Session) create() ! {
} }
// Create new session // Create new session
cmd := "tmux new-session -d -s ${s.name}" cmd := 'tmux new-session -d -s ${s.name}'
osal.exec(cmd: cmd, stdout: false, name: 'tmux_session_create') or { osal.exec(cmd: cmd, stdout: false, name: 'tmux_session_create') or {
return error("Can't create session ${s.name}: ${err}") return error("Can't create session ${s.name}: ${err}")
} }
} }
//load info from reality // load info from reality
pub fn (mut s Session) scan() ! { pub fn (mut s Session) scan() ! {
// Get current windows from tmux for this session // Get current windows from tmux for this session
cmd := "tmux list-windows -t ${s.name} -F '#{window_name}|#{window_id}|#{window_active}'" cmd := "tmux list-windows -t ${s.name} -F '#{window_name}|#{window_id}|#{window_active}'"
result := osal.execute_silent(cmd) or { result := osal.execute_silent(cmd) or {
if err.msg().contains('session not found') { if err.msg().contains('session not found') {
return // Session doesn't exist anymore return
} }
return error('Cannot list windows for session ${s.name}: ${err}') return error('Cannot list windows for session ${s.name}: ${err}')
} }
@@ -102,7 +102,6 @@ pub fn (mut s Session) scan() ! {
s.windows = s.windows.filter(current_windows[it.name] == true) s.windows = s.windows.filter(current_windows[it.name] == true)
} }
// window_name is the name of the window in session main (will always be called session main) // window_name is the name of the window in session main (will always be called session main)
// cmd to execute e.g. bash file // cmd to execute e.g. bash file
// environment arguments to use // environment arguments to use
@@ -143,10 +142,6 @@ pub fn (mut s Session) window_new(args WindowArgs) !Window {
return w return w
} }
// get all windows as found in a session // get all windows as found in a session
pub fn (mut s Session) windows_get() []&Window { pub fn (mut s Session) windows_get() []&Window {
mut res := []&Window{} mut res := []&Window{}
@@ -208,8 +203,6 @@ pub fn (mut s Session) stats() !ProcessStats {
// } // }
// } // }
fn (mut s Session) window_exist(args_ WindowGetArgs) bool { fn (mut s Session) window_exist(args_ WindowGetArgs) bool {
mut args := args_ mut args := args_
s.window_get(args) or { return false } s.window_get(args) or { return false }
@@ -249,7 +242,6 @@ pub fn (mut s Session) window_delete(args_ WindowGetArgs) ! {
s.windows.delete(i) // i is now the one in the list which needs to be removed s.windows.delete(i) // i is now the one in the list which needs to be removed
} }
pub fn (mut s Session) restart() ! { pub fn (mut s Session) restart() ! {
s.stop()! s.stop()!
s.create()! s.create()!

View File

@@ -22,12 +22,11 @@ pub mut:
pub struct PaneNewArgs { pub struct PaneNewArgs {
pub mut: pub mut:
name string name string
reset bool //means we reset the pane if it already exists reset bool // means we reset the pane if it already exists
cmd string cmd string
env map[string]string env map[string]string
} }
pub fn (mut w Window) scan() ! { pub fn (mut w Window) scan() ! {
// Get current panes for this window // Get current panes for this window
cmd := "tmux list-panes -t ${w.session.name}:@${w.id} -F '#{pane_id}|#{pane_pid}|#{pane_active}|#{pane_start_command}'" cmd := "tmux list-panes -t ${w.session.name}:@${w.id} -F '#{pane_id}|#{pane_pid}|#{pane_active}|#{pane_start_command}'"
@@ -81,12 +80,12 @@ pub fn (mut w Window) scan() ! {
w.panes = w.panes.filter(current_panes[it.id] == true) w.panes = w.panes.filter(current_panes[it.id] == true)
} }
pub fn (mut w Window) stop() ! { pub fn (mut w Window) stop() ! {
w.kill()! w.kill()!
} }
//helper function
//TODO env variables are not inserted in pane // helper function
// TODO env variables are not inserted in pane
pub fn (mut w Window) create(cmd_ string) ! { pub fn (mut w Window) create(cmd_ string) ! {
mut final_cmd := cmd_ mut final_cmd := cmd_
if cmd_.contains('\n') { if cmd_.contains('\n') {
@@ -100,7 +99,7 @@ pub fn (mut w Window) create(cmd_ string) ! {
} }
mut newcmd := '/bin/bash -c "${final_cmd}"' mut newcmd := '/bin/bash -c "${final_cmd}"'
if cmd_ == "" { if cmd_ == '' {
newcmd = '/bin/bash' newcmd = '/bin/bash'
} }

View File

@@ -1,17 +1,17 @@
module datamodel module datamodel
// I can bid for infra, and optionally get accepted
@[heap] @[heap]
//I can bid for infra, and optionally get accepted
pub struct Bid { pub struct Bid {
pub mut: pub mut:
id u32 id u32
customer_id u32 //links back to customer for this capacity (user on ledger) customer_id u32 // links back to customer for this capacity (user on ledger)
compute_slices_nr int //nr of slices I need in 1 machine compute_slices_nr int // nr of slices I need in 1 machine
compute_slice f64 //price per 1 GB slice I want to accept compute_slice f64 // price per 1 GB slice I want to accept
storage_slices []u32 storage_slices []u32
status BidStatus status BidStatus
obligation bool //if obligation then will be charged and money needs to be in escrow, otherwise its an intent obligation bool // if obligation then will be charged and money needs to be in escrow, otherwise its an intent
start_date u32 //epoch start_date u32 // epoch
end_date u32 end_date u32
} }

View File

@@ -4,11 +4,11 @@ module datamodel
pub struct Reservation { pub struct Reservation {
pub mut: pub mut:
id u32 id u32
customer_id u32 //links back to customer for this capacity customer_id u32 // links back to customer for this capacity
compute_slices []u32 compute_slices []u32
storage_slices []u32 storage_slices []u32
status ReservationStatus status ReservationStatus
start_date u32 //epoch start_date u32 // epoch
end_date u32 end_date u32
} }

View File

@@ -1,5 +1,7 @@
module datamodel module datamodel
import freeflowuniverse.herolib.threefold.grid4.datamodel { Node } import freeflowuniverse.herolib.threefold.grid4.datamodel { Node }
pub struct NodeSim { pub struct NodeSim {
Node Node
pub mut: pub mut: