...
This commit is contained in:
@@ -25,7 +25,7 @@ if agent.keys.len == 0 {
|
|||||||
console.print_header('No keys found, generating example key...')
|
console.print_header('No keys found, generating example key...')
|
||||||
mut key := agent.generate('example_key', '')!
|
mut key := agent.generate('example_key', '')!
|
||||||
console.print_debug('Generated key: ${key}')
|
console.print_debug('Generated key: ${key}')
|
||||||
|
|
||||||
// Load the generated key
|
// Load the generated key
|
||||||
key.load()!
|
key.load()!
|
||||||
console.print_debug('Key loaded into agent')
|
console.print_debug('Key loaded into agent')
|
||||||
@@ -62,4 +62,4 @@ if agent.keys.len > 0 {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
console.print_header('SSH Agent example completed successfully')
|
console.print_header('SSH Agent example completed successfully')
|
||||||
|
|||||||
@@ -25,19 +25,19 @@ fn test_user_mgmt() ! {
|
|||||||
mut lf := linux.new()!
|
mut lf := linux.new()!
|
||||||
// Test user creation
|
// Test user creation
|
||||||
lf.user_create(
|
lf.user_create(
|
||||||
name: 'testuser'
|
name: 'testuser'
|
||||||
sshkey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM3/2K7R8A/l0kM0/d'
|
sshkey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM3/2K7R8A/l0kM0/d'
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Test ssh key creation
|
// Test ssh key creation
|
||||||
lf.sshkey_create(
|
lf.sshkey_create(
|
||||||
username: 'testuser'
|
username: 'testuser'
|
||||||
sshkey_name: 'testkey'
|
sshkey_name: 'testkey'
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Test ssh key deletion
|
// Test ssh key deletion
|
||||||
lf.sshkey_delete(
|
lf.sshkey_delete(
|
||||||
username: 'testuser'
|
username: 'testuser'
|
||||||
sshkey_name: 'testkey'
|
sshkey_name: 'testkey'
|
||||||
)!
|
)!
|
||||||
|
|
||||||
|
|||||||
@@ -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()!
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import time
|
|||||||
|
|
||||||
mut t := tmux.new()!
|
mut t := tmux.new()!
|
||||||
if !t.is_running()! {
|
if !t.is_running()! {
|
||||||
t.start()!
|
t.start()!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a session and window
|
// Create a session and window
|
||||||
@@ -18,15 +18,15 @@ time.sleep(1000 * time.millisecond)
|
|||||||
|
|
||||||
// Get the active pane
|
// Get the active pane
|
||||||
if mut pane := window.pane_active() {
|
if mut pane := window.pane_active() {
|
||||||
// Get process info for the pane and its children
|
// Get process info for the pane and its children
|
||||||
process_map := pane.processinfo()!
|
process_map := pane.processinfo()!
|
||||||
|
|
||||||
println('Process tree for pane ${pane.id}:')
|
println('Process tree for pane ${pane.id}:')
|
||||||
for process in process_map.processes {
|
for process in process_map.processes {
|
||||||
println(' PID: ${process.pid}, CPU: ${process.cpu_perc}%, Memory: ${process.mem_perc}%, Command: ${process.cmd}')
|
println(' PID: ${process.pid}, CPU: ${process.cpu_perc}%, Memory: ${process.mem_perc}%, Command: ${process.cmd}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get just the main process info
|
// Get just the main process info
|
||||||
main_process := pane.processinfo_main()!
|
main_process := pane.processinfo_main()!
|
||||||
println('\nMain process: PID ${main_process.pid}, Command: ${main_process.cmd}')
|
println('\nMain process: PID ${main_process.pid}, Command: ${main_process.cmd}')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import time
|
|||||||
fn (mut c LivekitClient) post(path string, body any) !http.Response {
|
fn (mut c LivekitClient) post(path string, body any) !http.Response {
|
||||||
mut token := c.new_access_token(
|
mut token := c.new_access_token(
|
||||||
identity: 'api'
|
identity: 'api'
|
||||||
name: 'API User'
|
name: 'API User'
|
||||||
ttl: 10 * 60 // 10 minutes
|
ttl: 10 * 60 // 10 minutes
|
||||||
)!
|
)!
|
||||||
token.add_video_grant(VideoGrant{
|
token.add_video_grant(VideoGrant{
|
||||||
room_create: true
|
room_create: true
|
||||||
room_admin: true
|
room_admin: true
|
||||||
room_list: true
|
room_list: true
|
||||||
})
|
})
|
||||||
jwt := token.to_jwt()!
|
jwt := token.to_jwt()!
|
||||||
|
|
||||||
@@ -25,13 +25,13 @@ fn (mut c LivekitClient) post(path string, body any) !http.Response {
|
|||||||
data := json.encode(body)
|
data := json.encode(body)
|
||||||
mut req := http.Request{
|
mut req := http.Request{
|
||||||
method: .post
|
method: .post
|
||||||
url: url
|
url: url
|
||||||
header: header
|
header: header
|
||||||
data: data
|
data: data
|
||||||
}
|
}
|
||||||
resp := http.fetch(req)!
|
resp := http.fetch(req)!
|
||||||
if resp.status_code != 200 {
|
if resp.status_code != 200 {
|
||||||
return error('failed to execute request: ${resp.body}')
|
return error('failed to execute request: ${resp.body}')
|
||||||
}
|
}
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,10 +28,10 @@ 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 {
|
||||||
mut clients := _get()!
|
mut clients := _get()!
|
||||||
return name in clients
|
return name in clients
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ module livekit
|
|||||||
|
|
||||||
pub struct SendDataArgs {
|
pub struct SendDataArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
room_name string
|
room_name string
|
||||||
data []u8
|
data []u8
|
||||||
kind DataPacket_Kind
|
kind DataPacket_Kind
|
||||||
destination_sids []string
|
destination_sids []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,4 +15,4 @@ pub enum DataPacket_Kind {
|
|||||||
|
|
||||||
pub fn (mut c LivekitClient) send_data(args SendDataArgs) ! {
|
pub fn (mut c LivekitClient) send_data(args SendDataArgs) ! {
|
||||||
_ = c.post('twirp/livekit.RoomService/SendData', args)!
|
_ = c.post('twirp/livekit.RoomService/SendData', args)!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,40 +4,40 @@ import json
|
|||||||
|
|
||||||
pub struct EgressInfo {
|
pub struct EgressInfo {
|
||||||
pub mut:
|
pub mut:
|
||||||
egress_id string
|
egress_id string
|
||||||
room_id string
|
room_id string
|
||||||
status string
|
status string
|
||||||
started_at i64
|
started_at i64
|
||||||
ended_at i64
|
ended_at i64
|
||||||
error string
|
error string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StartRoomCompositeEgressArgs {
|
pub struct StartRoomCompositeEgressArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
room_name string
|
room_name string
|
||||||
layout string
|
layout string
|
||||||
audio_only bool
|
audio_only bool
|
||||||
video_only bool
|
video_only bool
|
||||||
custom_base_url string
|
custom_base_url string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StartTrackCompositeEgressArgs {
|
pub struct StartTrackCompositeEgressArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
room_name string
|
room_name string
|
||||||
audio_track_id string
|
audio_track_id string
|
||||||
video_track_id string
|
video_track_id string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StartWebEgressArgs {
|
pub struct StartWebEgressArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
url string
|
url string
|
||||||
audio_only bool
|
audio_only bool
|
||||||
video_only bool
|
video_only bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UpdateStreamArgs {
|
pub struct UpdateStreamArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
add_output_urls []string
|
add_output_urls []string
|
||||||
remove_output_urls []string
|
remove_output_urls []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,30 +77,20 @@ pub mut:
|
|||||||
|
|
||||||
pub struct InputAudioState {
|
pub struct InputAudioState {
|
||||||
pub mut:
|
pub mut:
|
||||||
mime_type string
|
mime_type string
|
||||||
channels u32
|
channels u32
|
||||||
sample_rate u32
|
sample_rate u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CreateIngressArgs {
|
pub struct CreateIngressArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string
|
name string
|
||||||
room_name string
|
room_name string
|
||||||
participant_identity string
|
participant_identity string
|
||||||
participant_name string
|
participant_name string
|
||||||
input_type IngressInput
|
input_type IngressInput
|
||||||
audio IngressAudioOptions
|
audio IngressAudioOptions
|
||||||
video IngressVideoOptions
|
video IngressVideoOptions
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UpdateIngressArgs {
|
|
||||||
pub mut:
|
|
||||||
name string
|
|
||||||
room_name string
|
|
||||||
participant_identity string
|
|
||||||
participant_name string
|
|
||||||
audio IngressAudioOptions
|
|
||||||
video IngressVideoOptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c LivekitClient) create_ingress(args CreateIngressArgs) !IngressInfo {
|
pub fn (mut c LivekitClient) create_ingress(args CreateIngressArgs) !IngressInfo {
|
||||||
@@ -109,20 +99,43 @@ pub fn (mut c LivekitClient) create_ingress(args CreateIngressArgs) !IngressInfo
|
|||||||
return ingress_info
|
return ingress_info
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c LivekitClient) update_ingress(ingress_id string, args UpdateIngressArgs) !IngressInfo {
|
pub struct UpdateIngressArgs {
|
||||||
mut resp := c.post('twirp/livekit.Ingress/UpdateIngress', {'ingress_id': ingress_id, ...args})!
|
pub mut:
|
||||||
|
ingress_id string
|
||||||
|
name string
|
||||||
|
room_name string
|
||||||
|
participant_identity string
|
||||||
|
participant_name string
|
||||||
|
audio IngressAudioOptions
|
||||||
|
video IngressVideoOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut c LivekitClient) update_ingress(args UpdateIngressArgs) !IngressInfo {
|
||||||
|
mut resp := c.post('twirp/livekit.Ingress/UpdateIngress', {
|
||||||
|
'ingress_id': args.ingress_id
|
||||||
|
'name': args.name
|
||||||
|
'room_name': args.room_name
|
||||||
|
'participant_identity': args.participant_identity
|
||||||
|
'participant_name': args.participant_name
|
||||||
|
'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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,4 @@ pub fn heroscript_dumps(obj LivekitClient) !string {
|
|||||||
pub fn heroscript_loads(heroscript string) !LivekitClient {
|
pub fn heroscript_loads(heroscript string) !LivekitClient {
|
||||||
mut obj := encoderhero.decode[LivekitClient](heroscript)!
|
mut obj := encoderhero.decode[LivekitClient](heroscript)!
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,23 +4,23 @@ import json
|
|||||||
|
|
||||||
pub struct ParticipantInfo {
|
pub struct ParticipantInfo {
|
||||||
pub mut:
|
pub mut:
|
||||||
sid string
|
sid string
|
||||||
identity string
|
identity string
|
||||||
state string
|
state string
|
||||||
metadata string
|
metadata string
|
||||||
joined_at i64
|
joined_at i64
|
||||||
name string
|
name string
|
||||||
version u32
|
version u32
|
||||||
permission string
|
permission string
|
||||||
region string
|
region string
|
||||||
publisher bool
|
publisher bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UpdateParticipantArgs {
|
pub struct UpdateParticipantArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
room_name string
|
room_name string
|
||||||
identity string
|
identity string
|
||||||
metadata string
|
metadata string
|
||||||
permission string
|
permission string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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) ! {
|
||||||
@@ -54,4 +62,4 @@ pub fn (mut c LivekitClient) update_participant(args UpdateParticipantArgs) ! {
|
|||||||
|
|
||||||
pub fn (mut c LivekitClient) mute_published_track(args MutePublishedTrackArgs) ! {
|
pub fn (mut c LivekitClient) mute_published_track(args MutePublishedTrackArgs) ! {
|
||||||
_ = c.post('twirp/livekit.RoomService/MutePublishedTrack', args)!
|
_ = c.post('twirp/livekit.RoomService/MutePublishedTrack', args)!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,19 +13,19 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
if plbook.exists_once(filter: 'livekit.init') {
|
if plbook.exists_once(filter: 'livekit.init') {
|
||||||
mut action := plbook.get(filter: 'livekit.init')!
|
mut action := plbook.get(filter: 'livekit.init')!
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
name := texttools.name_fix(p.get_default('name', 'default')!)
|
name := texttools.name_fix(p.get_default('name', 'default')!)
|
||||||
url := p.get('url')!
|
url := p.get('url')!
|
||||||
api_key := p.get('api_key')!
|
api_key := p.get('api_key')!
|
||||||
api_secret := p.get('api_secret')!
|
api_secret := p.get('api_secret')!
|
||||||
|
|
||||||
mut client := LivekitClient{
|
mut client := LivekitClient{
|
||||||
name: name
|
name: name
|
||||||
url: url
|
url: url
|
||||||
api_key: api_key
|
api_key: api_key
|
||||||
api_secret: api_secret
|
api_secret: api_secret
|
||||||
}
|
}
|
||||||
|
|
||||||
set(client)!
|
set(client)!
|
||||||
console.print_header('LiveKit client "${name}" configured')
|
console.print_header('LiveKit client "${name}" configured')
|
||||||
action.done = true
|
action.done = true
|
||||||
@@ -35,22 +35,22 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut room_create_actions := plbook.find(filter: 'livekit.room_create')!
|
mut room_create_actions := plbook.find(filter: 'livekit.room_create')!
|
||||||
for mut action in room_create_actions {
|
for mut action in room_create_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||||
room_name := p.get('name')!
|
room_name := p.get('name')!
|
||||||
empty_timeout := p.get_u32_default('empty_timeout', 300)!
|
empty_timeout := p.get_u32_default('empty_timeout', 300)!
|
||||||
max_participants := p.get_u32_default('max_participants', 50)!
|
max_participants := p.get_u32_default('max_participants', 50)!
|
||||||
metadata := p.get_default('metadata', '')!
|
metadata := p.get_default('metadata', '')!
|
||||||
|
|
||||||
mut client := get(name: client_name)!
|
mut client := get(name: client_name)!
|
||||||
|
|
||||||
room := client.create_room(
|
room := client.create_room(
|
||||||
name: room_name
|
name: room_name
|
||||||
empty_timeout: empty_timeout
|
empty_timeout: empty_timeout
|
||||||
max_participants: max_participants
|
max_participants: max_participants
|
||||||
metadata: metadata
|
metadata: metadata
|
||||||
)!
|
)!
|
||||||
|
|
||||||
console.print_header('Room "${room_name}" created successfully')
|
console.print_header('Room "${room_name}" created successfully')
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -59,13 +59,13 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut room_delete_actions := plbook.find(filter: 'livekit.room_delete')!
|
mut room_delete_actions := plbook.find(filter: 'livekit.room_delete')!
|
||||||
for mut action in room_delete_actions {
|
for mut action in room_delete_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||||
room_name := p.get('name')!
|
room_name := p.get('name')!
|
||||||
|
|
||||||
mut client := get(name: client_name)!
|
mut client := get(name: client_name)!
|
||||||
client.delete_room(room_name)!
|
client.delete_room(room_name)!
|
||||||
|
|
||||||
console.print_header('Room "${room_name}" deleted successfully')
|
console.print_header('Room "${room_name}" deleted successfully')
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -74,14 +74,14 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut participant_remove_actions := plbook.find(filter: 'livekit.participant_remove')!
|
mut participant_remove_actions := plbook.find(filter: 'livekit.participant_remove')!
|
||||||
for mut action in participant_remove_actions {
|
for mut action in participant_remove_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||||
room_name := p.get('room')!
|
room_name := p.get('room')!
|
||||||
identity := p.get('identity')!
|
identity := p.get('identity')!
|
||||||
|
|
||||||
mut client := get(name: client_name)!
|
mut client := get(name: client_name)!
|
||||||
client.remove_participant(room_name, identity)!
|
client.remove_participant(room_name, identity)!
|
||||||
|
|
||||||
console.print_header('Participant "${identity}" removed from room "${room_name}"')
|
console.print_header('Participant "${identity}" removed from room "${room_name}"')
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -90,21 +90,21 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut participant_mute_actions := plbook.find(filter: 'livekit.participant_mute')!
|
mut participant_mute_actions := plbook.find(filter: 'livekit.participant_mute')!
|
||||||
for mut action in participant_mute_actions {
|
for mut action in participant_mute_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||||
room_name := p.get('room')!
|
room_name := p.get('room')!
|
||||||
identity := p.get('identity')!
|
identity := p.get('identity')!
|
||||||
track_sid := p.get('track_sid')!
|
track_sid := p.get('track_sid')!
|
||||||
muted := p.get_default_true('muted')
|
muted := p.get_default_true('muted')
|
||||||
|
|
||||||
mut client := get(name: client_name)!
|
mut client := get(name: client_name)!
|
||||||
client.mute_published_track(
|
client.mute_published_track(
|
||||||
room_name: room_name
|
room_name: room_name
|
||||||
identity: identity
|
identity: identity
|
||||||
track_sid: track_sid
|
track_sid: track_sid
|
||||||
muted: muted
|
muted: muted
|
||||||
)!
|
)!
|
||||||
|
|
||||||
status := if muted { 'muted' } else { 'unmuted' }
|
status := if muted { 'muted' } else { 'unmuted' }
|
||||||
console.print_header('Track "${track_sid}" ${status} for participant "${identity}"')
|
console.print_header('Track "${track_sid}" ${status} for participant "${identity}"')
|
||||||
action.done = true
|
action.done = true
|
||||||
@@ -114,17 +114,17 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut room_update_actions := plbook.find(filter: 'livekit.room_update')!
|
mut room_update_actions := plbook.find(filter: 'livekit.room_update')!
|
||||||
for mut action in room_update_actions {
|
for mut action in room_update_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||||
room_name := p.get('room')!
|
room_name := p.get('room')!
|
||||||
metadata := p.get('metadata')!
|
metadata := p.get('metadata')!
|
||||||
|
|
||||||
mut client := get(name: client_name)!
|
mut client := get(name: client_name)!
|
||||||
client.update_room_metadata(
|
client.update_room_metadata(
|
||||||
room_name: room_name
|
room_name: room_name
|
||||||
metadata: metadata
|
metadata: metadata
|
||||||
)!
|
)!
|
||||||
|
|
||||||
console.print_header('Room "${room_name}" metadata updated')
|
console.print_header('Room "${room_name}" metadata updated')
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut token_create_actions := plbook.find(filter: 'livekit.token_create')!
|
mut token_create_actions := plbook.find(filter: 'livekit.token_create')!
|
||||||
for mut action in token_create_actions {
|
for mut action in token_create_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||||
identity := p.get('identity')!
|
identity := p.get('identity')!
|
||||||
name := p.get_default('name', identity)!
|
name := p.get_default('name', identity)!
|
||||||
@@ -142,26 +142,26 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
can_publish := p.get_default_false('can_publish')
|
can_publish := p.get_default_false('can_publish')
|
||||||
can_subscribe := p.get_default_true('can_subscribe')
|
can_subscribe := p.get_default_true('can_subscribe')
|
||||||
can_publish_data := p.get_default_false('can_publish_data')
|
can_publish_data := p.get_default_false('can_publish_data')
|
||||||
|
|
||||||
mut client := get(name: client_name)!
|
mut client := get(name: client_name)!
|
||||||
|
|
||||||
mut token := client.new_access_token(
|
mut token := client.new_access_token(
|
||||||
identity: identity
|
identity: identity
|
||||||
name: name
|
name: name
|
||||||
ttl: ttl
|
ttl: ttl
|
||||||
)!
|
)!
|
||||||
|
|
||||||
token.add_video_grant(VideoGrant{
|
token.add_video_grant(VideoGrant{
|
||||||
room: room
|
room: room
|
||||||
room_join: true
|
room_join: true
|
||||||
can_publish: can_publish
|
can_publish: can_publish
|
||||||
can_subscribe: can_subscribe
|
can_subscribe: can_subscribe
|
||||||
can_publish_data: can_publish_data
|
can_publish_data: can_publish_data
|
||||||
})
|
})
|
||||||
|
|
||||||
jwt := token.to_jwt()!
|
jwt := token.to_jwt()!
|
||||||
console.print_header('Access token generated for "${identity}"')
|
console.print_header('Access token generated for "${identity}"')
|
||||||
console.print_debug('Token: ${jwt}')
|
console.print_debug('Token: ${jwt}')
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,25 +5,25 @@ import net.http
|
|||||||
|
|
||||||
pub struct Room {
|
pub struct Room {
|
||||||
pub mut:
|
pub mut:
|
||||||
sid string
|
sid string
|
||||||
name string
|
name string
|
||||||
empty_timeout u32
|
empty_timeout u32
|
||||||
max_participants u32
|
max_participants u32
|
||||||
creation_time i64
|
creation_time i64
|
||||||
turn_password string
|
turn_password string
|
||||||
enabled_codecs []string
|
enabled_codecs []string
|
||||||
metadata string
|
metadata string
|
||||||
num_participants u32
|
num_participants u32
|
||||||
num_connected_participants u32
|
num_connected_participants u32
|
||||||
active_recording bool
|
active_recording bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CreateRoomArgs {
|
pub struct CreateRoomArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string
|
name string
|
||||||
empty_timeout u32
|
empty_timeout u32
|
||||||
max_participants u32
|
max_participants u32
|
||||||
metadata string
|
metadata string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UpdateRoomMetadataArgs {
|
pub struct UpdateRoomMetadataArgs {
|
||||||
@@ -39,9 +39,11 @@ 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) ! {
|
||||||
_ = c.post('twirp/livekit.RoomService/UpdateRoomMetadata', args)!
|
_ = c.post('twirp/livekit.RoomService/UpdateRoomMetadata', args)!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -5,33 +5,33 @@ import time
|
|||||||
|
|
||||||
pub struct AccessToken {
|
pub struct AccessToken {
|
||||||
pub mut:
|
pub mut:
|
||||||
api_key string
|
api_key string
|
||||||
api_secret string
|
api_secret string
|
||||||
identity string
|
identity string
|
||||||
name string
|
name string
|
||||||
ttl int
|
ttl int
|
||||||
video_grant VideoGrant
|
video_grant VideoGrant
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VideoGrant {
|
pub struct VideoGrant {
|
||||||
pub mut:
|
pub mut:
|
||||||
room_create bool
|
room_create bool
|
||||||
room_admin bool
|
room_admin bool
|
||||||
room_join bool
|
room_join bool
|
||||||
room_list bool
|
room_list bool
|
||||||
can_publish bool
|
can_publish bool
|
||||||
can_subscribe bool
|
can_subscribe bool
|
||||||
can_publish_data bool
|
can_publish_data bool
|
||||||
room string
|
room string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c LivekitClient) new_access_token(identity string, name string, ttl int) !AccessToken {
|
pub fn (mut c LivekitClient) new_access_token(identity string, name string, ttl int) !AccessToken {
|
||||||
return AccessToken{
|
return AccessToken{
|
||||||
api_key: c.api_key
|
api_key: c.api_key
|
||||||
api_secret: c.api_secret
|
api_secret: c.api_secret
|
||||||
identity: identity
|
identity: identity
|
||||||
name: name
|
name: name
|
||||||
ttl: ttl
|
ttl: ttl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,4 +49,4 @@ pub fn (t AccessToken) to_jwt() !string {
|
|||||||
claims.name = t.name
|
claims.name = t.name
|
||||||
claims.video = t.video_grant
|
claims.video = t.video_grant
|
||||||
return jwt.encode(claims, t.api_secret, .hs256)
|
return jwt.encode(claims, t.api_secret, .hs256)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ pub const default_url = 'http://localhost:8990'
|
|||||||
@[heap]
|
@[heap]
|
||||||
pub struct MyceliumRPC {
|
pub struct MyceliumRPC {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string = 'default'
|
name string = 'default'
|
||||||
url string = default_url // RPC server URL
|
url string = default_url // RPC server URL
|
||||||
// rpc_client ?&jsonrpc.Client @[skip]
|
// rpc_client ?&jsonrpc.Client @[skip]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ pub fn new(args FactoryArgs) !&TraefikManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mut redis := redisclient.core_get(redisclient.get_redis_url(args.redis_url)!)!
|
mut redis := redisclient.core_get(redisclient.get_redis_url(args.redis_url)!)!
|
||||||
|
|
||||||
mut manager := &TraefikManager{
|
mut manager := &TraefikManager{
|
||||||
name: name
|
name: name
|
||||||
redis: redis
|
redis: redis
|
||||||
config: osal_traefik.new_traefik_config()
|
config: osal_traefik.new_traefik_config()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set redis connection in config
|
// Set redis connection in config
|
||||||
manager.config.redis = redis
|
manager.config.redis = redis
|
||||||
|
|
||||||
traefik_managers[name] = manager
|
traefik_managers[name] = manager
|
||||||
return manager
|
return manager
|
||||||
}
|
}
|
||||||
@@ -48,4 +48,4 @@ pub fn default() !&TraefikManager {
|
|||||||
return new(name: 'default')!
|
return new(name: 'default')!
|
||||||
}
|
}
|
||||||
return get(name: 'default')!
|
return get(name: 'default')!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ pub mut:
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct RouterAddArgs {
|
pub struct RouterAddArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string @[required]
|
name string @[required]
|
||||||
rule string @[required]
|
rule string @[required]
|
||||||
service string @[required]
|
service string @[required]
|
||||||
entrypoints []string
|
entrypoints []string
|
||||||
middlewares []string
|
middlewares []string
|
||||||
tls bool
|
tls bool
|
||||||
@@ -43,8 +43,8 @@ pub mut:
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct MiddlewareAddArgs {
|
pub struct MiddlewareAddArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string @[required]
|
name string @[required]
|
||||||
typ string @[required]
|
typ string @[required]
|
||||||
settings map[string]string
|
settings map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ pub fn (mut tm TraefikManager) service_add(args ServiceAddArgs) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tm.config.add_service(
|
tm.config.add_service(
|
||||||
name: texttools.name_fix(args.name)
|
name: texttools.name_fix(args.name)
|
||||||
load_balancer: osal_traefik.LoadBalancerConfig{
|
load_balancer: osal_traefik.LoadBalancerConfig{
|
||||||
servers: servers
|
servers: servers
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,7 @@ pub fn (mut tm TraefikManager) entrypoint_add(args EntryPointAddArgs) ! {
|
|||||||
address: args.address
|
address: args.address
|
||||||
tls: args.tls
|
tls: args.tls
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if entrypoint already exists
|
// Check if entrypoint already exists
|
||||||
for mut ep in tm.entrypoints {
|
for mut ep in tm.entrypoints {
|
||||||
if ep.name == entrypoint.name {
|
if ep.name == entrypoint.name {
|
||||||
@@ -110,7 +110,7 @@ pub fn (mut tm TraefikManager) entrypoint_add(args EntryPointAddArgs) ! {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tm.entrypoints << entrypoint
|
tm.entrypoints << entrypoint
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ pub fn (mut tm TraefikManager) entrypoint_add(args EntryPointAddArgs) ! {
|
|||||||
pub fn (mut tm TraefikManager) apply() ! {
|
pub fn (mut tm TraefikManager) apply() ! {
|
||||||
// Apply dynamic configuration (routers, services, middlewares)
|
// Apply dynamic configuration (routers, services, middlewares)
|
||||||
tm.config.set()!
|
tm.config.set()!
|
||||||
|
|
||||||
// Store entrypoints separately (these would typically be in static config)
|
// Store entrypoints separately (these would typically be in static config)
|
||||||
for ep in tm.entrypoints {
|
for ep in tm.entrypoints {
|
||||||
tm.redis.hset('traefik:entrypoints', ep.name, '${ep.address}|${ep.tls}')!
|
tm.redis.hset('traefik:entrypoints', ep.name, '${ep.address}|${ep.tls}')!
|
||||||
@@ -135,7 +135,7 @@ pub fn (mut tm TraefikManager) clear() ! {
|
|||||||
tm.config = osal_traefik.new_traefik_config()
|
tm.config = osal_traefik.new_traefik_config()
|
||||||
tm.config.redis = tm.redis
|
tm.config.redis = tm.redis
|
||||||
tm.entrypoints = []EntryPointConfig{}
|
tm.entrypoints = []EntryPointConfig{}
|
||||||
|
|
||||||
// Clear Redis keys
|
// Clear Redis keys
|
||||||
keys := tm.redis.keys('traefik/*')!
|
keys := tm.redis.keys('traefik/*')!
|
||||||
for key in keys {
|
for key in keys {
|
||||||
@@ -151,4 +151,4 @@ pub fn (mut tm TraefikManager) status() !map[string]int {
|
|||||||
'middlewares': tm.config.middlewares.len
|
'middlewares': tm.config.middlewares.len
|
||||||
'entrypoints': tm.entrypoints.len
|
'entrypoints': tm.entrypoints.len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,44 +14,44 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
|
|
||||||
// Process entrypoints first
|
// Process entrypoints first
|
||||||
play_entrypoints(mut plbook, mut manager)!
|
play_entrypoints(mut plbook, mut manager)!
|
||||||
|
|
||||||
// Process services (before routers that might reference them)
|
// Process services (before routers that might reference them)
|
||||||
play_services(mut plbook, mut manager)!
|
play_services(mut plbook, mut manager)!
|
||||||
|
|
||||||
// Process middlewares (before routers that might reference them)
|
// Process middlewares (before routers that might reference them)
|
||||||
play_middlewares(mut plbook, mut manager)!
|
play_middlewares(mut plbook, mut manager)!
|
||||||
|
|
||||||
// Process routers
|
// Process routers
|
||||||
play_routers(mut plbook, mut manager)!
|
play_routers(mut plbook, mut manager)!
|
||||||
|
|
||||||
// Apply all configurations to Redis
|
// Apply all configurations to Redis
|
||||||
manager.apply()!
|
manager.apply()!
|
||||||
|
|
||||||
console.print_debug('Traefik configuration applied successfully')
|
console.print_debug('Traefik configuration applied successfully')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_entrypoints(mut plbook PlayBook, mut manager TraefikManager) ! {
|
fn play_entrypoints(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||||
entrypoint_actions := plbook.find(filter: 'traefik.entrypoint')!
|
entrypoint_actions := plbook.find(filter: 'traefik.entrypoint')!
|
||||||
|
|
||||||
for mut action in entrypoint_actions {
|
for mut action in entrypoint_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
manager.entrypoint_add(
|
manager.entrypoint_add(
|
||||||
name: p.get('name')!
|
name: p.get('name')!
|
||||||
address: p.get('address')!
|
address: p.get('address')!
|
||||||
tls: p.get_default_false('tls')
|
tls: p.get_default_false('tls')
|
||||||
)!
|
)!
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_routers(mut plbook PlayBook, mut manager TraefikManager) ! {
|
fn play_routers(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||||
router_actions := plbook.find(filter: 'traefik.router')!
|
router_actions := plbook.find(filter: 'traefik.router')!
|
||||||
|
|
||||||
for mut action in router_actions {
|
for mut action in router_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
// Parse entrypoints list
|
// Parse entrypoints list
|
||||||
mut entrypoints := []string{}
|
mut entrypoints := []string{}
|
||||||
if entrypoints_str := p.get_default('entrypoints', '') {
|
if entrypoints_str := p.get_default('entrypoints', '') {
|
||||||
@@ -59,7 +59,7 @@ fn play_routers(mut plbook PlayBook, mut manager TraefikManager) ! {
|
|||||||
entrypoints = entrypoints_str.split(',').map(it.trim_space())
|
entrypoints = entrypoints_str.split(',').map(it.trim_space())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse middlewares list
|
// Parse middlewares list
|
||||||
mut middlewares := []string{}
|
mut middlewares := []string{}
|
||||||
if middlewares_str := p.get_default('middlewares', '') {
|
if middlewares_str := p.get_default('middlewares', '') {
|
||||||
@@ -67,7 +67,7 @@ fn play_routers(mut plbook PlayBook, mut manager TraefikManager) ! {
|
|||||||
middlewares = middlewares_str.split(',').map(it.trim_space())
|
middlewares = middlewares_str.split(',').map(it.trim_space())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.router_add(
|
manager.router_add(
|
||||||
name: p.get('name')!
|
name: p.get('name')!
|
||||||
rule: p.get('rule')!
|
rule: p.get('rule')!
|
||||||
@@ -77,42 +77,42 @@ fn play_routers(mut plbook PlayBook, mut manager TraefikManager) ! {
|
|||||||
tls: p.get_default_false('tls')
|
tls: p.get_default_false('tls')
|
||||||
priority: p.get_int_default('priority', 0)
|
priority: p.get_int_default('priority', 0)
|
||||||
)!
|
)!
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_services(mut plbook PlayBook, mut manager TraefikManager) ! {
|
fn play_services(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||||
service_actions := plbook.find(filter: 'traefik.service')!
|
service_actions := plbook.find(filter: 'traefik.service')!
|
||||||
|
|
||||||
for mut action in service_actions {
|
for mut action in service_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
// Parse servers list
|
// Parse servers list
|
||||||
servers_str := p.get('servers')!
|
servers_str := p.get('servers')!
|
||||||
servers := servers_str.split(',').map(it.trim_space())
|
servers := servers_str.split(',').map(it.trim_space())
|
||||||
|
|
||||||
manager.service_add(
|
manager.service_add(
|
||||||
name: p.get('name')!
|
name: p.get('name')!
|
||||||
servers: servers
|
servers: servers
|
||||||
strategy: p.get_default('strategy', 'wrr')!
|
strategy: p.get_default('strategy', 'wrr')!
|
||||||
)!
|
)!
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_middlewares(mut plbook PlayBook, mut manager TraefikManager) ! {
|
fn play_middlewares(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||||
middleware_actions := plbook.find(filter: 'traefik.middleware')!
|
middleware_actions := plbook.find(filter: 'traefik.middleware')!
|
||||||
|
|
||||||
for mut action in middleware_actions {
|
for mut action in middleware_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
// Build settings map from remaining parameters
|
// Build settings map from remaining parameters
|
||||||
mut settings := map[string]string{}
|
mut settings := map[string]string{}
|
||||||
|
|
||||||
middleware_type := p.get('type')!
|
middleware_type := p.get('type')!
|
||||||
|
|
||||||
// Handle common middleware types
|
// Handle common middleware types
|
||||||
match middleware_type {
|
match middleware_type {
|
||||||
'basicAuth' {
|
'basicAuth' {
|
||||||
@@ -156,13 +156,13 @@ fn play_middlewares(mut plbook PlayBook, mut manager TraefikManager) ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.middleware_add(
|
manager.middleware_add(
|
||||||
name: p.get('name')!
|
name: p.get('name')!
|
||||||
typ: middleware_type
|
typ: middleware_type
|
||||||
settings: settings
|
settings: settings
|
||||||
)!
|
)!
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ pub fn scan(args_ GeneratorArgs) ! {
|
|||||||
// now walk over all directories, find .heroscript
|
// now walk over all directories, find .heroscript
|
||||||
mut pathroot := pathlib.get_dir(path: args.path, create: false)!
|
mut pathroot := pathlib.get_dir(path: args.path, create: false)!
|
||||||
mut plist := pathroot.list(
|
mut plist := pathroot.list(
|
||||||
recursive: true
|
recursive: true
|
||||||
ignore_default: false
|
ignore_default: false
|
||||||
regex: ['.heroscript']
|
regex: ['.heroscript']
|
||||||
)!
|
)!
|
||||||
|
|
||||||
for mut p in plist.paths {
|
for mut p in plist.paths {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import freeflowuniverse.herolib.ui.console
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct ListArgs {
|
pub struct ListArgs {
|
||||||
pub mut:
|
pub mut:
|
||||||
regex []string
|
regex []string
|
||||||
recursive bool = true
|
recursive bool = true
|
||||||
ignore_default bool = true // ignore files starting with . and _
|
ignore_default bool = true // ignore files starting with . and _
|
||||||
include_links bool // wether to include links in list
|
include_links bool // wether to include links in list
|
||||||
dirs_only bool
|
dirs_only bool
|
||||||
files_only bool
|
files_only bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// the result of pathlist
|
// the result of pathlist
|
||||||
@@ -54,12 +54,12 @@ pub fn (mut path Path) list(args_ ListArgs) !PathList {
|
|||||||
r << re
|
r << re
|
||||||
}
|
}
|
||||||
mut args := ListArgsInternal{
|
mut args := ListArgsInternal{
|
||||||
regex: r
|
regex: r
|
||||||
recursive: args_.recursive
|
recursive: args_.recursive
|
||||||
ignore_default: args_.ignore_default
|
ignore_default: args_.ignore_default
|
||||||
dirs_only: args_.dirs_only
|
dirs_only: args_.dirs_only
|
||||||
files_only: args_.files_only
|
files_only: args_.files_only
|
||||||
include_links: args_.include_links
|
include_links: args_.include_links
|
||||||
}
|
}
|
||||||
paths := path.list_internal(args)!
|
paths := path.list_internal(args)!
|
||||||
mut pl := PathList{
|
mut pl := PathList{
|
||||||
@@ -72,12 +72,12 @@ pub fn (mut path Path) list(args_ ListArgs) !PathList {
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct ListArgsInternal {
|
pub struct ListArgsInternal {
|
||||||
mut:
|
mut:
|
||||||
regex []regex.RE // only put files in which follow one of the regexes
|
regex []regex.RE // only put files in which follow one of the regexes
|
||||||
recursive bool = true
|
recursive bool = true
|
||||||
ignore_default bool = true // ignore files starting with . and _
|
ignore_default bool = true // ignore files starting with . and _
|
||||||
dirs_only bool
|
dirs_only bool
|
||||||
files_only bool
|
files_only bool
|
||||||
include_links bool
|
include_links bool
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut path Path) list_internal(args ListArgsInternal) ![]Path {
|
fn (mut path Path) list_internal(args ListArgsInternal) ![]Path {
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -24,6 +24,6 @@ pub:
|
|||||||
pub fn new(args LinuxNewArgs) !LinuxFactory {
|
pub fn new(args LinuxNewArgs) !LinuxFactory {
|
||||||
mut t := LinuxFactory{
|
mut t := LinuxFactory{
|
||||||
username: args.username
|
username: args.username
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,34 +11,34 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
|
|
||||||
// Process user_create actions
|
// Process user_create actions
|
||||||
play_user_create(mut plbook, mut lf)!
|
play_user_create(mut plbook, mut lf)!
|
||||||
|
|
||||||
// Process user_delete actions
|
// Process user_delete actions
|
||||||
play_user_delete(mut plbook, mut lf)!
|
play_user_delete(mut plbook, mut lf)!
|
||||||
|
|
||||||
// Process sshkey_create actions
|
// Process sshkey_create actions
|
||||||
play_sshkey_create(mut plbook, mut lf)!
|
play_sshkey_create(mut plbook, mut lf)!
|
||||||
|
|
||||||
// Process sshkey_delete actions
|
// Process sshkey_delete actions
|
||||||
play_sshkey_delete(mut plbook, mut lf)!
|
play_sshkey_delete(mut plbook, mut lf)!
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_user_create(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
fn play_user_create(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
||||||
mut actions := plbook.find(filter: 'usermgmt.user_create')!
|
mut actions := plbook.find(filter: 'usermgmt.user_create')!
|
||||||
|
|
||||||
for mut action in actions {
|
for mut action in actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
mut args := UserCreateArgs{
|
mut args := UserCreateArgs{
|
||||||
name: p.get('name')!
|
name: p.get('name')!
|
||||||
giteakey: p.get_default('giteakey', '')!
|
giteakey: p.get_default('giteakey', '')!
|
||||||
giteaurl: p.get_default('giteaurl', '')!
|
giteaurl: p.get_default('giteaurl', '')!
|
||||||
passwd: p.get_default('passwd', '')!
|
passwd: p.get_default('passwd', '')!
|
||||||
description: p.get_default('description', '')!
|
description: p.get_default('description', '')!
|
||||||
email: p.get_default('email', '')!
|
email: p.get_default('email', '')!
|
||||||
tel: p.get_default('tel', '')!
|
tel: p.get_default('tel', '')!
|
||||||
sshkey: p.get_default('sshkey', '')! // SSH public key
|
sshkey: p.get_default('sshkey', '')! // SSH public key
|
||||||
}
|
}
|
||||||
|
|
||||||
lf.user_create(args)!
|
lf.user_create(args)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -46,14 +46,14 @@ fn play_user_create(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
|||||||
|
|
||||||
fn play_user_delete(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
fn play_user_delete(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
||||||
mut actions := plbook.find(filter: 'usermgmt.user_delete')!
|
mut actions := plbook.find(filter: 'usermgmt.user_delete')!
|
||||||
|
|
||||||
for mut action in actions {
|
for mut action in actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
mut args := UserDeleteArgs{
|
mut args := UserDeleteArgs{
|
||||||
name: p.get('name')!
|
name: p.get('name')!
|
||||||
}
|
}
|
||||||
|
|
||||||
lf.user_delete(args)!
|
lf.user_delete(args)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -61,17 +61,17 @@ fn play_user_delete(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
|||||||
|
|
||||||
fn play_sshkey_create(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
fn play_sshkey_create(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
||||||
mut actions := plbook.find(filter: 'usermgmt.sshkey_create')!
|
mut actions := plbook.find(filter: 'usermgmt.sshkey_create')!
|
||||||
|
|
||||||
for mut action in actions {
|
for mut action in actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
mut args := SSHKeyCreateArgs{
|
mut args := SSHKeyCreateArgs{
|
||||||
username: p.get('username')!
|
username: p.get('username')!
|
||||||
sshkey_name: p.get('sshkey_name')!
|
sshkey_name: p.get('sshkey_name')!
|
||||||
sshkey_pub: p.get_default('sshkey_pub', '')!
|
sshkey_pub: p.get_default('sshkey_pub', '')!
|
||||||
sshkey_priv: p.get_default('sshkey_priv', '')!
|
sshkey_priv: p.get_default('sshkey_priv', '')!
|
||||||
}
|
}
|
||||||
|
|
||||||
lf.sshkey_create(args)!
|
lf.sshkey_create(args)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -79,16 +79,16 @@ fn play_sshkey_create(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
|||||||
|
|
||||||
fn play_sshkey_delete(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
fn play_sshkey_delete(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
||||||
mut actions := plbook.find(filter: 'usermgmt.sshkey_delete')!
|
mut actions := plbook.find(filter: 'usermgmt.sshkey_delete')!
|
||||||
|
|
||||||
for mut action in actions {
|
for mut action in actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
|
|
||||||
mut args := SSHKeyDeleteArgs{
|
mut args := SSHKeyDeleteArgs{
|
||||||
username: p.get('username')!
|
username: p.get('username')!
|
||||||
sshkey_name: p.get('sshkey_name')!
|
sshkey_name: p.get('sshkey_name')!
|
||||||
}
|
}
|
||||||
|
|
||||||
lf.sshkey_delete(args)!
|
lf.sshkey_delete(args)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
lib/osal/linux/templates/profile_sshagent.sh
Normal file
12
lib/osal/linux/templates/profile_sshagent.sh
Normal 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
|
||||||
@@ -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"
|
||||||
@@ -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}')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,12 +177,12 @@ fn (mut lf LinuxFactory) save_user_config(args UserCreateArgs) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
new_config := UserConfig{
|
new_config := UserConfig{
|
||||||
name: args.name
|
name: args.name
|
||||||
giteakey: args.giteakey
|
giteakey: args.giteakey
|
||||||
giteaurl: args.giteaurl
|
giteaurl: args.giteaurl
|
||||||
email: args.email
|
email: args.email
|
||||||
description: args.description
|
description: args.description
|
||||||
tel: args.tel
|
tel: args.tel
|
||||||
}
|
}
|
||||||
|
|
||||||
if found_idx >= 0 {
|
if found_idx >= 0 {
|
||||||
@@ -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}')!
|
||||||
@@ -351,4 +306,4 @@ fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.print_green('✅ SSH agent profile created for ${username}')
|
console.print_green('✅ SSH agent profile created for ${username}')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,27 +3,27 @@ module sshagent
|
|||||||
// Check if SSH agent is properly configured and all is good
|
// Check if SSH agent is properly configured and all is good
|
||||||
fn agent_check(mut agent SSHAgent) ! {
|
fn agent_check(mut agent SSHAgent) ! {
|
||||||
console.print_header('SSH Agent Check')
|
console.print_header('SSH Agent Check')
|
||||||
|
|
||||||
// Ensure single agent is running
|
// Ensure single agent is running
|
||||||
agent.ensure_single_agent()!
|
agent.ensure_single_agent()!
|
||||||
|
|
||||||
// Get diagnostics
|
// Get diagnostics
|
||||||
diag := agent.diagnostics()
|
diag := agent.diagnostics()
|
||||||
|
|
||||||
for key, value in diag {
|
for key, value in diag {
|
||||||
console.print_item('${key}: ${value}')
|
console.print_item('${key}: ${value}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify agent is responsive
|
// Verify agent is responsive
|
||||||
if !agent.is_agent_responsive() {
|
if !agent.is_agent_responsive() {
|
||||||
return error('SSH agent is not responsive')
|
return error('SSH agent is not responsive')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all existing keys from ~/.ssh that aren't loaded yet
|
// Load all existing keys from ~/.ssh that aren't loaded yet
|
||||||
agent.init()!
|
agent.init()!
|
||||||
|
|
||||||
console.print_green('✓ SSH Agent is properly configured and running')
|
console.print_green('✓ SSH Agent is properly configured and running')
|
||||||
|
|
||||||
// Show loaded keys
|
// Show loaded keys
|
||||||
loaded_keys := agent.keys_loaded()!
|
loaded_keys := agent.keys_loaded()!
|
||||||
console.print_item('Loaded keys: ${loaded_keys.len}')
|
console.print_item('Loaded keys: ${loaded_keys.len}')
|
||||||
@@ -35,17 +35,17 @@ fn agent_check(mut agent SSHAgent) ! {
|
|||||||
// Create a new SSH key
|
// Create a new SSH key
|
||||||
fn sshkey_create(mut agent SSHAgent, name string, passphrase string) ! {
|
fn sshkey_create(mut agent SSHAgent, name string, passphrase string) ! {
|
||||||
console.print_header('Creating SSH key: ${name}')
|
console.print_header('Creating SSH key: ${name}')
|
||||||
|
|
||||||
// Check if key already exists
|
// Check if key already exists
|
||||||
if agent.exists(name: name) {
|
if agent.exists(name: name) {
|
||||||
console.print_debug('SSH key "${name}" already exists')
|
console.print_debug('SSH key "${name}" already exists')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new key
|
// Generate new key
|
||||||
mut key := agent.generate(name, passphrase)!
|
mut key := agent.generate(name, passphrase)!
|
||||||
console.print_green('✓ SSH key "${name}" created successfully')
|
console.print_green('✓ SSH key "${name}" created successfully')
|
||||||
|
|
||||||
// Automatically load the key
|
// Automatically load the key
|
||||||
key.load()!
|
key.load()!
|
||||||
console.print_green('✓ SSH key "${name}" loaded into agent')
|
console.print_green('✓ SSH key "${name}" loaded into agent')
|
||||||
@@ -54,13 +54,13 @@ fn sshkey_create(mut agent SSHAgent, name string, passphrase string) ! {
|
|||||||
// Delete an SSH key
|
// Delete an SSH key
|
||||||
fn sshkey_delete(mut agent SSHAgent, name string) ! {
|
fn sshkey_delete(mut agent SSHAgent, name string) ! {
|
||||||
console.print_header('Deleting SSH key: ${name}')
|
console.print_header('Deleting SSH key: ${name}')
|
||||||
|
|
||||||
// Check if key exists
|
// Check if key exists
|
||||||
mut key := agent.get(name: name) or {
|
mut key := agent.get(name: name) or {
|
||||||
console.print_debug('SSH key "${name}" does not exist')
|
console.print_debug('SSH key "${name}" does not exist')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get key paths before deletion
|
// Get key paths before deletion
|
||||||
key_path := key.keypath() or {
|
key_path := key.keypath() or {
|
||||||
console.print_debug('Private key path not available for "${name}"')
|
console.print_debug('Private key path not available for "${name}"')
|
||||||
@@ -70,12 +70,12 @@ fn sshkey_delete(mut agent SSHAgent, name string) ! {
|
|||||||
console.print_debug('Public key path not available for "${name}"')
|
console.print_debug('Public key path not available for "${name}"')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove from agent if loaded (temporarily disabled due to reset_ssh panic)
|
// Remove from agent if loaded (temporarily disabled due to reset_ssh panic)
|
||||||
// if key.loaded {
|
// if key.loaded {
|
||||||
// key.forget()!
|
// key.forget()!
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Delete key files
|
// Delete key files
|
||||||
if key_path.exists() {
|
if key_path.exists() {
|
||||||
key_path.delete()!
|
key_path.delete()!
|
||||||
@@ -85,26 +85,24 @@ fn sshkey_delete(mut agent SSHAgent, name string) ! {
|
|||||||
key_pub_path.delete()!
|
key_pub_path.delete()!
|
||||||
console.print_debug('Deleted public key: ${key_pub_path.path}')
|
console.print_debug('Deleted public key: ${key_pub_path.path}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reinitialize agent to update key list
|
// Reinitialize agent to update key list
|
||||||
agent.init()!
|
agent.init()!
|
||||||
|
|
||||||
console.print_green('✓ SSH key "${name}" deleted successfully')
|
console.print_green('✓ SSH key "${name}" deleted successfully')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load SSH key into agent
|
// Load SSH key into agent
|
||||||
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')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
key.load()!
|
key.load()!
|
||||||
console.print_green('✓ SSH key "${name}" loaded into agent')
|
console.print_green('✓ SSH key "${name}" loaded into agent')
|
||||||
}
|
}
|
||||||
@@ -112,28 +110,22 @@ fn sshkey_load(mut agent SSHAgent, name string) ! {
|
|||||||
// Check if SSH key is valid
|
// Check if SSH key is valid
|
||||||
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 { return error('Public key file not found for "${name}"') }
|
||||||
|
|
||||||
key_pub_path := key.keypath_pub() or {
|
|
||||||
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}')
|
||||||
}
|
}
|
||||||
|
|
||||||
if !key_pub_path.exists() {
|
if !key_pub_path.exists() {
|
||||||
return error('Public key file does not exist: ${key_pub_path.path}')
|
return error('Public key file does not exist: ${key_pub_path.path}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify key can be loaded (if not already loaded)
|
// Verify key can be loaded (if not already loaded)
|
||||||
if !key.loaded {
|
if !key.loaded {
|
||||||
// Test load without actually loading (since forget is disabled)
|
// Test load without actually loading (since forget is disabled)
|
||||||
@@ -142,70 +134,68 @@ fn sshkey_check(mut agent SSHAgent, name string) ! {
|
|||||||
return error('Invalid private key format in "${name}"')
|
return error('Invalid private key format in "${name}"')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.print_item('Key type: ${key.cat}')
|
console.print_item('Key type: ${key.cat}')
|
||||||
console.print_item('Loaded: ${key.loaded}')
|
console.print_item('Loaded: ${key.loaded}')
|
||||||
console.print_item('Email: ${key.email}')
|
console.print_item('Email: ${key.email}')
|
||||||
console.print_item('Private key: ${key_path.path}')
|
console.print_item('Private key: ${key_path.path}')
|
||||||
console.print_item('Public key: ${key_pub_path.path}')
|
console.print_item('Public key: ${key_pub_path.path}')
|
||||||
|
|
||||||
console.print_green('✓ SSH key "${name}" is valid')
|
console.print_green('✓ SSH key "${name}" is valid')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy private key to remote node
|
// Copy private key to remote node
|
||||||
fn remote_copy(mut agent SSHAgent, node_addr string, key_name string) ! {
|
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()!
|
||||||
mut node := b.node_new(ipaddr: node_addr)!
|
mut node := b.node_new(ipaddr: node_addr)!
|
||||||
|
|
||||||
// Get private key content
|
// Get private key content
|
||||||
key_path := key.keypath()!
|
key_path := key.keypath()!
|
||||||
if !key_path.exists() {
|
if !key_path.exists() {
|
||||||
return error('Private key file not found: ${key_path.path}')
|
return error('Private key file not found: ${key_path.path}')
|
||||||
}
|
}
|
||||||
|
|
||||||
private_key_content := key_path.read()!
|
private_key_content := key_path.read()!
|
||||||
|
|
||||||
// Get home directory on remote
|
// Get home directory on remote
|
||||||
home_dir := node.environ_get()!['HOME'] or {
|
home_dir := node.environ_get()!['HOME'] or {
|
||||||
return error('Could not determine HOME directory on remote node')
|
return error('Could not determine HOME directory on remote node')
|
||||||
}
|
}
|
||||||
|
|
||||||
remote_ssh_dir := '${home_dir}/.ssh'
|
remote_ssh_dir := '${home_dir}/.ssh'
|
||||||
remote_key_path := '${remote_ssh_dir}/${key_name}'
|
remote_key_path := '${remote_ssh_dir}/${key_name}'
|
||||||
|
|
||||||
// Ensure .ssh directory exists with correct permissions
|
// Ensure .ssh directory exists with correct permissions
|
||||||
node.exec_silent('mkdir -p ${remote_ssh_dir}')!
|
node.exec_silent('mkdir -p ${remote_ssh_dir}')!
|
||||||
node.exec_silent('chmod 700 ${remote_ssh_dir}')!
|
node.exec_silent('chmod 700 ${remote_ssh_dir}')!
|
||||||
|
|
||||||
// Copy private key to remote
|
// Copy private key to remote
|
||||||
node.file_write(remote_key_path, private_key_content)!
|
node.file_write(remote_key_path, private_key_content)!
|
||||||
node.exec_silent('chmod 600 ${remote_key_path}')!
|
node.exec_silent('chmod 600 ${remote_key_path}')!
|
||||||
|
|
||||||
// Generate public key on remote
|
// Generate public key on remote
|
||||||
node.exec_silent('ssh-keygen -y -f ${remote_key_path} > ${remote_key_path}.pub')!
|
node.exec_silent('ssh-keygen -y -f ${remote_key_path} > ${remote_key_path}.pub')!
|
||||||
node.exec_silent('chmod 644 ${remote_key_path}.pub')!
|
node.exec_silent('chmod 644 ${remote_key_path}.pub')!
|
||||||
|
|
||||||
console.print_green('✓ SSH key "${key_name}" copied to ${node_addr}')
|
console.print_green('✓ SSH key "${key_name}" copied to ${node_addr}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add public key to authorized_keys on remote node
|
// Add public key to authorized_keys on remote node
|
||||||
fn remote_auth(mut agent SSHAgent, node_addr string, key_name string) ! {
|
fn remote_auth(mut agent SSHAgent, node_addr string, key_name string) ! {
|
||||||
console.print_header('Adding SSH key "${key_name}" to authorized_keys on ${node_addr}')
|
console.print_header('Adding SSH key "${key_name}" to authorized_keys on ${node_addr}')
|
||||||
|
|
||||||
// Create builder node
|
// Create builder node
|
||||||
mut b := builder.new()!
|
mut b := builder.new()!
|
||||||
mut node := b.node_new(ipaddr: node_addr)!
|
mut node := b.node_new(ipaddr: node_addr)!
|
||||||
|
|
||||||
// Use existing builder integration
|
// Use existing builder integration
|
||||||
agent.push_key_to_node(mut node, key_name)!
|
agent.push_key_to_node(mut node, key_name)!
|
||||||
|
|
||||||
console.print_green('✓ SSH key "${key_name}" added to authorized_keys on ${node_addr}')
|
console.print_green('✓ SSH key "${key_name}" added to authorized_keys on ${node_addr}')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,27 +10,27 @@ pub fn (mut agent SSHAgent) push_key_to_node(mut node builder.Node, key_name str
|
|||||||
if node_info['category'] != 'ssh' {
|
if node_info['category'] != 'ssh' {
|
||||||
return error('Can only push keys to SSH nodes, got: ${node_info['category']}')
|
return error('Can only push keys to SSH nodes, got: ${node_info['category']}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the key
|
// Find 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 in agent')
|
return error('SSH key "${key_name}" not found in agent')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get public key content
|
// Get public key content
|
||||||
pubkey_content := key.keypub()!
|
pubkey_content := key.keypub()!
|
||||||
|
|
||||||
// Check if authorized_keys file exists on remote
|
// Check if authorized_keys file exists on remote
|
||||||
home_dir := node.environ_get()!['HOME'] or {
|
home_dir := node.environ_get()!['HOME'] or {
|
||||||
return error('Could not determine HOME directory on remote node')
|
return error('Could not determine HOME directory on remote node')
|
||||||
}
|
}
|
||||||
|
|
||||||
ssh_dir := '${home_dir}/.ssh'
|
ssh_dir := '${home_dir}/.ssh'
|
||||||
authorized_keys_path := '${ssh_dir}/authorized_keys'
|
authorized_keys_path := '${ssh_dir}/authorized_keys'
|
||||||
|
|
||||||
// Ensure .ssh directory exists with correct permissions
|
// Ensure .ssh directory exists with correct permissions
|
||||||
node.exec_silent('mkdir -p ${ssh_dir}')!
|
node.exec_silent('mkdir -p ${ssh_dir}')!
|
||||||
node.exec_silent('chmod 700 ${ssh_dir}')!
|
node.exec_silent('chmod 700 ${ssh_dir}')!
|
||||||
|
|
||||||
// Check if key already exists
|
// Check if key already exists
|
||||||
if node.file_exists(authorized_keys_path) {
|
if node.file_exists(authorized_keys_path) {
|
||||||
existing_keys := node.file_read(authorized_keys_path)!
|
existing_keys := node.file_read(authorized_keys_path)!
|
||||||
@@ -39,11 +39,11 @@ pub fn (mut agent SSHAgent) push_key_to_node(mut node builder.Node, key_name str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add key to authorized_keys
|
// Add key to authorized_keys
|
||||||
node.exec_silent('echo "${pubkey_content}" >> ${authorized_keys_path}')!
|
node.exec_silent('echo "${pubkey_content}" >> ${authorized_keys_path}')!
|
||||||
node.exec_silent('chmod 600 ${authorized_keys_path}')!
|
node.exec_silent('chmod 600 ${authorized_keys_path}')!
|
||||||
|
|
||||||
console.print_debug('SSH key "${key_name}" successfully pushed to node')
|
console.print_debug('SSH key "${key_name}" successfully pushed to node')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,31 +54,31 @@ pub fn (mut agent SSHAgent) remove_key_from_node(mut node builder.Node, key_name
|
|||||||
if node_info['category'] != 'ssh' {
|
if node_info['category'] != 'ssh' {
|
||||||
return error('Can only remove keys from SSH nodes, got: ${node_info['category']}')
|
return error('Can only remove keys from SSH nodes, got: ${node_info['category']}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the key
|
// Find 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 in agent')
|
return error('SSH key "${key_name}" not found in agent')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get public key content
|
// Get public key content
|
||||||
pubkey_content := key.keypub()!
|
pubkey_content := key.keypub()!
|
||||||
|
|
||||||
// Get authorized_keys path
|
// Get authorized_keys path
|
||||||
home_dir := node.environ_get()!['HOME'] or {
|
home_dir := node.environ_get()!['HOME'] or {
|
||||||
return error('Could not determine HOME directory on remote node')
|
return error('Could not determine HOME directory on remote node')
|
||||||
}
|
}
|
||||||
|
|
||||||
authorized_keys_path := '${home_dir}/.ssh/authorized_keys'
|
authorized_keys_path := '${home_dir}/.ssh/authorized_keys'
|
||||||
|
|
||||||
if !node.file_exists(authorized_keys_path) {
|
if !node.file_exists(authorized_keys_path) {
|
||||||
console.print_debug('authorized_keys file does not exist on remote node')
|
console.print_debug('authorized_keys file does not exist on remote node')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the key line from authorized_keys
|
// Remove the key line from authorized_keys
|
||||||
escaped_key := pubkey_content.replace('/', '\\/')
|
escaped_key := pubkey_content.replace('/', '\\/')
|
||||||
node.exec_silent('sed -i "\\|${escaped_key}|d" ${authorized_keys_path}')!
|
node.exec_silent('sed -i "\\|${escaped_key}|d" ${authorized_keys_path}')!
|
||||||
|
|
||||||
console.print_debug('SSH key "${key_name}" removed from remote node')
|
console.print_debug('SSH key "${key_name}" removed from remote node')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,11 +90,9 @@ pub fn (mut agent SSHAgent) verify_key_access(mut node builder.Node, key_name st
|
|||||||
if node_info['category'] != 'ssh' {
|
if node_info['category'] != 'ssh' {
|
||||||
return error('Can only verify access to SSH nodes')
|
return error('Can only verify access to SSH nodes')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut p := action.params
|
mut p := action.params
|
||||||
name := p.get('name')!
|
name := p.get('name')!
|
||||||
passphrase := p.get_default('passphrase', '')!
|
passphrase := p.get_default('passphrase', '')!
|
||||||
|
|
||||||
sshkey_create(mut agent, name, passphrase)!
|
sshkey_create(mut agent, name, passphrase)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
for mut action in delete_actions {
|
for mut action in delete_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
name := p.get('name')!
|
name := p.get('name')!
|
||||||
|
|
||||||
sshkey_delete(mut agent, name)!
|
sshkey_delete(mut agent, name)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
for mut action in load_actions {
|
for mut action in load_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
name := p.get('name')!
|
name := p.get('name')!
|
||||||
|
|
||||||
sshkey_load(mut agent, name)!
|
sshkey_load(mut agent, name)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
for mut action in check_key_actions {
|
for mut action in check_key_actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
name := p.get('name')!
|
name := p.get('name')!
|
||||||
|
|
||||||
sshkey_check(mut agent, name)!
|
sshkey_check(mut agent, name)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut p := action.params
|
mut p := action.params
|
||||||
node_addr := p.get('node')!
|
node_addr := p.get('node')!
|
||||||
key_name := p.get('name')!
|
key_name := p.get('name')!
|
||||||
|
|
||||||
remote_copy(mut agent, node_addr, key_name)!
|
remote_copy(mut agent, node_addr, key_name)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
mut p := action.params
|
mut p := action.params
|
||||||
node_addr := p.get('node')!
|
node_addr := p.get('node')!
|
||||||
key_name := p.get('name')!
|
key_name := p.get('name')!
|
||||||
|
|
||||||
remote_auth(mut agent, node_addr, key_name)!
|
remote_auth(mut agent, node_addr, key_name)!
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,19 +16,19 @@ pub mut:
|
|||||||
pub fn (mut agent SSHAgent) ensure_single_agent() ! {
|
pub fn (mut agent SSHAgent) ensure_single_agent() ! {
|
||||||
user := os.getenv('USER')
|
user := os.getenv('USER')
|
||||||
socket_path := get_agent_socket_path(user)
|
socket_path := get_agent_socket_path(user)
|
||||||
|
|
||||||
// Check if we have a valid agent already
|
// Check if we have a valid agent already
|
||||||
if agent.is_agent_responsive() {
|
if agent.is_agent_responsive() {
|
||||||
console.print_debug('SSH agent already running and responsive')
|
console.print_debug('SSH agent already running and responsive')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kill any orphaned agents
|
// Kill any orphaned agents
|
||||||
agent.cleanup_orphaned_agents()!
|
agent.cleanup_orphaned_agents()!
|
||||||
|
|
||||||
// Start new agent with consistent socket
|
// Start new agent with consistent socket
|
||||||
agent.start_agent_with_socket(socket_path)!
|
agent.start_agent_with_socket(socket_path)!
|
||||||
|
|
||||||
// Set environment variables
|
// Set environment variables
|
||||||
os.setenv('SSH_AUTH_SOCK', socket_path, true)
|
os.setenv('SSH_AUTH_SOCK', socket_path, true)
|
||||||
agent.active = true
|
agent.active = true
|
||||||
@@ -44,7 +44,7 @@ pub fn (mut agent SSHAgent) is_agent_responsive() bool {
|
|||||||
if os.getenv('SSH_AUTH_SOCK') == '' {
|
if os.getenv('SSH_AUTH_SOCK') == '' {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
res := os.execute('ssh-add -l 2>/dev/null')
|
res := os.execute('ssh-add -l 2>/dev/null')
|
||||||
return res.exit_code == 0 || res.exit_code == 1 // 1 means no keys, but agent is running
|
return res.exit_code == 0 || res.exit_code == 1 // 1 means no keys, but agent is running
|
||||||
}
|
}
|
||||||
@@ -52,12 +52,12 @@ pub fn (mut agent SSHAgent) is_agent_responsive() bool {
|
|||||||
// cleanup orphaned ssh-agent processes
|
// cleanup orphaned ssh-agent processes
|
||||||
pub fn (mut agent SSHAgent) cleanup_orphaned_agents() ! {
|
pub fn (mut agent SSHAgent) cleanup_orphaned_agents() ! {
|
||||||
user := os.getenv('USER')
|
user := os.getenv('USER')
|
||||||
|
|
||||||
// Find ssh-agent processes for current user
|
// Find ssh-agent processes for current user
|
||||||
res := os.execute('pgrep -u ${user} ssh-agent')
|
res := os.execute('pgrep -u ${user} ssh-agent')
|
||||||
if res.exit_code == 0 && res.output.len > 0 {
|
if res.exit_code == 0 && res.output.len > 0 {
|
||||||
pids := res.output.trim_space().split('\n')
|
pids := res.output.trim_space().split('\n')
|
||||||
|
|
||||||
for pid in pids {
|
for pid in pids {
|
||||||
if pid.trim_space() != '' {
|
if pid.trim_space() != '' {
|
||||||
// Check if this agent has a valid socket
|
// Check if this agent has a valid socket
|
||||||
@@ -77,7 +77,7 @@ fn (mut agent SSHAgent) is_agent_pid_valid(pid int) bool {
|
|||||||
if res.exit_code != 0 {
|
if res.exit_code != 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for socket_path in res.output.split('\n') {
|
for socket_path in res.output.split('\n') {
|
||||||
if socket_path.trim_space() != '' {
|
if socket_path.trim_space() != '' {
|
||||||
// Test if this socket responds
|
// Test if this socket responds
|
||||||
@@ -85,7 +85,7 @@ fn (mut agent SSHAgent) is_agent_pid_valid(pid int) bool {
|
|||||||
os.setenv('SSH_AUTH_SOCK', socket_path, true)
|
os.setenv('SSH_AUTH_SOCK', socket_path, true)
|
||||||
test_res := os.execute('ssh-add -l 2>/dev/null')
|
test_res := os.execute('ssh-add -l 2>/dev/null')
|
||||||
os.setenv('SSH_AUTH_SOCK', old_sock, true)
|
os.setenv('SSH_AUTH_SOCK', old_sock, true)
|
||||||
|
|
||||||
if test_res.exit_code == 0 || test_res.exit_code == 1 {
|
if test_res.exit_code == 0 || test_res.exit_code == 1 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -100,45 +100,45 @@ pub fn (mut agent SSHAgent) start_agent_with_socket(socket_path string) ! {
|
|||||||
if os.exists(socket_path) {
|
if os.exists(socket_path) {
|
||||||
os.rm(socket_path)!
|
os.rm(socket_path)!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start ssh-agent with specific socket
|
// Start ssh-agent with specific socket
|
||||||
cmd := 'ssh-agent -a ${socket_path}'
|
cmd := 'ssh-agent -a ${socket_path}'
|
||||||
res := os.execute(cmd)
|
res := os.execute(cmd)
|
||||||
if res.exit_code != 0 {
|
if res.exit_code != 0 {
|
||||||
return error('Failed to start ssh-agent: ${res.output}')
|
return error('Failed to start ssh-agent: ${res.output}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify socket was created
|
// Verify socket was created
|
||||||
if !os.exists(socket_path) {
|
if !os.exists(socket_path) {
|
||||||
return error('SSH agent socket was not created at ${socket_path}')
|
return error('SSH agent socket was not created at ${socket_path}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set environment variable
|
// Set environment variable
|
||||||
os.setenv('SSH_AUTH_SOCK', socket_path, true)
|
os.setenv('SSH_AUTH_SOCK', socket_path, true)
|
||||||
|
|
||||||
// Verify agent is responsive
|
// Verify agent is responsive
|
||||||
if !agent.is_agent_responsive() {
|
if !agent.is_agent_responsive() {
|
||||||
return error('SSH agent started but is not responsive')
|
return error('SSH agent started but is not responsive')
|
||||||
}
|
}
|
||||||
|
|
||||||
console.print_debug('SSH agent started with socket: ${socket_path}')
|
console.print_debug('SSH agent started with socket: ${socket_path}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// get agent status and diagnostics
|
// get agent status and diagnostics
|
||||||
pub fn (mut agent SSHAgent) diagnostics() map[string]string {
|
pub fn (mut agent SSHAgent) diagnostics() map[string]string {
|
||||||
mut diag := map[string]string{}
|
mut diag := map[string]string{}
|
||||||
|
|
||||||
diag['socket_path'] = os.getenv('SSH_AUTH_SOCK')
|
diag['socket_path'] = os.getenv('SSH_AUTH_SOCK')
|
||||||
diag['socket_exists'] = os.exists(diag['socket_path']).str()
|
diag['socket_exists'] = os.exists(diag['socket_path']).str()
|
||||||
diag['agent_responsive'] = agent.is_agent_responsive().str()
|
diag['agent_responsive'] = agent.is_agent_responsive().str()
|
||||||
diag['loaded_keys_count'] = agent.keys.filter(it.loaded).len.str()
|
diag['loaded_keys_count'] = agent.keys.filter(it.loaded).len.str()
|
||||||
diag['total_keys_count'] = agent.keys.len.str()
|
diag['total_keys_count'] = agent.keys.len.str()
|
||||||
|
|
||||||
// Count running ssh-agent processes
|
// Count running ssh-agent processes
|
||||||
user := os.getenv('USER')
|
user := os.getenv('USER')
|
||||||
res := os.execute('pgrep -u ${user} ssh-agent | wc -l')
|
res := os.execute('pgrep -u ${user} ssh-agent | wc -l')
|
||||||
diag['agent_processes'] = if res.exit_code == 0 { res.output.trim_space() } else { '0' }
|
diag['agent_processes'] = if res.exit_code == 0 { res.output.trim_space() } else { '0' }
|
||||||
|
|
||||||
return diag
|
return diag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub fn play(mut plbook PlayBook) ! {
|
|||||||
|
|
||||||
// Create tmux instance
|
// Create tmux instance
|
||||||
mut tmux_instance := new()!
|
mut tmux_instance := new()!
|
||||||
|
|
||||||
// Start tmux if not running
|
// Start tmux if not running
|
||||||
if !tmux_instance.is_running()! {
|
if !tmux_instance.is_running()! {
|
||||||
tmux_instance.start()!
|
tmux_instance.start()!
|
||||||
@@ -44,7 +44,7 @@ fn parse_window_name(name string) !ParsedWindowName {
|
|||||||
}
|
}
|
||||||
return ParsedWindowName{
|
return ParsedWindowName{
|
||||||
session: texttools.name_fix(parts[0])
|
session: texttools.name_fix(parts[0])
|
||||||
window: texttools.name_fix(parts[1])
|
window: texttools.name_fix(parts[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ fn parse_pane_name(name string) !ParsedPaneName {
|
|||||||
}
|
}
|
||||||
return ParsedPaneName{
|
return ParsedPaneName{
|
||||||
session: texttools.name_fix(parts[0])
|
session: texttools.name_fix(parts[0])
|
||||||
window: texttools.name_fix(parts[1])
|
window: texttools.name_fix(parts[1])
|
||||||
pane: texttools.name_fix(parts[2])
|
pane: texttools.name_fix(parts[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,12 +66,12 @@ fn play_session_create(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
|||||||
mut p := action.params
|
mut p := action.params
|
||||||
session_name := p.get('name')!
|
session_name := p.get('name')!
|
||||||
reset := p.get_default_false('reset')
|
reset := p.get_default_false('reset')
|
||||||
|
|
||||||
tmux_instance.session_create(
|
tmux_instance.session_create(
|
||||||
name: session_name
|
name: session_name
|
||||||
reset: reset
|
reset: reset
|
||||||
)!
|
)!
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,9 +81,9 @@ fn play_session_delete(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
|||||||
for mut action in actions {
|
for mut action in actions {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
session_name := p.get('name')!
|
session_name := p.get('name')!
|
||||||
|
|
||||||
tmux_instance.session_delete(session_name)!
|
tmux_instance.session_delete(session_name)!
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ fn play_window_create(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
|||||||
parsed := parse_window_name(name)!
|
parsed := parse_window_name(name)!
|
||||||
cmd := p.get_default('cmd', '')!
|
cmd := p.get_default('cmd', '')!
|
||||||
reset := p.get_default_false('reset')
|
reset := p.get_default_false('reset')
|
||||||
|
|
||||||
// Parse environment variables if provided
|
// Parse environment variables if provided
|
||||||
mut env := map[string]string{}
|
mut env := map[string]string{}
|
||||||
if env_str := p.get_default('env', '') {
|
if env_str := p.get_default('env', '') {
|
||||||
@@ -109,21 +109,21 @@ fn play_window_create(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get or create session
|
// Get or create session
|
||||||
mut session := if tmux_instance.session_exist(parsed.session) {
|
mut session := if tmux_instance.session_exist(parsed.session) {
|
||||||
tmux_instance.session_get(parsed.session)!
|
tmux_instance.session_get(parsed.session)!
|
||||||
} else {
|
} else {
|
||||||
tmux_instance.session_create(name: parsed.session)!
|
tmux_instance.session_create(name: parsed.session)!
|
||||||
}
|
}
|
||||||
|
|
||||||
session.window_new(
|
session.window_new(
|
||||||
name: parsed.window
|
name: parsed.window
|
||||||
cmd: cmd
|
cmd: cmd
|
||||||
env: env
|
env: env
|
||||||
reset: reset
|
reset: reset
|
||||||
)!
|
)!
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,12 +134,12 @@ fn play_window_delete(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
|||||||
mut p := action.params
|
mut p := action.params
|
||||||
name := p.get('name')!
|
name := p.get('name')!
|
||||||
parsed := parse_window_name(name)!
|
parsed := parse_window_name(name)!
|
||||||
|
|
||||||
if tmux_instance.session_exist(parsed.session) {
|
if tmux_instance.session_exist(parsed.session) {
|
||||||
mut session := tmux_instance.session_get(parsed.session)!
|
mut session := tmux_instance.session_get(parsed.session)!
|
||||||
session.window_delete(name: parsed.window)!
|
session.window_delete(name: parsed.window)!
|
||||||
}
|
}
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,19 +151,19 @@ fn play_pane_execute(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
|||||||
name := p.get('name')!
|
name := p.get('name')!
|
||||||
cmd := p.get('cmd')!
|
cmd := p.get('cmd')!
|
||||||
parsed := parse_pane_name(name)!
|
parsed := parse_pane_name(name)!
|
||||||
|
|
||||||
// Find the session and window
|
// Find the session and window
|
||||||
if tmux_instance.session_exist(parsed.session) {
|
if tmux_instance.session_exist(parsed.session) {
|
||||||
mut session := tmux_instance.session_get(parsed.session)!
|
mut session := tmux_instance.session_get(parsed.session)!
|
||||||
if session.window_exist(name: parsed.window) {
|
if session.window_exist(name: parsed.window) {
|
||||||
mut window := session.window_get(name: parsed.window)!
|
mut window := session.window_get(name: parsed.window)!
|
||||||
|
|
||||||
// Send command to the window (goes to active pane by default)
|
// Send command to the window (goes to active pane by default)
|
||||||
tmux_cmd := 'tmux send-keys -t ${session.name}:@${window.id} "${cmd}" Enter'
|
tmux_cmd := 'tmux send-keys -t ${session.name}:@${window.id} "${cmd}" Enter'
|
||||||
osal.exec(cmd: tmux_cmd, stdout: false, name: 'tmux_pane_execute')!
|
osal.exec(cmd: tmux_cmd, stdout: false, name: 'tmux_pane_execute')!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,21 +174,26 @@ fn play_pane_kill(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
|||||||
mut p := action.params
|
mut p := action.params
|
||||||
name := p.get('name')!
|
name := p.get('name')!
|
||||||
parsed := parse_pane_name(name)!
|
parsed := parse_pane_name(name)!
|
||||||
|
|
||||||
// Find the session and window, then kill the active pane
|
// Find the session and window, then kill the active pane
|
||||||
if tmux_instance.session_exist(parsed.session) {
|
if tmux_instance.session_exist(parsed.session) {
|
||||||
mut session := tmux_instance.session_get(parsed.session)!
|
mut session := tmux_instance.session_get(parsed.session)!
|
||||||
if session.window_exist(name: parsed.window) {
|
if session.window_exist(name: parsed.window) {
|
||||||
mut window := session.window_get(name: parsed.window)!
|
mut window := session.window_get(name: parsed.window)!
|
||||||
|
|
||||||
// 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
|
||||||
|
)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action.done = true
|
action.done = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -116,17 +112,16 @@ pub fn (mut t Tmux) window_new(args WindowNewArgs) !&Window {
|
|||||||
} else {
|
} else {
|
||||||
t.session_create(name: args.session_name)!
|
t.session_create(name: args.session_name)!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create window in session
|
// Create window in session
|
||||||
return session.window_new(
|
return session.window_new(
|
||||||
name: args.name
|
name: args.name
|
||||||
cmd: args.cmd
|
cmd: args.cmd
|
||||||
env: args.env
|
env: args.env
|
||||||
reset: args.reset
|
reset: args.reset
|
||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 ....')
|
||||||
|
|||||||
@@ -10,141 +10,134 @@ import freeflowuniverse.herolib.ui.console
|
|||||||
@[heap]
|
@[heap]
|
||||||
struct Pane {
|
struct Pane {
|
||||||
pub mut:
|
pub mut:
|
||||||
window &Window @[str: skip]
|
window &Window @[str: skip]
|
||||||
id int // pane id (e.g., %1, %2)
|
id int // pane id (e.g., %1, %2)
|
||||||
pid int // process id
|
pid int // process id
|
||||||
active bool // is this the active pane
|
active bool // is this the active pane
|
||||||
cmd string // command running in pane
|
cmd string // command running in pane
|
||||||
env map[string]string
|
env map[string]string
|
||||||
created_at time.Time
|
created_at time.Time
|
||||||
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{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use ps command to get CPU and memory stats
|
// Use ps command to get CPU and memory stats
|
||||||
cmd := 'ps -p ${p.pid} -o %cpu,%mem,rss --no-headers'
|
cmd := 'ps -p ${p.pid} -o %cpu,%mem,rss --no-headers'
|
||||||
result := osal.execute_silent(cmd) or {
|
result := osal.execute_silent(cmd) or {
|
||||||
return error('Cannot get stats for PID ${p.pid}: ${err}')
|
return error('Cannot get stats for PID ${p.pid}: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.trim_space() == '' {
|
if result.trim_space() == '' {
|
||||||
return error('Process ${p.pid} not found')
|
return error('Process ${p.pid} not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := result.trim_space().split_any(' \t').filter(it != '')
|
parts := result.trim_space().split_any(' \t').filter(it != '')
|
||||||
if parts.len < 3 {
|
if parts.len < 3 {
|
||||||
return error('Invalid ps output: ${result}')
|
return error('Invalid ps output: ${result}')
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProcessStats{
|
return ProcessStats{
|
||||||
cpu_percent: parts[0].f64()
|
cpu_percent: parts[0].f64()
|
||||||
memory_percent: parts[1].f64()
|
memory_percent: parts[1].f64()
|
||||||
memory_bytes: parts[2].u64() * 1024 // ps returns KB, convert to bytes
|
memory_bytes: parts[2].u64() * 1024 // ps returns KB, convert to bytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct TMuxLogEntry {
|
pub struct TMuxLogEntry {
|
||||||
pub mut:
|
pub mut:
|
||||||
content string
|
content string
|
||||||
timestamp time.Time
|
timestamp time.Time
|
||||||
offset int
|
offset int
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut p Pane) logs_get_new(reset bool) ![]TMuxLogEntry {
|
pub fn (mut p Pane) logs_get_new(reset bool) ![]TMuxLogEntry {
|
||||||
|
if reset {
|
||||||
|
p.last_output_offset = 0
|
||||||
|
}
|
||||||
|
// 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'
|
||||||
|
result := osal.execute_silent(cmd) or { return error('Cannot capture pane output: ${err}') }
|
||||||
|
|
||||||
if reset{
|
lines := result.split_into_lines()
|
||||||
p.last_output_offset = 0
|
mut entries := []TMuxLogEntry{}
|
||||||
}
|
|
||||||
// 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'
|
|
||||||
result := osal.execute_silent(cmd) or {
|
|
||||||
return error('Cannot capture pane output: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := result.split_into_lines()
|
mut i := 0
|
||||||
mut entries := []TMuxLogEntry{}
|
for line in lines {
|
||||||
|
if line.trim_space() != '' {
|
||||||
mut i:= 0
|
entries << TMuxLogEntry{
|
||||||
for line in lines {
|
content: line
|
||||||
if line.trim_space() != '' {
|
timestamp: time.now()
|
||||||
entries << TMuxLogEntry{
|
offset: p.last_output_offset + i + 1
|
||||||
content: line
|
}
|
||||||
timestamp: time.now()
|
}
|
||||||
offset: p.last_output_offset + i + 1
|
}
|
||||||
}
|
// Update offset to avoid duplicates next time
|
||||||
}
|
if entries.len > 0 {
|
||||||
}
|
p.last_output_offset = entries.last().offset
|
||||||
// Update offset to avoid duplicates next time
|
}
|
||||||
if entries.len > 0 {
|
return entries
|
||||||
p.last_output_offset = entries.last().offset
|
|
||||||
}
|
|
||||||
return entries
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut p Pane) exit_status() !ProcessStatus {
|
pub fn (mut p Pane) exit_status() !ProcessStatus {
|
||||||
// Get the last few lines to see if there's an exit status
|
// Get the last few lines to see if there's an exit status
|
||||||
logs := p.logs_all()!
|
logs := p.logs_all()!
|
||||||
lines := logs.split_into_lines()
|
lines := logs.split_into_lines()
|
||||||
|
|
||||||
// Look for shell prompt indicating command finished
|
// Look for shell prompt indicating command finished
|
||||||
for line in lines.reverse() {
|
for line in lines.reverse() {
|
||||||
line_clean := line.trim_space()
|
line_clean := line.trim_space()
|
||||||
if line_clean.contains('$') || line_clean.contains('#') || line_clean.contains('>') {
|
if line_clean.contains('$') || line_clean.contains('#') || line_clean.contains('>') {
|
||||||
// Found shell prompt, command likely finished
|
// Found shell prompt, command likely finished
|
||||||
// Could also check for specific exit codes in history
|
// Could also check for specific exit codes in history
|
||||||
return .finished_ok
|
return .finished_ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .finished_error
|
return .finished_error
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
pub fn (mut p Pane) output_wait(c_ string, timeoutsec int) ! {
|
pub fn (mut p Pane) output_wait(c_ string, timeoutsec int) ! {
|
||||||
mut t := ourtime.now()
|
mut t := ourtime.now()
|
||||||
start := t.unix()
|
start := t.unix()
|
||||||
c := c_.replace('\n', '')
|
c := c_.replace('\n', '')
|
||||||
for i in 0 .. 2000 {
|
for i in 0 .. 2000 {
|
||||||
entries := p.logs_get_new(reset: false)!
|
entries := p.logs_get_new(reset: false)!
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
if entry.content.replace('\n', '').contains(c) {
|
if entry.content.replace('\n', '').contains(c) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mut t2 := ourtime.now()
|
mut t2 := ourtime.now()
|
||||||
if t2.unix() > start + timeoutsec {
|
if t2.unix() > start + timeoutsec {
|
||||||
return error('timeout on output wait for tmux.\n${p} .\nwaiting for:\n${c}')
|
return error('timeout on output wait for tmux.\n${p} .\nwaiting for:\n${c}')
|
||||||
}
|
}
|
||||||
time.sleep(100 * time.millisecond)
|
time.sleep(100 * time.millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get process information for this pane and all its children
|
// Get process information for this pane and all its children
|
||||||
pub fn (mut p Pane) processinfo() !osal.ProcessMap {
|
pub fn (mut p Pane) processinfo() !osal.ProcessMap {
|
||||||
if p.pid == 0 {
|
if p.pid == 0 {
|
||||||
return error('Pane has no associated process (pid is 0)')
|
return error('Pane has no associated process (pid is 0)')
|
||||||
}
|
}
|
||||||
|
|
||||||
return osal.processinfo_with_children(p.pid)!
|
return osal.processinfo_with_children(p.pid)!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get process information for just this pane's main process
|
// Get process information for just this pane's main process
|
||||||
pub fn (mut p Pane) processinfo_main() !osal.ProcessInfo {
|
pub fn (mut p Pane) processinfo_main() !osal.ProcessInfo {
|
||||||
if p.pid == 0 {
|
if p.pid == 0 {
|
||||||
return error('Pane has no associated process (pid is 0)')
|
return error('Pane has no associated process (pid is 0)')
|
||||||
}
|
}
|
||||||
|
|
||||||
return osal.processinfo_get(p.pid)!
|
return osal.processinfo_get(p.pid)!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
module tmux
|
module tmux
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct ProcessStats {
|
pub struct ProcessStats {
|
||||||
pub mut:
|
pub mut:
|
||||||
cpu_percent f64
|
cpu_percent f64
|
||||||
memory_bytes u64
|
memory_bytes u64
|
||||||
memory_percent f64
|
memory_percent f64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum ProcessStatus {
|
enum ProcessStatus {
|
||||||
running
|
running
|
||||||
finished_ok
|
finished_ok
|
||||||
finished_error
|
finished_error
|
||||||
not_found
|
not_found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,66 +6,66 @@ import freeflowuniverse.herolib.ui.console
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
fn (mut t Tmux) scan_add(line string) !&Pane {
|
fn (mut t Tmux) scan_add(line string) !&Pane {
|
||||||
// Parse the line to get session, window, and pane info
|
// Parse the line to get session, window, and pane info
|
||||||
line_arr := line.split('|')
|
line_arr := line.split('|')
|
||||||
session_name := line_arr[0]
|
session_name := line_arr[0]
|
||||||
window_name := line_arr[1]
|
window_name := line_arr[1]
|
||||||
window_id := line_arr[2]
|
window_id := line_arr[2]
|
||||||
pane_active := line_arr[3]
|
pane_active := line_arr[3]
|
||||||
pane_id := line_arr[4]
|
pane_id := line_arr[4]
|
||||||
pane_pid := line_arr[5]
|
pane_pid := line_arr[5]
|
||||||
pane_start_command := line_arr[6] or { '' }
|
pane_start_command := line_arr[6] or { '' }
|
||||||
|
|
||||||
wid := (window_id.replace('@', '')).int()
|
wid := (window_id.replace('@', '')).int()
|
||||||
pid := (pane_id.replace('%', '')).int()
|
pid := (pane_id.replace('%', '')).int()
|
||||||
|
|
||||||
mut s := t.session_get(session_name)!
|
mut s := t.session_get(session_name)!
|
||||||
|
|
||||||
// Get or create window
|
// Get or create window
|
||||||
mut w := if s.window_exist(name: window_name, id: wid) {
|
mut w := if s.window_exist(name: window_name, id: wid) {
|
||||||
s.window_get(name: window_name, id: wid)!
|
s.window_get(name: window_name, id: wid)!
|
||||||
} else {
|
} else {
|
||||||
mut new_w := Window{
|
mut new_w := Window{
|
||||||
session: s
|
session: s
|
||||||
name: texttools.name_fix(window_name)
|
name: texttools.name_fix(window_name)
|
||||||
id: wid
|
id: wid
|
||||||
panes: []&Pane{}
|
panes: []&Pane{}
|
||||||
}
|
}
|
||||||
s.windows << &new_w
|
s.windows << &new_w
|
||||||
&new_w
|
&new_w
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create or update pane
|
// Create or update pane
|
||||||
mut p := Pane{
|
mut p := Pane{
|
||||||
window: w
|
window: w
|
||||||
id: pid
|
id: pid
|
||||||
pid: pane_pid.int()
|
pid: pane_pid.int()
|
||||||
active: pane_active == '1'
|
active: pane_active == '1'
|
||||||
cmd: pane_start_command
|
cmd: pane_start_command
|
||||||
created_at: time.now()
|
created_at: time.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if pane already exists
|
// Check if pane already exists
|
||||||
mut found := false
|
mut found := false
|
||||||
for mut existing_pane in w.panes {
|
for mut existing_pane in w.panes {
|
||||||
if existing_pane.id == pid {
|
if existing_pane.id == pid {
|
||||||
existing_pane.pid = p.pid
|
existing_pane.pid = p.pid
|
||||||
existing_pane.active = p.active
|
existing_pane.active = p.active
|
||||||
existing_pane.cmd = p.cmd
|
existing_pane.cmd = p.cmd
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
w.panes << &p
|
w.panes << &p
|
||||||
}
|
}
|
||||||
|
|
||||||
return &p
|
return &p
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 ....')
|
||||||
|
|
||||||
|
|||||||
@@ -21,87 +21,86 @@ 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:
|
||||||
name string
|
name string
|
||||||
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{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if check_result.exit_code == 0 {
|
if check_result.exit_code == 0 {
|
||||||
return error('duplicate session: ${s.name}')
|
return error('duplicate session: ${s.name}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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}')
|
||||||
}
|
}
|
||||||
|
|
||||||
mut current_windows := map[string]bool{}
|
|
||||||
for line in result.split_into_lines() {
|
|
||||||
if line.contains('|') {
|
|
||||||
parts := line.split('|')
|
|
||||||
if parts.len >= 2 {
|
|
||||||
window_name := texttools.name_fix(parts[0])
|
|
||||||
window_id := parts[1].replace('@', '').int()
|
|
||||||
window_active := parts[2] == '1'
|
|
||||||
|
|
||||||
current_windows[window_name] = true
|
|
||||||
|
|
||||||
// Update existing window or create new one
|
|
||||||
mut found := false
|
|
||||||
for mut w in s.windows {
|
|
||||||
if w.name == window_name {
|
|
||||||
w.id = window_id
|
|
||||||
w.active = window_active
|
|
||||||
w.scan()! // Scan panes for this window
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
mut new_window := Window{
|
|
||||||
session: &s
|
|
||||||
name: window_name
|
|
||||||
id: window_id
|
|
||||||
active: window_active
|
|
||||||
panes: []&Pane{}
|
|
||||||
env: map[string]string{}
|
|
||||||
}
|
|
||||||
new_window.scan()! // Scan panes for new window
|
|
||||||
s.windows << &new_window
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove windows that no longer exist in tmux
|
|
||||||
s.windows = s.windows.filter(current_windows[it.name] == true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
mut current_windows := map[string]bool{}
|
||||||
|
for line in result.split_into_lines() {
|
||||||
|
if line.contains('|') {
|
||||||
|
parts := line.split('|')
|
||||||
|
if parts.len >= 2 {
|
||||||
|
window_name := texttools.name_fix(parts[0])
|
||||||
|
window_id := parts[1].replace('@', '').int()
|
||||||
|
window_active := parts[2] == '1'
|
||||||
|
|
||||||
|
current_windows[window_name] = true
|
||||||
|
|
||||||
|
// Update existing window or create new one
|
||||||
|
mut found := false
|
||||||
|
for mut w in s.windows {
|
||||||
|
if w.name == window_name {
|
||||||
|
w.id = window_id
|
||||||
|
w.active = window_active
|
||||||
|
w.scan()! // Scan panes for this window
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
mut new_window := Window{
|
||||||
|
session: &s
|
||||||
|
name: window_name
|
||||||
|
id: window_id
|
||||||
|
active: window_active
|
||||||
|
panes: []&Pane{}
|
||||||
|
env: map[string]string{}
|
||||||
|
}
|
||||||
|
new_window.scan()! // Scan panes for new window
|
||||||
|
s.windows << &new_window
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove windows that no longer exist in tmux
|
||||||
|
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
|
||||||
@@ -139,14 +138,10 @@ pub fn (mut s Session) window_new(args WindowArgs) !Window {
|
|||||||
// Create the window with the specified command
|
// Create the window with the specified command
|
||||||
w.create(args.cmd)!
|
w.create(args.cmd)!
|
||||||
s.scan()!
|
s.scan()!
|
||||||
|
|
||||||
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{}
|
||||||
@@ -179,14 +174,14 @@ pub fn (mut s Session) str() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut s Session) stats() !ProcessStats {
|
pub fn (mut s Session) stats() !ProcessStats {
|
||||||
mut total := ProcessStats{}
|
mut total := ProcessStats{}
|
||||||
for mut window in s.windows {
|
for mut window in s.windows {
|
||||||
stats := window.stats() or { continue }
|
stats := window.stats() or { continue }
|
||||||
total.cpu_percent += stats.cpu_percent
|
total.cpu_percent += stats.cpu_percent
|
||||||
total.memory_bytes += stats.memory_bytes
|
total.memory_bytes += stats.memory_bytes
|
||||||
total.memory_percent += stats.memory_percent
|
total.memory_percent += stats.memory_percent
|
||||||
}
|
}
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn (mut s Session) activate()! {
|
// pub fn (mut s Session) activate()! {
|
||||||
@@ -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()!
|
||||||
@@ -259,4 +251,4 @@ pub fn (mut s Session) stop() ! {
|
|||||||
osal.execute_silent('tmux kill-session -t ${s.name}') or {
|
osal.execute_silent('tmux kill-session -t ${s.name}') or {
|
||||||
return error("Can't delete session ${s.name} - This may happen when session is not found: ${err}")
|
return error("Can't delete session ${s.name} - This may happen when session is not found: ${err}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,37 +45,37 @@ fn test_stop() ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_windows_get() ! {
|
fn test_windows_get() ! {
|
||||||
mut tmux := new()!
|
mut tmux := new()!
|
||||||
tmux.start()!
|
tmux.start()!
|
||||||
|
|
||||||
// After start, scan to get the initial session
|
// After start, scan to get the initial session
|
||||||
tmux.scan()!
|
tmux.scan()!
|
||||||
|
|
||||||
windows := tmux.windows_get()
|
windows := tmux.windows_get()
|
||||||
assert windows.len >= 0 // At least the default session should exist
|
assert windows.len >= 0 // At least the default session should exist
|
||||||
|
|
||||||
tmux.stop()!
|
tmux.stop()!
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_scan() ! {
|
fn test_scan() ! {
|
||||||
console.print_debug('-----Testing scan------')
|
console.print_debug('-----Testing scan------')
|
||||||
mut tmux := new()!
|
mut tmux := new()!
|
||||||
tmux.start()!
|
tmux.start()!
|
||||||
|
|
||||||
// Test initial scan
|
// Test initial scan
|
||||||
tmux.scan()!
|
tmux.scan()!
|
||||||
sessions_before := tmux.sessions.len
|
sessions_before := tmux.sessions.len
|
||||||
|
|
||||||
// Create a test session
|
// Create a test session
|
||||||
mut session := tmux.session_create(name: 'test_scan')!
|
mut session := tmux.session_create(name: 'test_scan')!
|
||||||
|
|
||||||
// Scan again
|
// Scan again
|
||||||
tmux.scan()!
|
tmux.scan()!
|
||||||
sessions_after := tmux.sessions.len
|
sessions_after := tmux.sessions.len
|
||||||
|
|
||||||
assert sessions_after >= sessions_before
|
assert sessions_after >= sessions_before
|
||||||
|
|
||||||
tmux.stop()!
|
tmux.stop()!
|
||||||
}
|
}
|
||||||
|
|
||||||
// //TODO: fix test
|
// //TODO: fix test
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub mut:
|
|||||||
session &Session @[skip]
|
session &Session @[skip]
|
||||||
name string
|
name string
|
||||||
id int
|
id int
|
||||||
panes []&Pane // windows contain multiple panes
|
panes []&Pane // windows contain multiple panes
|
||||||
active bool
|
active bool
|
||||||
env map[string]string
|
env map[string]string
|
||||||
}
|
}
|
||||||
@@ -22,105 +22,104 @@ 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}'"
|
||||||
result := osal.execute_silent(cmd) or {
|
result := osal.execute_silent(cmd) or {
|
||||||
// Window might not exist anymore
|
// Window might not exist anymore
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mut current_panes := map[int]bool{}
|
|
||||||
for line in result.split_into_lines() {
|
|
||||||
if line.contains('|') {
|
|
||||||
parts := line.split('|')
|
|
||||||
if parts.len >= 3 {
|
|
||||||
pane_id := parts[0].replace('%', '').int()
|
|
||||||
pane_pid := parts[1].int()
|
|
||||||
pane_active := parts[2] == '1'
|
|
||||||
pane_cmd := if parts.len > 3 { parts[3] } else { '' }
|
|
||||||
|
|
||||||
current_panes[pane_id] = true
|
|
||||||
|
|
||||||
// Update existing pane or create new one
|
|
||||||
mut found := false
|
|
||||||
for mut p in w.panes {
|
|
||||||
if p.id == pane_id {
|
|
||||||
p.pid = pane_pid
|
|
||||||
p.active = pane_active
|
|
||||||
p.cmd = pane_cmd
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
mut new_pane := Pane{
|
|
||||||
window: &w
|
|
||||||
id: pane_id
|
|
||||||
pid: pane_pid
|
|
||||||
active: pane_active
|
|
||||||
cmd: pane_cmd
|
|
||||||
env: map[string]string{}
|
|
||||||
created_at: time.now()
|
|
||||||
last_output_offset: 0
|
|
||||||
}
|
|
||||||
w.panes << &new_pane
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove panes that no longer exist
|
|
||||||
w.panes = w.panes.filter(current_panes[it.id] == true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
mut current_panes := map[int]bool{}
|
||||||
|
for line in result.split_into_lines() {
|
||||||
|
if line.contains('|') {
|
||||||
|
parts := line.split('|')
|
||||||
|
if parts.len >= 3 {
|
||||||
|
pane_id := parts[0].replace('%', '').int()
|
||||||
|
pane_pid := parts[1].int()
|
||||||
|
pane_active := parts[2] == '1'
|
||||||
|
pane_cmd := if parts.len > 3 { parts[3] } else { '' }
|
||||||
|
|
||||||
|
current_panes[pane_id] = true
|
||||||
|
|
||||||
|
// Update existing pane or create new one
|
||||||
|
mut found := false
|
||||||
|
for mut p in w.panes {
|
||||||
|
if p.id == pane_id {
|
||||||
|
p.pid = pane_pid
|
||||||
|
p.active = pane_active
|
||||||
|
p.cmd = pane_cmd
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
mut new_pane := Pane{
|
||||||
|
window: &w
|
||||||
|
id: pane_id
|
||||||
|
pid: pane_pid
|
||||||
|
active: pane_active
|
||||||
|
cmd: pane_cmd
|
||||||
|
env: map[string]string{}
|
||||||
|
created_at: time.now()
|
||||||
|
last_output_offset: 0
|
||||||
|
}
|
||||||
|
w.panes << &new_pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove panes that no longer exist
|
||||||
|
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') {
|
||||||
os.mkdir_all('/tmp/tmux/${w.session.name}')!
|
os.mkdir_all('/tmp/tmux/${w.session.name}')!
|
||||||
// Fix: osal.exec_string doesn't exist, use file writing instead
|
// Fix: osal.exec_string doesn't exist, use file writing instead
|
||||||
script_path := '/tmp/tmux/${w.session.name}/${w.name}.sh'
|
script_path := '/tmp/tmux/${w.session.name}/${w.name}.sh'
|
||||||
script_content := '#!/bin/bash\n' + cmd_
|
script_content := '#!/bin/bash\n' + cmd_
|
||||||
os.write_file(script_path, script_content)!
|
os.write_file(script_path, script_content)!
|
||||||
os.chmod(script_path, 0o755)!
|
os.chmod(script_path, 0o755)!
|
||||||
final_cmd = script_path
|
final_cmd = script_path
|
||||||
}
|
}
|
||||||
|
|
||||||
mut newcmd := '/bin/bash -c "${final_cmd}"'
|
mut newcmd := '/bin/bash -c "${final_cmd}"'
|
||||||
if cmd_ == "" {
|
if cmd_ == '' {
|
||||||
newcmd = '/bin/bash'
|
newcmd = '/bin/bash'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build environment arguments
|
// Build environment arguments
|
||||||
mut env_args := ''
|
mut env_args := ''
|
||||||
for key, value in w.env {
|
for key, value in w.env {
|
||||||
env_args += ' -e ${key}="${value}"'
|
env_args += ' -e ${key}="${value}"'
|
||||||
}
|
}
|
||||||
|
|
||||||
res_opt := "-P -F '#{session_name}|#{window_name}|#{window_id}|#{pane_active}|#{pane_id}|#{pane_pid}|#{pane_start_command}'"
|
res_opt := "-P -F '#{session_name}|#{window_name}|#{window_id}|#{pane_active}|#{pane_id}|#{pane_pid}|#{pane_start_command}'"
|
||||||
cmd := 'tmux new-window ${res_opt}${env_args} -t ${w.session.name} -n ${w.name} \'${newcmd}\''
|
cmd := 'tmux new-window ${res_opt}${env_args} -t ${w.session.name} -n ${w.name} \'${newcmd}\''
|
||||||
console.print_debug(cmd)
|
console.print_debug(cmd)
|
||||||
|
|
||||||
res := osal.exec(cmd: cmd, stdout: false, name: 'tmux_window_create') or {
|
res := osal.exec(cmd: cmd, stdout: false, name: 'tmux_window_create') or {
|
||||||
return error("Can't create new window ${w.name} \n${cmd}\n${err}")
|
return error("Can't create new window ${w.name} \n${cmd}\n${err}")
|
||||||
}
|
}
|
||||||
|
|
||||||
line_arr := res.output.split('|')
|
line_arr := res.output.split('|')
|
||||||
wid := line_arr[2] or { return error('cannot split line for window create.\n${line_arr}') }
|
wid := line_arr[2] or { return error('cannot split line for window create.\n${line_arr}') }
|
||||||
w.id = wid.replace('@', '').int()
|
w.id = wid.replace('@', '').int()
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop the window
|
// stop the window
|
||||||
@@ -143,14 +142,14 @@ pub fn (window Window) str() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut w Window) stats() !ProcessStats {
|
pub fn (mut w Window) stats() !ProcessStats {
|
||||||
mut total := ProcessStats{}
|
mut total := ProcessStats{}
|
||||||
for mut pane in w.panes {
|
for mut pane in w.panes {
|
||||||
stats := pane.stats() or { continue }
|
stats := pane.stats() or { continue }
|
||||||
total.cpu_percent += stats.cpu_percent
|
total.cpu_percent += stats.cpu_percent
|
||||||
total.memory_bytes += stats.memory_bytes
|
total.memory_bytes += stats.memory_bytes
|
||||||
total.memory_percent += stats.memory_percent
|
total.memory_percent += stats.memory_percent
|
||||||
}
|
}
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
// will select the current window so with tmux a we can go there .
|
// will select the current window so with tmux a we can go there .
|
||||||
@@ -169,10 +168,10 @@ pub fn (mut w Window) pane_list() []&Pane {
|
|||||||
|
|
||||||
// Get active pane in window
|
// Get active pane in window
|
||||||
pub fn (mut w Window) pane_active() ?&Pane {
|
pub fn (mut w Window) pane_active() ?&Pane {
|
||||||
for pane in w.panes {
|
for pane in w.panes {
|
||||||
if pane.active {
|
if pane.active {
|
||||||
return pane
|
return pane
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,23 +24,23 @@ fn testsuite_end() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_window_new() ! {
|
fn test_window_new() ! {
|
||||||
mut tmux := new()!
|
mut tmux := new()!
|
||||||
tmux.start()!
|
tmux.start()!
|
||||||
|
|
||||||
// Create session first
|
// Create session first
|
||||||
mut session := tmux.session_create(name: 'main')!
|
mut session := tmux.session_create(name: 'main')!
|
||||||
|
|
||||||
// Test window creation
|
// Test window creation
|
||||||
mut window := session.window_new(
|
mut window := session.window_new(
|
||||||
name: 'TestWindow'
|
name: 'TestWindow'
|
||||||
cmd: 'bash'
|
cmd: 'bash'
|
||||||
reset: true
|
reset: true
|
||||||
)!
|
)!
|
||||||
|
|
||||||
assert window.name == 'testwindow' // name_fix converts to lowercase
|
assert window.name == 'testwindow' // name_fix converts to lowercase
|
||||||
assert session.window_exist(name: 'testwindow')
|
assert session.window_exist(name: 'testwindow')
|
||||||
|
|
||||||
tmux.stop()!
|
tmux.stop()!
|
||||||
}
|
}
|
||||||
|
|
||||||
// tests creating duplicate windows
|
// tests creating duplicate windows
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BidStatus {
|
pub enum BidStatus {
|
||||||
@@ -21,4 +21,4 @@ pub enum BidStatus {
|
|||||||
assigned
|
assigned
|
||||||
cancelled
|
cancelled
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ module datamodel
|
|||||||
@[heap]
|
@[heap]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ReservationStatus {
|
pub enum ReservationStatus {
|
||||||
@@ -18,4 +18,4 @@ pub enum ReservationStatus {
|
|||||||
assigned
|
assigned
|
||||||
cancelled
|
cancelled
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user