...
This commit is contained in:
@@ -25,7 +25,7 @@ if agent.keys.len == 0 {
|
||||
console.print_header('No keys found, generating example key...')
|
||||
mut key := agent.generate('example_key', '')!
|
||||
console.print_debug('Generated key: ${key}')
|
||||
|
||||
|
||||
// Load the generated key
|
||||
key.load()!
|
||||
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()!
|
||||
// Test user creation
|
||||
lf.user_create(
|
||||
name: 'testuser'
|
||||
name: 'testuser'
|
||||
sshkey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM3/2K7R8A/l0kM0/d'
|
||||
)!
|
||||
|
||||
// Test ssh key creation
|
||||
lf.sshkey_create(
|
||||
username: 'testuser'
|
||||
username: 'testuser'
|
||||
sshkey_name: 'testkey'
|
||||
)!
|
||||
|
||||
// Test ssh key deletion
|
||||
lf.sshkey_delete(
|
||||
username: 'testuser'
|
||||
username: 'testuser'
|
||||
sshkey_name: 'testkey'
|
||||
)!
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
|
||||
import freeflowuniverse.herolib.osal.tmux
|
||||
|
||||
mut t := tmux.new()!
|
||||
|
||||
@@ -6,7 +6,7 @@ import time
|
||||
|
||||
mut t := tmux.new()!
|
||||
if !t.is_running()! {
|
||||
t.start()!
|
||||
t.start()!
|
||||
}
|
||||
|
||||
// Create a session and window
|
||||
@@ -18,15 +18,15 @@ time.sleep(1000 * time.millisecond)
|
||||
|
||||
// Get the active pane
|
||||
if mut pane := window.pane_active() {
|
||||
// Get process info for the pane and its children
|
||||
process_map := pane.processinfo()!
|
||||
|
||||
println('Process tree for pane ${pane.id}:')
|
||||
for process in process_map.processes {
|
||||
println(' PID: ${process.pid}, CPU: ${process.cpu_perc}%, Memory: ${process.mem_perc}%, Command: ${process.cmd}')
|
||||
}
|
||||
|
||||
// Get just the main process info
|
||||
main_process := pane.processinfo_main()!
|
||||
println('\nMain process: PID ${main_process.pid}, Command: ${main_process.cmd}')
|
||||
}
|
||||
// Get process info for the pane and its children
|
||||
process_map := pane.processinfo()!
|
||||
|
||||
println('Process tree for pane ${pane.id}:')
|
||||
for process in process_map.processes {
|
||||
println(' PID: ${process.pid}, CPU: ${process.cpu_perc}%, Memory: ${process.mem_perc}%, Command: ${process.cmd}')
|
||||
}
|
||||
|
||||
// Get just the main process info
|
||||
main_process := pane.processinfo_main()!
|
||||
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 {
|
||||
mut token := c.new_access_token(
|
||||
identity: 'api'
|
||||
name: 'API User'
|
||||
ttl: 10 * 60 // 10 minutes
|
||||
name: 'API User'
|
||||
ttl: 10 * 60 // 10 minutes
|
||||
)!
|
||||
token.add_video_grant(VideoGrant{
|
||||
room_create: true
|
||||
room_admin: true
|
||||
room_list: true
|
||||
room_admin: true
|
||||
room_list: true
|
||||
})
|
||||
jwt := token.to_jwt()!
|
||||
|
||||
@@ -25,13 +25,13 @@ fn (mut c LivekitClient) post(path string, body any) !http.Response {
|
||||
data := json.encode(body)
|
||||
mut req := http.Request{
|
||||
method: .post
|
||||
url: url
|
||||
url: url
|
||||
header: header
|
||||
data: data
|
||||
data: data
|
||||
}
|
||||
resp := http.fetch(req)!
|
||||
if resp.status_code != 200 {
|
||||
return error('failed to execute request: ${resp.body}')
|
||||
}
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@ module livekit
|
||||
import freeflowuniverse.herolib.data.caching
|
||||
import os
|
||||
|
||||
const CACHING_METHOD = caching.CachingMethod.once_per_process
|
||||
// const CACHING_METHOD = caching.CachingMethod.once_per_process
|
||||
|
||||
fn _init() ! {
|
||||
if caching.is_set(key: 'livekit_clients') {
|
||||
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 {
|
||||
@@ -25,10 +28,10 @@ pub fn get(name string) !LivekitClient {
|
||||
pub fn set(client LivekitClient) ! {
|
||||
mut clients := _get()!
|
||||
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 {
|
||||
mut clients := _get()!
|
||||
return name in clients
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ module livekit
|
||||
|
||||
pub struct SendDataArgs {
|
||||
pub mut:
|
||||
room_name string
|
||||
data []u8
|
||||
kind DataPacket_Kind
|
||||
room_name string
|
||||
data []u8
|
||||
kind DataPacket_Kind
|
||||
destination_sids []string
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@ pub enum DataPacket_Kind {
|
||||
|
||||
pub fn (mut c LivekitClient) send_data(args SendDataArgs) ! {
|
||||
_ = c.post('twirp/livekit.RoomService/SendData', args)!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,40 +4,40 @@ import json
|
||||
|
||||
pub struct EgressInfo {
|
||||
pub mut:
|
||||
egress_id string
|
||||
room_id string
|
||||
status string
|
||||
egress_id string
|
||||
room_id string
|
||||
status string
|
||||
started_at i64
|
||||
ended_at i64
|
||||
error string
|
||||
ended_at i64
|
||||
error string
|
||||
}
|
||||
|
||||
pub struct StartRoomCompositeEgressArgs {
|
||||
pub mut:
|
||||
room_name string
|
||||
layout string
|
||||
audio_only bool
|
||||
video_only bool
|
||||
room_name string
|
||||
layout string
|
||||
audio_only bool
|
||||
video_only bool
|
||||
custom_base_url string
|
||||
}
|
||||
|
||||
pub struct StartTrackCompositeEgressArgs {
|
||||
pub mut:
|
||||
room_name string
|
||||
room_name string
|
||||
audio_track_id string
|
||||
video_track_id string
|
||||
}
|
||||
|
||||
pub struct StartWebEgressArgs {
|
||||
pub mut:
|
||||
url string
|
||||
url string
|
||||
audio_only bool
|
||||
video_only bool
|
||||
}
|
||||
|
||||
pub struct UpdateStreamArgs {
|
||||
pub mut:
|
||||
add_output_urls []string
|
||||
add_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 {
|
||||
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)!
|
||||
return egress_info
|
||||
}
|
||||
|
||||
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)!
|
||||
return egress_info
|
||||
}
|
||||
|
||||
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)!
|
||||
return egress_infos
|
||||
}
|
||||
|
||||
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)!
|
||||
return egress_info
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,30 +77,20 @@ pub mut:
|
||||
|
||||
pub struct InputAudioState {
|
||||
pub mut:
|
||||
mime_type string
|
||||
channels u32
|
||||
mime_type string
|
||||
channels u32
|
||||
sample_rate u32
|
||||
}
|
||||
|
||||
pub struct CreateIngressArgs {
|
||||
pub mut:
|
||||
name string
|
||||
room_name string
|
||||
name string
|
||||
room_name string
|
||||
participant_identity string
|
||||
participant_name string
|
||||
input_type IngressInput
|
||||
audio IngressAudioOptions
|
||||
video IngressVideoOptions
|
||||
}
|
||||
|
||||
pub struct UpdateIngressArgs {
|
||||
pub mut:
|
||||
name string
|
||||
room_name string
|
||||
participant_identity string
|
||||
participant_name string
|
||||
audio IngressAudioOptions
|
||||
video IngressVideoOptions
|
||||
input_type IngressInput
|
||||
audio IngressAudioOptions
|
||||
video IngressVideoOptions
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn (mut c LivekitClient) update_ingress(ingress_id string, args UpdateIngressArgs) !IngressInfo {
|
||||
mut resp := c.post('twirp/livekit.Ingress/UpdateIngress', {'ingress_id': ingress_id, ...args})!
|
||||
pub struct UpdateIngressArgs {
|
||||
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)!
|
||||
return ingress_info
|
||||
}
|
||||
|
||||
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)!
|
||||
return ingress_infos
|
||||
}
|
||||
|
||||
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)!
|
||||
return ingress_info
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,4 @@ pub fn heroscript_dumps(obj LivekitClient) !string {
|
||||
pub fn heroscript_loads(heroscript string) !LivekitClient {
|
||||
mut obj := encoderhero.decode[LivekitClient](heroscript)!
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,23 @@ import json
|
||||
|
||||
pub struct ParticipantInfo {
|
||||
pub mut:
|
||||
sid string
|
||||
identity string
|
||||
state string
|
||||
metadata string
|
||||
joined_at i64
|
||||
name string
|
||||
version u32
|
||||
sid string
|
||||
identity string
|
||||
state string
|
||||
metadata string
|
||||
joined_at i64
|
||||
name string
|
||||
version u32
|
||||
permission string
|
||||
region string
|
||||
publisher bool
|
||||
region string
|
||||
publisher bool
|
||||
}
|
||||
|
||||
pub struct UpdateParticipantArgs {
|
||||
pub mut:
|
||||
room_name string
|
||||
identity string
|
||||
metadata string
|
||||
room_name string
|
||||
identity string
|
||||
metadata string
|
||||
permission string
|
||||
}
|
||||
|
||||
@@ -33,19 +33,27 @@ pub mut:
|
||||
}
|
||||
|
||||
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)!
|
||||
return participants
|
||||
}
|
||||
|
||||
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)!
|
||||
return participant
|
||||
}
|
||||
|
||||
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) ! {
|
||||
@@ -54,4 +62,4 @@ pub fn (mut c LivekitClient) update_participant(args UpdateParticipantArgs) ! {
|
||||
|
||||
pub fn (mut c LivekitClient) mute_published_track(args MutePublishedTrackArgs) ! {
|
||||
_ = c.post('twirp/livekit.RoomService/MutePublishedTrack', args)!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,19 +13,19 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
if plbook.exists_once(filter: 'livekit.init') {
|
||||
mut action := plbook.get(filter: 'livekit.init')!
|
||||
mut p := action.params
|
||||
|
||||
|
||||
name := texttools.name_fix(p.get_default('name', 'default')!)
|
||||
url := p.get('url')!
|
||||
api_key := p.get('api_key')!
|
||||
api_secret := p.get('api_secret')!
|
||||
|
||||
|
||||
mut client := LivekitClient{
|
||||
name: name
|
||||
url: url
|
||||
api_key: api_key
|
||||
name: name
|
||||
url: url
|
||||
api_key: api_key
|
||||
api_secret: api_secret
|
||||
}
|
||||
|
||||
|
||||
set(client)!
|
||||
console.print_header('LiveKit client "${name}" configured')
|
||||
action.done = true
|
||||
@@ -35,22 +35,22 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut room_create_actions := plbook.find(filter: 'livekit.room_create')!
|
||||
for mut action in room_create_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||
room_name := p.get('name')!
|
||||
empty_timeout := p.get_u32_default('empty_timeout', 300)!
|
||||
max_participants := p.get_u32_default('max_participants', 50)!
|
||||
metadata := p.get_default('metadata', '')!
|
||||
|
||||
|
||||
mut client := get(name: client_name)!
|
||||
|
||||
|
||||
room := client.create_room(
|
||||
name: room_name
|
||||
empty_timeout: empty_timeout
|
||||
name: room_name
|
||||
empty_timeout: empty_timeout
|
||||
max_participants: max_participants
|
||||
metadata: metadata
|
||||
metadata: metadata
|
||||
)!
|
||||
|
||||
|
||||
console.print_header('Room "${room_name}" created successfully')
|
||||
action.done = true
|
||||
}
|
||||
@@ -59,13 +59,13 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut room_delete_actions := plbook.find(filter: 'livekit.room_delete')!
|
||||
for mut action in room_delete_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||
room_name := p.get('name')!
|
||||
|
||||
|
||||
mut client := get(name: client_name)!
|
||||
client.delete_room(room_name)!
|
||||
|
||||
|
||||
console.print_header('Room "${room_name}" deleted successfully')
|
||||
action.done = true
|
||||
}
|
||||
@@ -74,14 +74,14 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut participant_remove_actions := plbook.find(filter: 'livekit.participant_remove')!
|
||||
for mut action in participant_remove_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||
room_name := p.get('room')!
|
||||
identity := p.get('identity')!
|
||||
|
||||
|
||||
mut client := get(name: client_name)!
|
||||
client.remove_participant(room_name, identity)!
|
||||
|
||||
|
||||
console.print_header('Participant "${identity}" removed from room "${room_name}"')
|
||||
action.done = true
|
||||
}
|
||||
@@ -90,21 +90,21 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut participant_mute_actions := plbook.find(filter: 'livekit.participant_mute')!
|
||||
for mut action in participant_mute_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||
room_name := p.get('room')!
|
||||
identity := p.get('identity')!
|
||||
track_sid := p.get('track_sid')!
|
||||
muted := p.get_default_true('muted')
|
||||
|
||||
|
||||
mut client := get(name: client_name)!
|
||||
client.mute_published_track(
|
||||
room_name: room_name
|
||||
identity: identity
|
||||
identity: identity
|
||||
track_sid: track_sid
|
||||
muted: muted
|
||||
muted: muted
|
||||
)!
|
||||
|
||||
|
||||
status := if muted { 'muted' } else { 'unmuted' }
|
||||
console.print_header('Track "${track_sid}" ${status} for participant "${identity}"')
|
||||
action.done = true
|
||||
@@ -114,17 +114,17 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut room_update_actions := plbook.find(filter: 'livekit.room_update')!
|
||||
for mut action in room_update_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||
room_name := p.get('room')!
|
||||
metadata := p.get('metadata')!
|
||||
|
||||
|
||||
mut client := get(name: client_name)!
|
||||
client.update_room_metadata(
|
||||
room_name: room_name
|
||||
metadata: metadata
|
||||
metadata: metadata
|
||||
)!
|
||||
|
||||
|
||||
console.print_header('Room "${room_name}" metadata updated')
|
||||
action.done = true
|
||||
}
|
||||
@@ -133,7 +133,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut token_create_actions := plbook.find(filter: 'livekit.token_create')!
|
||||
for mut action in token_create_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
client_name := texttools.name_fix(p.get_default('client', 'default')!)
|
||||
identity := p.get('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_subscribe := p.get_default_true('can_subscribe')
|
||||
can_publish_data := p.get_default_false('can_publish_data')
|
||||
|
||||
|
||||
mut client := get(name: client_name)!
|
||||
|
||||
|
||||
mut token := client.new_access_token(
|
||||
identity: identity
|
||||
name: name
|
||||
ttl: ttl
|
||||
name: name
|
||||
ttl: ttl
|
||||
)!
|
||||
|
||||
|
||||
token.add_video_grant(VideoGrant{
|
||||
room: room
|
||||
room_join: true
|
||||
can_publish: can_publish
|
||||
can_subscribe: can_subscribe
|
||||
room: room
|
||||
room_join: true
|
||||
can_publish: can_publish
|
||||
can_subscribe: can_subscribe
|
||||
can_publish_data: can_publish_data
|
||||
})
|
||||
|
||||
|
||||
jwt := token.to_jwt()!
|
||||
console.print_header('Access token generated for "${identity}"')
|
||||
console.print_debug('Token: ${jwt}')
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,25 +5,25 @@ import net.http
|
||||
|
||||
pub struct Room {
|
||||
pub mut:
|
||||
sid string
|
||||
name string
|
||||
empty_timeout u32
|
||||
max_participants u32
|
||||
creation_time i64
|
||||
turn_password string
|
||||
enabled_codecs []string
|
||||
metadata string
|
||||
num_participants u32
|
||||
sid string
|
||||
name string
|
||||
empty_timeout u32
|
||||
max_participants u32
|
||||
creation_time i64
|
||||
turn_password string
|
||||
enabled_codecs []string
|
||||
metadata string
|
||||
num_participants u32
|
||||
num_connected_participants u32
|
||||
active_recording bool
|
||||
active_recording bool
|
||||
}
|
||||
|
||||
pub struct CreateRoomArgs {
|
||||
pub mut:
|
||||
name string
|
||||
empty_timeout u32
|
||||
max_participants u32
|
||||
metadata string
|
||||
name string
|
||||
empty_timeout u32
|
||||
max_participants u32
|
||||
metadata string
|
||||
}
|
||||
|
||||
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) ! {
|
||||
_ = 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) ! {
|
||||
_ = c.post('twirp/livekit.RoomService/UpdateRoomMetadata', args)!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,19 +15,19 @@ pub:
|
||||
unix_micro string
|
||||
}
|
||||
|
||||
pub struct Room {
|
||||
pub:
|
||||
active_recording bool
|
||||
creation_time string
|
||||
departure_timeout int
|
||||
empty_timeout int
|
||||
enabled_codecs []Codec
|
||||
max_participants int
|
||||
metadata string
|
||||
name string
|
||||
num_participants int
|
||||
num_publishers int
|
||||
sid string
|
||||
turn_password string
|
||||
version Version
|
||||
}
|
||||
// pub struct Room {
|
||||
// pub:
|
||||
// active_recording bool
|
||||
// creation_time string
|
||||
// departure_timeout int
|
||||
// empty_timeout int
|
||||
// enabled_codecs []Codec
|
||||
// max_participants int
|
||||
// metadata string
|
||||
// name string
|
||||
// num_participants int
|
||||
// num_publishers int
|
||||
// sid string
|
||||
// turn_password string
|
||||
// version Version
|
||||
// }
|
||||
|
||||
@@ -5,33 +5,33 @@ import time
|
||||
|
||||
pub struct AccessToken {
|
||||
pub mut:
|
||||
api_key string
|
||||
api_secret string
|
||||
identity string
|
||||
name string
|
||||
ttl int
|
||||
api_key string
|
||||
api_secret string
|
||||
identity string
|
||||
name string
|
||||
ttl int
|
||||
video_grant VideoGrant
|
||||
}
|
||||
|
||||
pub struct VideoGrant {
|
||||
pub mut:
|
||||
room_create bool
|
||||
room_admin bool
|
||||
room_join bool
|
||||
room_list bool
|
||||
can_publish bool
|
||||
can_subscribe bool
|
||||
room_create bool
|
||||
room_admin bool
|
||||
room_join bool
|
||||
room_list bool
|
||||
can_publish bool
|
||||
can_subscribe bool
|
||||
can_publish_data bool
|
||||
room string
|
||||
room string
|
||||
}
|
||||
|
||||
pub fn (mut c LivekitClient) new_access_token(identity string, name string, ttl int) !AccessToken {
|
||||
return AccessToken{
|
||||
api_key: c.api_key
|
||||
api_key: c.api_key
|
||||
api_secret: c.api_secret
|
||||
identity: identity
|
||||
name: name
|
||||
ttl: ttl
|
||||
identity: identity
|
||||
name: name
|
||||
ttl: ttl
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,4 +49,4 @@ pub fn (t AccessToken) to_jwt() !string {
|
||||
claims.name = t.name
|
||||
claims.video = t.video_grant
|
||||
return jwt.encode(claims, t.api_secret, .hs256)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,60 +18,60 @@ pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
// VideoGrant struct placeholder
|
||||
pub struct VideoGrant {
|
||||
pub mut:
|
||||
room string
|
||||
room_join bool @[json: 'roomJoin']
|
||||
room_list bool @[json: 'roomList']
|
||||
can_publish bool @[json: 'canPublish']
|
||||
can_publish_data bool @[json: 'canPublishData']
|
||||
can_subscribe bool @[json: 'canSubscribe']
|
||||
}
|
||||
// // VideoGrant struct placeholder
|
||||
// pub struct VideoGrant {
|
||||
// pub mut:
|
||||
// room string
|
||||
// room_join bool @[json: 'roomJoin']
|
||||
// room_list bool @[json: 'roomList']
|
||||
// can_publish bool @[json: 'canPublish']
|
||||
// can_publish_data bool @[json: 'canPublishData']
|
||||
// can_subscribe bool @[json: 'canSubscribe']
|
||||
// }
|
||||
|
||||
// SIPGrant struct placeholder
|
||||
struct SIPGrant {}
|
||||
|
||||
// AccessToken class
|
||||
pub struct AccessToken {
|
||||
mut:
|
||||
api_key string
|
||||
api_secret string
|
||||
grants ClaimGrants
|
||||
identity string
|
||||
ttl int
|
||||
}
|
||||
// // AccessToken class
|
||||
// pub struct AccessToken {
|
||||
// mut:
|
||||
// api_key string
|
||||
// api_secret string
|
||||
// grants ClaimGrants
|
||||
// identity string
|
||||
// ttl int
|
||||
// }
|
||||
|
||||
// Method to add a video grant to the token
|
||||
pub fn (mut token AccessToken) add_video_grant(grant VideoGrant) {
|
||||
token.grants.video = grant
|
||||
}
|
||||
// pub fn (mut token AccessToken) add_video_grant(grant VideoGrant) {
|
||||
// token.grants.video = grant
|
||||
// }
|
||||
|
||||
// Method to generate a JWT token
|
||||
pub fn (token AccessToken) to_jwt() !string {
|
||||
// Create JWT payload
|
||||
payload := json.encode(token.grants)
|
||||
// // Method to generate a JWT token
|
||||
// pub fn (token AccessToken) to_jwt() !string {
|
||||
// // Create JWT payload
|
||||
// payload := json.encode(token.grants)
|
||||
|
||||
println('payload: ${payload}')
|
||||
// println('payload: ${payload}')
|
||||
|
||||
// Create JWT header
|
||||
header := '{"alg":"HS256","typ":"JWT"}'
|
||||
// // Create JWT header
|
||||
// header := '{"alg":"HS256","typ":"JWT"}'
|
||||
|
||||
// Encode header and payload in base64
|
||||
header_encoded := base64.url_encode_str(header)
|
||||
payload_encoded := base64.url_encode_str(payload)
|
||||
// // Encode header and payload in base64
|
||||
// header_encoded := base64.url_encode_str(header)
|
||||
// payload_encoded := base64.url_encode_str(payload)
|
||||
|
||||
// Create the unsigned token
|
||||
unsigned_token := '${header_encoded}.${payload_encoded}'
|
||||
// // Create the unsigned token
|
||||
// unsigned_token := '${header_encoded}.${payload_encoded}'
|
||||
|
||||
// Create the HMAC-SHA256 signature
|
||||
signature := hmac.new(token.api_secret.bytes(), unsigned_token.bytes(), sha256.sum,
|
||||
sha256.block_size)
|
||||
// // Create the HMAC-SHA256 signature
|
||||
// signature := hmac.new(token.api_secret.bytes(), unsigned_token.bytes(), sha256.sum,
|
||||
// sha256.block_size)
|
||||
|
||||
// Encode the signature in base64
|
||||
signature_encoded := base64.url_encode(signature)
|
||||
// // Encode the signature in base64
|
||||
// signature_encoded := base64.url_encode(signature)
|
||||
|
||||
// Create the final JWT
|
||||
jwt := '${unsigned_token}.${signature_encoded}'
|
||||
return jwt
|
||||
}
|
||||
// // Create the final JWT
|
||||
// jwt := '${unsigned_token}.${signature_encoded}'
|
||||
// return jwt
|
||||
// }
|
||||
|
||||
@@ -15,8 +15,8 @@ pub const default_url = 'http://localhost:8990'
|
||||
@[heap]
|
||||
pub struct MyceliumRPC {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
url string = default_url // RPC server URL
|
||||
name string = 'default'
|
||||
url string = default_url // RPC server URL
|
||||
// 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 manager := &TraefikManager{
|
||||
name: name
|
||||
redis: redis
|
||||
name: name
|
||||
redis: redis
|
||||
config: osal_traefik.new_traefik_config()
|
||||
}
|
||||
|
||||
|
||||
// Set redis connection in config
|
||||
manager.config.redis = redis
|
||||
|
||||
|
||||
traefik_managers[name] = manager
|
||||
return manager
|
||||
}
|
||||
@@ -48,4 +48,4 @@ pub fn default() !&TraefikManager {
|
||||
return new(name: 'default')!
|
||||
}
|
||||
return get(name: 'default')!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ pub mut:
|
||||
@[params]
|
||||
pub struct RouterAddArgs {
|
||||
pub mut:
|
||||
name string @[required]
|
||||
rule string @[required]
|
||||
service string @[required]
|
||||
name string @[required]
|
||||
rule string @[required]
|
||||
service string @[required]
|
||||
entrypoints []string
|
||||
middlewares []string
|
||||
tls bool
|
||||
@@ -43,8 +43,8 @@ pub mut:
|
||||
@[params]
|
||||
pub struct MiddlewareAddArgs {
|
||||
pub mut:
|
||||
name string @[required]
|
||||
typ string @[required]
|
||||
name string @[required]
|
||||
typ string @[required]
|
||||
settings map[string]string
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ pub fn (mut tm TraefikManager) service_add(args ServiceAddArgs) ! {
|
||||
}
|
||||
|
||||
tm.config.add_service(
|
||||
name: texttools.name_fix(args.name)
|
||||
name: texttools.name_fix(args.name)
|
||||
load_balancer: osal_traefik.LoadBalancerConfig{
|
||||
servers: servers
|
||||
}
|
||||
@@ -101,7 +101,7 @@ pub fn (mut tm TraefikManager) entrypoint_add(args EntryPointAddArgs) ! {
|
||||
address: args.address
|
||||
tls: args.tls
|
||||
}
|
||||
|
||||
|
||||
// Check if entrypoint already exists
|
||||
for mut ep in tm.entrypoints {
|
||||
if ep.name == entrypoint.name {
|
||||
@@ -110,7 +110,7 @@ pub fn (mut tm TraefikManager) entrypoint_add(args EntryPointAddArgs) ! {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tm.entrypoints << entrypoint
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ pub fn (mut tm TraefikManager) entrypoint_add(args EntryPointAddArgs) ! {
|
||||
pub fn (mut tm TraefikManager) apply() ! {
|
||||
// Apply dynamic configuration (routers, services, middlewares)
|
||||
tm.config.set()!
|
||||
|
||||
|
||||
// Store entrypoints separately (these would typically be in static config)
|
||||
for ep in tm.entrypoints {
|
||||
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.redis = tm.redis
|
||||
tm.entrypoints = []EntryPointConfig{}
|
||||
|
||||
|
||||
// Clear Redis keys
|
||||
keys := tm.redis.keys('traefik/*')!
|
||||
for key in keys {
|
||||
@@ -151,4 +151,4 @@ pub fn (mut tm TraefikManager) status() !map[string]int {
|
||||
'middlewares': tm.config.middlewares.len
|
||||
'entrypoints': tm.entrypoints.len
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,44 +14,44 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
|
||||
// Process entrypoints first
|
||||
play_entrypoints(mut plbook, mut manager)!
|
||||
|
||||
|
||||
// Process services (before routers that might reference them)
|
||||
play_services(mut plbook, mut manager)!
|
||||
|
||||
|
||||
// Process middlewares (before routers that might reference them)
|
||||
play_middlewares(mut plbook, mut manager)!
|
||||
|
||||
|
||||
// Process routers
|
||||
play_routers(mut plbook, mut manager)!
|
||||
|
||||
|
||||
// Apply all configurations to Redis
|
||||
manager.apply()!
|
||||
|
||||
|
||||
console.print_debug('Traefik configuration applied successfully')
|
||||
}
|
||||
|
||||
fn play_entrypoints(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||
entrypoint_actions := plbook.find(filter: 'traefik.entrypoint')!
|
||||
|
||||
|
||||
for mut action in entrypoint_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
manager.entrypoint_add(
|
||||
name: p.get('name')!
|
||||
address: p.get('address')!
|
||||
tls: p.get_default_false('tls')
|
||||
)!
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
fn play_routers(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||
router_actions := plbook.find(filter: 'traefik.router')!
|
||||
|
||||
|
||||
for mut action in router_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
// Parse entrypoints list
|
||||
mut entrypoints := []string{}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parse middlewares list
|
||||
mut middlewares := []string{}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
manager.router_add(
|
||||
name: p.get('name')!
|
||||
rule: p.get('rule')!
|
||||
@@ -77,42 +77,42 @@ fn play_routers(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||
tls: p.get_default_false('tls')
|
||||
priority: p.get_int_default('priority', 0)
|
||||
)!
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
fn play_services(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||
service_actions := plbook.find(filter: 'traefik.service')!
|
||||
|
||||
|
||||
for mut action in service_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
// Parse servers list
|
||||
servers_str := p.get('servers')!
|
||||
servers := servers_str.split(',').map(it.trim_space())
|
||||
|
||||
|
||||
manager.service_add(
|
||||
name: p.get('name')!
|
||||
servers: servers
|
||||
strategy: p.get_default('strategy', 'wrr')!
|
||||
)!
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
fn play_middlewares(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||
middleware_actions := plbook.find(filter: 'traefik.middleware')!
|
||||
|
||||
|
||||
for mut action in middleware_actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
// Build settings map from remaining parameters
|
||||
mut settings := map[string]string{}
|
||||
|
||||
|
||||
middleware_type := p.get('type')!
|
||||
|
||||
|
||||
// Handle common middleware types
|
||||
match middleware_type {
|
||||
'basicAuth' {
|
||||
@@ -156,13 +156,13 @@ fn play_middlewares(mut plbook PlayBook, mut manager TraefikManager) ! {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
manager.middleware_add(
|
||||
name: p.get('name')!
|
||||
typ: middleware_type
|
||||
settings: settings
|
||||
)!
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ pub fn scan(args_ GeneratorArgs) ! {
|
||||
// now walk over all directories, find .heroscript
|
||||
mut pathroot := pathlib.get_dir(path: args.path, create: false)!
|
||||
mut plist := pathroot.list(
|
||||
recursive: true
|
||||
recursive: true
|
||||
ignore_default: false
|
||||
regex: ['.heroscript']
|
||||
regex: ['.heroscript']
|
||||
)!
|
||||
|
||||
for mut p in plist.paths {
|
||||
|
||||
@@ -8,12 +8,12 @@ import freeflowuniverse.herolib.ui.console
|
||||
@[params]
|
||||
pub struct ListArgs {
|
||||
pub mut:
|
||||
regex []string
|
||||
recursive bool = true
|
||||
regex []string
|
||||
recursive bool = true
|
||||
ignore_default bool = true // ignore files starting with . and _
|
||||
include_links bool // wether to include links in list
|
||||
dirs_only bool
|
||||
files_only bool
|
||||
include_links bool // wether to include links in list
|
||||
dirs_only bool
|
||||
files_only bool
|
||||
}
|
||||
|
||||
// the result of pathlist
|
||||
@@ -54,12 +54,12 @@ pub fn (mut path Path) list(args_ ListArgs) !PathList {
|
||||
r << re
|
||||
}
|
||||
mut args := ListArgsInternal{
|
||||
regex: r
|
||||
recursive: args_.recursive
|
||||
regex: r
|
||||
recursive: args_.recursive
|
||||
ignore_default: args_.ignore_default
|
||||
dirs_only: args_.dirs_only
|
||||
files_only: args_.files_only
|
||||
include_links: args_.include_links
|
||||
dirs_only: args_.dirs_only
|
||||
files_only: args_.files_only
|
||||
include_links: args_.include_links
|
||||
}
|
||||
paths := path.list_internal(args)!
|
||||
mut pl := PathList{
|
||||
@@ -72,12 +72,12 @@ pub fn (mut path Path) list(args_ ListArgs) !PathList {
|
||||
@[params]
|
||||
pub struct ListArgsInternal {
|
||||
mut:
|
||||
regex []regex.RE // only put files in which follow one of the regexes
|
||||
recursive bool = true
|
||||
regex []regex.RE // only put files in which follow one of the regexes
|
||||
recursive bool = true
|
||||
ignore_default bool = true // ignore files starting with . and _
|
||||
dirs_only bool
|
||||
files_only bool
|
||||
include_links bool
|
||||
dirs_only bool
|
||||
files_only bool
|
||||
include_links bool
|
||||
}
|
||||
|
||||
fn (mut path Path) list_internal(args ListArgsInternal) ![]Path {
|
||||
|
||||
@@ -40,12 +40,6 @@ pub fn (key SSHKey) private_key() !string {
|
||||
return content
|
||||
}
|
||||
|
||||
|
||||
module core
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
@[params]
|
||||
pub struct SSHConfig {
|
||||
pub:
|
||||
|
||||
@@ -24,6 +24,6 @@ pub:
|
||||
pub fn new(args LinuxNewArgs) !LinuxFactory {
|
||||
mut t := LinuxFactory{
|
||||
username: args.username
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
@@ -11,34 +11,34 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
|
||||
// Process user_create actions
|
||||
play_user_create(mut plbook, mut lf)!
|
||||
|
||||
|
||||
// Process user_delete actions
|
||||
play_user_delete(mut plbook, mut lf)!
|
||||
|
||||
|
||||
// Process sshkey_create actions
|
||||
play_sshkey_create(mut plbook, mut lf)!
|
||||
|
||||
|
||||
// Process sshkey_delete actions
|
||||
play_sshkey_delete(mut plbook, mut lf)!
|
||||
}
|
||||
|
||||
fn play_user_create(mut plbook PlayBook, mut lf LinuxFactory) ! {
|
||||
mut actions := plbook.find(filter: 'usermgmt.user_create')!
|
||||
|
||||
|
||||
for mut action in actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
mut args := UserCreateArgs{
|
||||
name: p.get('name')!
|
||||
giteakey: p.get_default('giteakey', '')!
|
||||
giteaurl: p.get_default('giteaurl', '')!
|
||||
passwd: p.get_default('passwd', '')!
|
||||
name: p.get('name')!
|
||||
giteakey: p.get_default('giteakey', '')!
|
||||
giteaurl: p.get_default('giteaurl', '')!
|
||||
passwd: p.get_default('passwd', '')!
|
||||
description: p.get_default('description', '')!
|
||||
email: p.get_default('email', '')!
|
||||
tel: p.get_default('tel', '')!
|
||||
sshkey: p.get_default('sshkey', '')! // SSH public key
|
||||
email: p.get_default('email', '')!
|
||||
tel: p.get_default('tel', '')!
|
||||
sshkey: p.get_default('sshkey', '')! // SSH public key
|
||||
}
|
||||
|
||||
|
||||
lf.user_create(args)!
|
||||
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) ! {
|
||||
mut actions := plbook.find(filter: 'usermgmt.user_delete')!
|
||||
|
||||
|
||||
for mut action in actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
mut args := UserDeleteArgs{
|
||||
name: p.get('name')!
|
||||
}
|
||||
|
||||
|
||||
lf.user_delete(args)!
|
||||
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) ! {
|
||||
mut actions := plbook.find(filter: 'usermgmt.sshkey_create')!
|
||||
|
||||
|
||||
for mut action in actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
mut args := SSHKeyCreateArgs{
|
||||
username: p.get('username')!
|
||||
username: p.get('username')!
|
||||
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', '')!
|
||||
}
|
||||
|
||||
|
||||
lf.sshkey_create(args)!
|
||||
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) ! {
|
||||
mut actions := plbook.find(filter: 'usermgmt.sshkey_delete')!
|
||||
|
||||
|
||||
for mut action in actions {
|
||||
mut p := action.params
|
||||
|
||||
|
||||
mut args := SSHKeyDeleteArgs{
|
||||
username: p.get('username')!
|
||||
username: p.get('username')!
|
||||
sshkey_name: p.get('sshkey_name')!
|
||||
}
|
||||
|
||||
|
||||
lf.sshkey_delete(args)!
|
||||
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
|
||||
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"
|
||||
@@ -119,7 +119,9 @@ pub fn (mut lf LinuxFactory) sshkey_create(args SSHKeyCreateArgs) ! {
|
||||
} else {
|
||||
// Generate new SSH key (modern ed25519)
|
||||
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}')
|
||||
}
|
||||
|
||||
@@ -175,12 +177,12 @@ fn (mut lf LinuxFactory) save_user_config(args UserCreateArgs) ! {
|
||||
}
|
||||
|
||||
new_config := UserConfig{
|
||||
name: args.name
|
||||
giteakey: args.giteakey
|
||||
giteaurl: args.giteaurl
|
||||
email: args.email
|
||||
name: args.name
|
||||
giteakey: args.giteakey
|
||||
giteaurl: args.giteaurl
|
||||
email: args.email
|
||||
description: args.description
|
||||
tel: args.tel
|
||||
tel: args.tel
|
||||
}
|
||||
|
||||
if found_idx >= 0 {
|
||||
@@ -201,7 +203,7 @@ fn (mut lf LinuxFactory) remove_user_config(username string) ! {
|
||||
config_path := '${config_dir}/myconfig.json'
|
||||
|
||||
if !os.exists(config_path) {
|
||||
return // Nothing to remove
|
||||
return
|
||||
}
|
||||
|
||||
content := osal.file_read(config_path)!
|
||||
@@ -243,7 +245,9 @@ fn (mut lf LinuxFactory) create_user_system(args UserCreateArgs) ! {
|
||||
|
||||
// Ensure ourworld group exists
|
||||
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 {
|
||||
console.print_item('➕ Creating group ourworld')
|
||||
@@ -284,58 +288,9 @@ fn (mut lf LinuxFactory) create_ssh_agent_profile(username string) ! {
|
||||
user_home := '/home/${username}'
|
||||
profile_script := '${user_home}/.profile_sshagent'
|
||||
|
||||
script_content := '# Auto-start ssh-agent if not running
|
||||
SSH_AGENT_PID_FILE="$HOME/.ssh/agent.pid"
|
||||
SSH_AUTH_SOCK_FILE="$HOME/.ssh/agent.sock"
|
||||
// script_content := ''
|
||||
|
||||
# Function to start ssh-agent
|
||||
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
|
||||
'
|
||||
panic('implement')
|
||||
|
||||
osal.file_write(profile_script, script_content)!
|
||||
osal.exec(cmd: 'chown ${username}:${username} ${profile_script}')!
|
||||
@@ -351,4 +306,4 @@ fi
|
||||
}
|
||||
|
||||
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
|
||||
fn agent_check(mut agent SSHAgent) ! {
|
||||
console.print_header('SSH Agent Check')
|
||||
|
||||
|
||||
// Ensure single agent is running
|
||||
agent.ensure_single_agent()!
|
||||
|
||||
|
||||
// Get diagnostics
|
||||
diag := agent.diagnostics()
|
||||
|
||||
|
||||
for key, value in diag {
|
||||
console.print_item('${key}: ${value}')
|
||||
}
|
||||
|
||||
|
||||
// Verify agent is responsive
|
||||
if !agent.is_agent_responsive() {
|
||||
return error('SSH agent is not responsive')
|
||||
}
|
||||
|
||||
|
||||
// Load all existing keys from ~/.ssh that aren't loaded yet
|
||||
agent.init()!
|
||||
|
||||
|
||||
console.print_green('✓ SSH Agent is properly configured and running')
|
||||
|
||||
|
||||
// Show loaded keys
|
||||
loaded_keys := agent.keys_loaded()!
|
||||
console.print_item('Loaded keys: ${loaded_keys.len}')
|
||||
@@ -35,17 +35,17 @@ fn agent_check(mut agent SSHAgent) ! {
|
||||
// Create a new SSH key
|
||||
fn sshkey_create(mut agent SSHAgent, name string, passphrase string) ! {
|
||||
console.print_header('Creating SSH key: ${name}')
|
||||
|
||||
|
||||
// Check if key already exists
|
||||
if agent.exists(name: name) {
|
||||
console.print_debug('SSH key "${name}" already exists')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Generate new key
|
||||
mut key := agent.generate(name, passphrase)!
|
||||
console.print_green('✓ SSH key "${name}" created successfully')
|
||||
|
||||
|
||||
// Automatically load the key
|
||||
key.load()!
|
||||
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
|
||||
fn sshkey_delete(mut agent SSHAgent, name string) ! {
|
||||
console.print_header('Deleting SSH key: ${name}')
|
||||
|
||||
|
||||
// Check if key exists
|
||||
mut key := agent.get(name: name) or {
|
||||
console.print_debug('SSH key "${name}" does not exist')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Get key paths before deletion
|
||||
key_path := key.keypath() or {
|
||||
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}"')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Remove from agent if loaded (temporarily disabled due to reset_ssh panic)
|
||||
// if key.loaded {
|
||||
// key.forget()!
|
||||
// }
|
||||
|
||||
|
||||
// Delete key files
|
||||
if key_path.exists() {
|
||||
key_path.delete()!
|
||||
@@ -85,26 +85,24 @@ fn sshkey_delete(mut agent SSHAgent, name string) ! {
|
||||
key_pub_path.delete()!
|
||||
console.print_debug('Deleted public key: ${key_pub_path.path}')
|
||||
}
|
||||
|
||||
|
||||
// Reinitialize agent to update key list
|
||||
agent.init()!
|
||||
|
||||
|
||||
console.print_green('✓ SSH key "${name}" deleted successfully')
|
||||
}
|
||||
|
||||
// Load SSH key into agent
|
||||
fn sshkey_load(mut agent SSHAgent, name string) ! {
|
||||
console.print_header('Loading SSH key: ${name}')
|
||||
|
||||
mut key := agent.get(name: name) or {
|
||||
return error('SSH key "${name}" not found')
|
||||
}
|
||||
|
||||
|
||||
mut key := agent.get(name: name) or { return error('SSH key "${name}" not found') }
|
||||
|
||||
if key.loaded {
|
||||
console.print_debug('SSH key "${name}" is already loaded')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
key.load()!
|
||||
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
|
||||
fn sshkey_check(mut agent SSHAgent, name string) ! {
|
||||
console.print_header('Checking SSH key: ${name}')
|
||||
|
||||
mut key := agent.get(name: name) or {
|
||||
return error('SSH key "${name}" not found')
|
||||
}
|
||||
|
||||
|
||||
mut key := agent.get(name: name) or { return error('SSH key "${name}" not found') }
|
||||
|
||||
// Check if key files exist
|
||||
key_path := key.keypath() or {
|
||||
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_path := key.keypath() or { 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}"') }
|
||||
|
||||
if !key_path.exists() {
|
||||
return error('Private key file does not exist: ${key_path.path}')
|
||||
}
|
||||
|
||||
|
||||
if !key_pub_path.exists() {
|
||||
return error('Public key file does not exist: ${key_pub_path.path}')
|
||||
}
|
||||
|
||||
|
||||
// Verify key can be loaded (if not already loaded)
|
||||
if !key.loaded {
|
||||
// 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}"')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.print_item('Key type: ${key.cat}')
|
||||
console.print_item('Loaded: ${key.loaded}')
|
||||
console.print_item('Email: ${key.email}')
|
||||
console.print_item('Private key: ${key_path.path}')
|
||||
console.print_item('Public key: ${key_pub_path.path}')
|
||||
|
||||
|
||||
console.print_green('✓ SSH key "${name}" is valid')
|
||||
}
|
||||
|
||||
// Copy private key to remote node
|
||||
fn remote_copy(mut agent SSHAgent, node_addr string, key_name string) ! {
|
||||
console.print_header('Copying SSH key "${key_name}" to ${node_addr}')
|
||||
|
||||
|
||||
// Get the key
|
||||
mut key := agent.get(name: key_name) or {
|
||||
return error('SSH key "${key_name}" not found')
|
||||
}
|
||||
|
||||
mut key := agent.get(name: key_name) or { return error('SSH key "${key_name}" not found') }
|
||||
|
||||
// Create builder node
|
||||
mut b := builder.new()!
|
||||
mut node := b.node_new(ipaddr: node_addr)!
|
||||
|
||||
|
||||
// Get private key content
|
||||
key_path := key.keypath()!
|
||||
if !key_path.exists() {
|
||||
return error('Private key file not found: ${key_path.path}')
|
||||
}
|
||||
|
||||
|
||||
private_key_content := key_path.read()!
|
||||
|
||||
|
||||
// Get home directory on remote
|
||||
home_dir := node.environ_get()!['HOME'] or {
|
||||
return error('Could not determine HOME directory on remote node')
|
||||
}
|
||||
|
||||
|
||||
remote_ssh_dir := '${home_dir}/.ssh'
|
||||
remote_key_path := '${remote_ssh_dir}/${key_name}'
|
||||
|
||||
|
||||
// Ensure .ssh directory exists with correct permissions
|
||||
node.exec_silent('mkdir -p ${remote_ssh_dir}')!
|
||||
node.exec_silent('chmod 700 ${remote_ssh_dir}')!
|
||||
|
||||
|
||||
// Copy private key to remote
|
||||
node.file_write(remote_key_path, private_key_content)!
|
||||
node.exec_silent('chmod 600 ${remote_key_path}')!
|
||||
|
||||
|
||||
// Generate public key on remote
|
||||
node.exec_silent('ssh-keygen -y -f ${remote_key_path} > ${remote_key_path}.pub')!
|
||||
node.exec_silent('chmod 644 ${remote_key_path}.pub')!
|
||||
|
||||
|
||||
console.print_green('✓ SSH key "${key_name}" copied to ${node_addr}')
|
||||
}
|
||||
|
||||
// Add public key to authorized_keys on remote node
|
||||
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}')
|
||||
|
||||
|
||||
// Create builder node
|
||||
mut b := builder.new()!
|
||||
mut node := b.node_new(ipaddr: node_addr)!
|
||||
|
||||
|
||||
// Use existing builder integration
|
||||
agent.push_key_to_node(mut node, key_name)!
|
||||
|
||||
|
||||
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' {
|
||||
return error('Can only push keys to SSH nodes, got: ${node_info['category']}')
|
||||
}
|
||||
|
||||
|
||||
// Find the key
|
||||
mut key := agent.get(name: key_name) or {
|
||||
return error('SSH key "${key_name}" not found in agent')
|
||||
}
|
||||
|
||||
|
||||
// Get public key content
|
||||
pubkey_content := key.keypub()!
|
||||
|
||||
|
||||
// Check if authorized_keys file exists on remote
|
||||
home_dir := node.environ_get()!['HOME'] or {
|
||||
return error('Could not determine HOME directory on remote node')
|
||||
}
|
||||
|
||||
|
||||
ssh_dir := '${home_dir}/.ssh'
|
||||
authorized_keys_path := '${ssh_dir}/authorized_keys'
|
||||
|
||||
|
||||
// Ensure .ssh directory exists with correct permissions
|
||||
node.exec_silent('mkdir -p ${ssh_dir}')!
|
||||
node.exec_silent('chmod 700 ${ssh_dir}')!
|
||||
|
||||
|
||||
// Check if key already exists
|
||||
if node.file_exists(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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add key to authorized_keys
|
||||
node.exec_silent('echo "${pubkey_content}" >> ${authorized_keys_path}')!
|
||||
node.exec_silent('chmod 600 ${authorized_keys_path}')!
|
||||
|
||||
|
||||
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' {
|
||||
return error('Can only remove keys from SSH nodes, got: ${node_info['category']}')
|
||||
}
|
||||
|
||||
|
||||
// Find the key
|
||||
mut key := agent.get(name: key_name) or {
|
||||
return error('SSH key "${key_name}" not found in agent')
|
||||
}
|
||||
|
||||
|
||||
// Get public key content
|
||||
pubkey_content := key.keypub()!
|
||||
|
||||
|
||||
// Get authorized_keys path
|
||||
home_dir := node.environ_get()!['HOME'] or {
|
||||
return error('Could not determine HOME directory on remote node')
|
||||
}
|
||||
|
||||
|
||||
authorized_keys_path := '${home_dir}/.ssh/authorized_keys'
|
||||
|
||||
|
||||
if !node.file_exists(authorized_keys_path) {
|
||||
console.print_debug('authorized_keys file does not exist on remote node')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Remove the key line from authorized_keys
|
||||
escaped_key := pubkey_content.replace('/', '\\/')
|
||||
node.exec_silent('sed -i "\\|${escaped_key}|d" ${authorized_keys_path}')!
|
||||
|
||||
|
||||
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' {
|
||||
return error('Can only verify access to SSH nodes')
|
||||
}
|
||||
|
||||
|
||||
// Test basic connectivity
|
||||
result := node.exec_silent('echo "SSH key verification successful"') or {
|
||||
return false
|
||||
}
|
||||
|
||||
result := node.exec_silent('echo "SSH key verification successful"') or { return false }
|
||||
|
||||
return result.contains('SSH key verification successful')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
passphrase := p.get_default('passphrase', '')!
|
||||
|
||||
|
||||
sshkey_create(mut agent, name, passphrase)!
|
||||
action.done = true
|
||||
}
|
||||
@@ -35,7 +35,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
for mut action in delete_actions {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
|
||||
|
||||
sshkey_delete(mut agent, name)!
|
||||
action.done = true
|
||||
}
|
||||
@@ -45,7 +45,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
for mut action in load_actions {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
|
||||
|
||||
sshkey_load(mut agent, name)!
|
||||
action.done = true
|
||||
}
|
||||
@@ -55,7 +55,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
for mut action in check_key_actions {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
|
||||
|
||||
sshkey_check(mut agent, name)!
|
||||
action.done = true
|
||||
}
|
||||
@@ -66,7 +66,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut p := action.params
|
||||
node_addr := p.get('node')!
|
||||
key_name := p.get('name')!
|
||||
|
||||
|
||||
remote_copy(mut agent, node_addr, key_name)!
|
||||
action.done = true
|
||||
}
|
||||
@@ -77,7 +77,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
mut p := action.params
|
||||
node_addr := p.get('node')!
|
||||
key_name := p.get('name')!
|
||||
|
||||
|
||||
remote_auth(mut agent, node_addr, key_name)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
@@ -16,19 +16,19 @@ pub mut:
|
||||
pub fn (mut agent SSHAgent) ensure_single_agent() ! {
|
||||
user := os.getenv('USER')
|
||||
socket_path := get_agent_socket_path(user)
|
||||
|
||||
|
||||
// Check if we have a valid agent already
|
||||
if agent.is_agent_responsive() {
|
||||
console.print_debug('SSH agent already running and responsive')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Kill any orphaned agents
|
||||
agent.cleanup_orphaned_agents()!
|
||||
|
||||
|
||||
// Start new agent with consistent socket
|
||||
agent.start_agent_with_socket(socket_path)!
|
||||
|
||||
|
||||
// Set environment variables
|
||||
os.setenv('SSH_AUTH_SOCK', socket_path, true)
|
||||
agent.active = true
|
||||
@@ -44,7 +44,7 @@ pub fn (mut agent SSHAgent) is_agent_responsive() bool {
|
||||
if os.getenv('SSH_AUTH_SOCK') == '' {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
@@ -52,12 +52,12 @@ pub fn (mut agent SSHAgent) is_agent_responsive() bool {
|
||||
// cleanup orphaned ssh-agent processes
|
||||
pub fn (mut agent SSHAgent) cleanup_orphaned_agents() ! {
|
||||
user := os.getenv('USER')
|
||||
|
||||
|
||||
// Find ssh-agent processes for current user
|
||||
res := os.execute('pgrep -u ${user} ssh-agent')
|
||||
if res.exit_code == 0 && res.output.len > 0 {
|
||||
pids := res.output.trim_space().split('\n')
|
||||
|
||||
|
||||
for pid in pids {
|
||||
if pid.trim_space() != '' {
|
||||
// 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 {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
for socket_path in res.output.split('\n') {
|
||||
if socket_path.trim_space() != '' {
|
||||
// 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)
|
||||
test_res := os.execute('ssh-add -l 2>/dev/null')
|
||||
os.setenv('SSH_AUTH_SOCK', old_sock, true)
|
||||
|
||||
|
||||
if test_res.exit_code == 0 || test_res.exit_code == 1 {
|
||||
return true
|
||||
}
|
||||
@@ -100,45 +100,45 @@ pub fn (mut agent SSHAgent) start_agent_with_socket(socket_path string) ! {
|
||||
if os.exists(socket_path) {
|
||||
os.rm(socket_path)!
|
||||
}
|
||||
|
||||
|
||||
// Start ssh-agent with specific socket
|
||||
cmd := 'ssh-agent -a ${socket_path}'
|
||||
res := os.execute(cmd)
|
||||
if res.exit_code != 0 {
|
||||
return error('Failed to start ssh-agent: ${res.output}')
|
||||
}
|
||||
|
||||
|
||||
// Verify socket was created
|
||||
if !os.exists(socket_path) {
|
||||
return error('SSH agent socket was not created at ${socket_path}')
|
||||
}
|
||||
|
||||
|
||||
// Set environment variable
|
||||
os.setenv('SSH_AUTH_SOCK', socket_path, true)
|
||||
|
||||
|
||||
// Verify agent is responsive
|
||||
if !agent.is_agent_responsive() {
|
||||
return error('SSH agent started but is not responsive')
|
||||
}
|
||||
|
||||
|
||||
console.print_debug('SSH agent started with socket: ${socket_path}')
|
||||
}
|
||||
|
||||
// get agent status and diagnostics
|
||||
pub fn (mut agent SSHAgent) diagnostics() map[string]string {
|
||||
mut diag := map[string]string{}
|
||||
|
||||
|
||||
diag['socket_path'] = os.getenv('SSH_AUTH_SOCK')
|
||||
diag['socket_exists'] = os.exists(diag['socket_path']).str()
|
||||
diag['agent_responsive'] = agent.is_agent_responsive().str()
|
||||
diag['loaded_keys_count'] = agent.keys.filter(it.loaded).len.str()
|
||||
diag['total_keys_count'] = agent.keys.len.str()
|
||||
|
||||
|
||||
// Count running ssh-agent processes
|
||||
user := os.getenv('USER')
|
||||
res := os.execute('pgrep -u ${user} ssh-agent | wc -l')
|
||||
diag['agent_processes'] = if res.exit_code == 0 { res.output.trim_space() } else { '0' }
|
||||
|
||||
|
||||
return diag
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
|
||||
// Create tmux instance
|
||||
mut tmux_instance := new()!
|
||||
|
||||
|
||||
// Start tmux if not running
|
||||
if !tmux_instance.is_running()! {
|
||||
tmux_instance.start()!
|
||||
@@ -44,7 +44,7 @@ fn parse_window_name(name string) !ParsedWindowName {
|
||||
}
|
||||
return ParsedWindowName{
|
||||
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{
|
||||
session: texttools.name_fix(parts[0])
|
||||
window: texttools.name_fix(parts[1])
|
||||
pane: texttools.name_fix(parts[2])
|
||||
window: texttools.name_fix(parts[1])
|
||||
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
|
||||
session_name := p.get('name')!
|
||||
reset := p.get_default_false('reset')
|
||||
|
||||
|
||||
tmux_instance.session_create(
|
||||
name: session_name
|
||||
name: session_name
|
||||
reset: reset
|
||||
)!
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
@@ -81,9 +81,9 @@ fn play_session_delete(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
||||
for mut action in actions {
|
||||
mut p := action.params
|
||||
session_name := p.get('name')!
|
||||
|
||||
|
||||
tmux_instance.session_delete(session_name)!
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ fn play_window_create(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
||||
parsed := parse_window_name(name)!
|
||||
cmd := p.get_default('cmd', '')!
|
||||
reset := p.get_default_false('reset')
|
||||
|
||||
|
||||
// Parse environment variables if provided
|
||||
mut env := map[string]string{}
|
||||
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
|
||||
mut session := if tmux_instance.session_exist(parsed.session) {
|
||||
tmux_instance.session_get(parsed.session)!
|
||||
} else {
|
||||
tmux_instance.session_create(name: parsed.session)!
|
||||
}
|
||||
|
||||
|
||||
session.window_new(
|
||||
name: parsed.window
|
||||
cmd: cmd
|
||||
env: env
|
||||
name: parsed.window
|
||||
cmd: cmd
|
||||
env: env
|
||||
reset: reset
|
||||
)!
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
@@ -134,12 +134,12 @@ fn play_window_delete(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
parsed := parse_window_name(name)!
|
||||
|
||||
|
||||
if tmux_instance.session_exist(parsed.session) {
|
||||
mut session := tmux_instance.session_get(parsed.session)!
|
||||
session.window_delete(name: parsed.window)!
|
||||
}
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
@@ -151,19 +151,19 @@ fn play_pane_execute(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
||||
name := p.get('name')!
|
||||
cmd := p.get('cmd')!
|
||||
parsed := parse_pane_name(name)!
|
||||
|
||||
|
||||
// Find the session and window
|
||||
if tmux_instance.session_exist(parsed.session) {
|
||||
mut session := tmux_instance.session_get(parsed.session)!
|
||||
if session.window_exist(name: parsed.window) {
|
||||
mut window := session.window_get(name: parsed.window)!
|
||||
|
||||
|
||||
// Send command to the window (goes to active pane by default)
|
||||
tmux_cmd := 'tmux send-keys -t ${session.name}:@${window.id} "${cmd}" Enter'
|
||||
osal.exec(cmd: tmux_cmd, stdout: false, name: 'tmux_pane_execute')!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
@@ -174,21 +174,26 @@ fn play_pane_kill(mut plbook PlayBook, mut tmux_instance Tmux) ! {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
parsed := parse_pane_name(name)!
|
||||
|
||||
|
||||
// Find the session and window, then kill the active pane
|
||||
if tmux_instance.session_exist(parsed.session) {
|
||||
mut session := tmux_instance.session_get(parsed.session)!
|
||||
if session.window_exist(name: parsed.window) {
|
||||
mut window := session.window_get(name: parsed.window)!
|
||||
|
||||
|
||||
// Kill the active pane in the window
|
||||
if pane := window.pane_active() {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ pub mut:
|
||||
sessionid string // unique link to job
|
||||
}
|
||||
|
||||
|
||||
// get session (session has windows) .
|
||||
// returns none if not found
|
||||
pub fn (mut t Tmux) session_get(name_ string) !&Session {
|
||||
@@ -56,8 +55,6 @@ pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
|
||||
|
||||
// create session, if reset will re-create
|
||||
pub fn (mut t Tmux) session_create(args SessionCreateArgs) !&Session {
|
||||
name := texttools.name_fix(args.name)
|
||||
@@ -83,7 +80,6 @@ pub fn (mut t Tmux) session_create(args SessionCreateArgs) !&Session {
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@[params]
|
||||
pub struct TmuxNewArgs {
|
||||
sessionid string
|
||||
@@ -116,17 +112,16 @@ pub fn (mut t Tmux) window_new(args WindowNewArgs) !&Window {
|
||||
} else {
|
||||
t.session_create(name: args.session_name)!
|
||||
}
|
||||
|
||||
|
||||
// Create window in session
|
||||
return session.window_new(
|
||||
name: args.name
|
||||
cmd: args.cmd
|
||||
env: args.env
|
||||
name: args.name
|
||||
cmd: args.cmd
|
||||
env: args.env
|
||||
reset: args.reset
|
||||
)!
|
||||
}
|
||||
|
||||
|
||||
pub fn (mut t Tmux) stop() ! {
|
||||
$if debug {
|
||||
console.print_debug('Stopping tmux...')
|
||||
@@ -156,7 +151,6 @@ pub fn (mut t Tmux) start() ! {
|
||||
t.scan()!
|
||||
}
|
||||
|
||||
|
||||
// print list of tmux sessions
|
||||
pub fn (mut t Tmux) list_print() {
|
||||
// os.log('TMUX - Start listing ....')
|
||||
|
||||
@@ -10,141 +10,134 @@ import freeflowuniverse.herolib.ui.console
|
||||
@[heap]
|
||||
struct Pane {
|
||||
pub mut:
|
||||
window &Window @[str: skip]
|
||||
id int // pane id (e.g., %1, %2)
|
||||
pid int // process id
|
||||
active bool // is this the active pane
|
||||
cmd string // command running in pane
|
||||
env map[string]string
|
||||
created_at time.Time
|
||||
last_output_offset int // for tracking new logs
|
||||
window &Window @[str: skip]
|
||||
id int // pane id (e.g., %1, %2)
|
||||
pid int // process id
|
||||
active bool // is this the active pane
|
||||
cmd string // command running in pane
|
||||
env map[string]string
|
||||
created_at time.Time
|
||||
last_output_offset int // for tracking new logs
|
||||
}
|
||||
|
||||
|
||||
pub fn (mut p Pane) stats() !ProcessStats {
|
||||
if p.pid == 0 {
|
||||
return ProcessStats{}
|
||||
}
|
||||
if p.pid == 0 {
|
||||
return ProcessStats{}
|
||||
}
|
||||
|
||||
// Use ps command to get CPU and memory stats
|
||||
cmd := 'ps -p ${p.pid} -o %cpu,%mem,rss --no-headers'
|
||||
result := osal.execute_silent(cmd) or {
|
||||
return error('Cannot get stats for PID ${p.pid}: ${err}')
|
||||
}
|
||||
// Use ps command to get CPU and memory stats
|
||||
cmd := 'ps -p ${p.pid} -o %cpu,%mem,rss --no-headers'
|
||||
result := osal.execute_silent(cmd) or {
|
||||
return error('Cannot get stats for PID ${p.pid}: ${err}')
|
||||
}
|
||||
|
||||
if result.trim_space() == '' {
|
||||
return error('Process ${p.pid} not found')
|
||||
}
|
||||
if result.trim_space() == '' {
|
||||
return error('Process ${p.pid} not found')
|
||||
}
|
||||
|
||||
parts := result.trim_space().split_any(' \t').filter(it != '')
|
||||
if parts.len < 3 {
|
||||
return error('Invalid ps output: ${result}')
|
||||
}
|
||||
parts := result.trim_space().split_any(' \t').filter(it != '')
|
||||
if parts.len < 3 {
|
||||
return error('Invalid ps output: ${result}')
|
||||
}
|
||||
|
||||
return ProcessStats{
|
||||
cpu_percent: parts[0].f64()
|
||||
memory_percent: parts[1].f64()
|
||||
memory_bytes: parts[2].u64() * 1024 // ps returns KB, convert to bytes
|
||||
}
|
||||
return ProcessStats{
|
||||
cpu_percent: parts[0].f64()
|
||||
memory_percent: parts[1].f64()
|
||||
memory_bytes: parts[2].u64() * 1024 // ps returns KB, convert to bytes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct TMuxLogEntry {
|
||||
pub mut:
|
||||
content string
|
||||
timestamp time.Time
|
||||
offset int
|
||||
content string
|
||||
timestamp time.Time
|
||||
offset int
|
||||
}
|
||||
|
||||
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{
|
||||
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}')
|
||||
}
|
||||
lines := result.split_into_lines()
|
||||
mut entries := []TMuxLogEntry{}
|
||||
|
||||
lines := result.split_into_lines()
|
||||
mut entries := []TMuxLogEntry{}
|
||||
|
||||
mut i:= 0
|
||||
for line in lines {
|
||||
if line.trim_space() != '' {
|
||||
entries << TMuxLogEntry{
|
||||
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
|
||||
}
|
||||
return entries
|
||||
mut i := 0
|
||||
for line in lines {
|
||||
if line.trim_space() != '' {
|
||||
entries << TMuxLogEntry{
|
||||
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
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
pub fn (mut p Pane) exit_status() !ProcessStatus {
|
||||
// Get the last few lines to see if there's an exit status
|
||||
logs := p.logs_all()!
|
||||
lines := logs.split_into_lines()
|
||||
// Get the last few lines to see if there's an exit status
|
||||
logs := p.logs_all()!
|
||||
lines := logs.split_into_lines()
|
||||
|
||||
// Look for shell prompt indicating command finished
|
||||
for line in lines.reverse() {
|
||||
line_clean := line.trim_space()
|
||||
if line_clean.contains('$') || line_clean.contains('#') || line_clean.contains('>') {
|
||||
// Found shell prompt, command likely finished
|
||||
// Could also check for specific exit codes in history
|
||||
return .finished_ok
|
||||
}
|
||||
}
|
||||
return .finished_error
|
||||
// Look for shell prompt indicating command finished
|
||||
for line in lines.reverse() {
|
||||
line_clean := line.trim_space()
|
||||
if line_clean.contains('$') || line_clean.contains('#') || line_clean.contains('>') {
|
||||
// Found shell prompt, command likely finished
|
||||
// Could also check for specific exit codes in history
|
||||
return .finished_ok
|
||||
}
|
||||
}
|
||||
return .finished_error
|
||||
}
|
||||
|
||||
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'
|
||||
return osal.execute_silent(cmd) or {
|
||||
error('Cannot capture pane output: ${err}')
|
||||
}
|
||||
cmd := 'tmux capture-pane -t ${p.window.session.name}:@${p.window.id}.%${p.id} -S -2000 -p'
|
||||
return osal.execute_silent(cmd) or { error('Cannot capture pane output: ${err}') }
|
||||
}
|
||||
|
||||
// Fix the output_wait method to use correct method name
|
||||
pub fn (mut p Pane) output_wait(c_ string, timeoutsec int) ! {
|
||||
mut t := ourtime.now()
|
||||
start := t.unix()
|
||||
c := c_.replace('\n', '')
|
||||
for i in 0 .. 2000 {
|
||||
entries := p.logs_get_new(reset: false)!
|
||||
for entry in entries {
|
||||
if entry.content.replace('\n', '').contains(c) {
|
||||
return
|
||||
}
|
||||
}
|
||||
mut t2 := ourtime.now()
|
||||
if t2.unix() > start + timeoutsec {
|
||||
return error('timeout on output wait for tmux.\n${p} .\nwaiting for:\n${c}')
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
mut t := ourtime.now()
|
||||
start := t.unix()
|
||||
c := c_.replace('\n', '')
|
||||
for i in 0 .. 2000 {
|
||||
entries := p.logs_get_new(reset: false)!
|
||||
for entry in entries {
|
||||
if entry.content.replace('\n', '').contains(c) {
|
||||
return
|
||||
}
|
||||
}
|
||||
mut t2 := ourtime.now()
|
||||
if t2.unix() > start + timeoutsec {
|
||||
return error('timeout on output wait for tmux.\n${p} .\nwaiting for:\n${c}')
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Get process information for this pane and all its children
|
||||
pub fn (mut p Pane) processinfo() !osal.ProcessMap {
|
||||
if p.pid == 0 {
|
||||
return error('Pane has no associated process (pid is 0)')
|
||||
}
|
||||
|
||||
return osal.processinfo_with_children(p.pid)!
|
||||
if p.pid == 0 {
|
||||
return error('Pane has no associated process (pid is 0)')
|
||||
}
|
||||
|
||||
return osal.processinfo_with_children(p.pid)!
|
||||
}
|
||||
|
||||
// Get process information for just this pane's main process
|
||||
pub fn (mut p Pane) processinfo_main() !osal.ProcessInfo {
|
||||
if p.pid == 0 {
|
||||
return error('Pane has no associated process (pid is 0)')
|
||||
}
|
||||
|
||||
return osal.processinfo_get(p.pid)!
|
||||
if p.pid == 0 {
|
||||
return error('Pane has no associated process (pid is 0)')
|
||||
}
|
||||
|
||||
return osal.processinfo_get(p.pid)!
|
||||
}
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
module tmux
|
||||
|
||||
|
||||
|
||||
pub struct ProcessStats {
|
||||
pub mut:
|
||||
cpu_percent f64
|
||||
memory_bytes u64
|
||||
memory_percent f64
|
||||
cpu_percent f64
|
||||
memory_bytes u64
|
||||
memory_percent f64
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum ProcessStatus {
|
||||
running
|
||||
finished_ok
|
||||
finished_error
|
||||
not_found
|
||||
}
|
||||
|
||||
|
||||
running
|
||||
finished_ok
|
||||
finished_error
|
||||
not_found
|
||||
}
|
||||
|
||||
@@ -6,66 +6,66 @@ import freeflowuniverse.herolib.ui.console
|
||||
import time
|
||||
|
||||
fn (mut t Tmux) scan_add(line string) !&Pane {
|
||||
// Parse the line to get session, window, and pane info
|
||||
line_arr := line.split('|')
|
||||
session_name := line_arr[0]
|
||||
window_name := line_arr[1]
|
||||
window_id := line_arr[2]
|
||||
pane_active := line_arr[3]
|
||||
pane_id := line_arr[4]
|
||||
pane_pid := line_arr[5]
|
||||
pane_start_command := line_arr[6] or { '' }
|
||||
// Parse the line to get session, window, and pane info
|
||||
line_arr := line.split('|')
|
||||
session_name := line_arr[0]
|
||||
window_name := line_arr[1]
|
||||
window_id := line_arr[2]
|
||||
pane_active := line_arr[3]
|
||||
pane_id := line_arr[4]
|
||||
pane_pid := line_arr[5]
|
||||
pane_start_command := line_arr[6] or { '' }
|
||||
|
||||
wid := (window_id.replace('@', '')).int()
|
||||
pid := (pane_id.replace('%', '')).int()
|
||||
wid := (window_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
|
||||
mut w := if s.window_exist(name: window_name, id: wid) {
|
||||
s.window_get(name: window_name, id: wid)!
|
||||
} else {
|
||||
mut new_w := Window{
|
||||
session: s
|
||||
name: texttools.name_fix(window_name)
|
||||
id: wid
|
||||
panes: []&Pane{}
|
||||
}
|
||||
s.windows << &new_w
|
||||
&new_w
|
||||
}
|
||||
// Get or create window
|
||||
mut w := if s.window_exist(name: window_name, id: wid) {
|
||||
s.window_get(name: window_name, id: wid)!
|
||||
} else {
|
||||
mut new_w := Window{
|
||||
session: s
|
||||
name: texttools.name_fix(window_name)
|
||||
id: wid
|
||||
panes: []&Pane{}
|
||||
}
|
||||
s.windows << &new_w
|
||||
&new_w
|
||||
}
|
||||
|
||||
// Create or update pane
|
||||
mut p := Pane{
|
||||
window: w
|
||||
id: pid
|
||||
pid: pane_pid.int()
|
||||
active: pane_active == '1'
|
||||
cmd: pane_start_command
|
||||
created_at: time.now()
|
||||
}
|
||||
// Create or update pane
|
||||
mut p := Pane{
|
||||
window: w
|
||||
id: pid
|
||||
pid: pane_pid.int()
|
||||
active: pane_active == '1'
|
||||
cmd: pane_start_command
|
||||
created_at: time.now()
|
||||
}
|
||||
|
||||
// Check if pane already exists
|
||||
mut found := false
|
||||
for mut existing_pane in w.panes {
|
||||
if existing_pane.id == pid {
|
||||
existing_pane.pid = p.pid
|
||||
existing_pane.active = p.active
|
||||
existing_pane.cmd = p.cmd
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check if pane already exists
|
||||
mut found := false
|
||||
for mut existing_pane in w.panes {
|
||||
if existing_pane.id == pid {
|
||||
existing_pane.pid = p.pid
|
||||
existing_pane.active = p.active
|
||||
existing_pane.cmd = p.cmd
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
w.panes << &p
|
||||
}
|
||||
if !found {
|
||||
w.panes << &p
|
||||
}
|
||||
|
||||
return &p
|
||||
return &p
|
||||
}
|
||||
|
||||
// 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() ! {
|
||||
// os.log('TMUX - Scanning ....')
|
||||
|
||||
|
||||
@@ -21,87 +21,86 @@ pub mut:
|
||||
env map[string]string
|
||||
reset bool
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct WindowGetArgs {
|
||||
pub mut:
|
||||
name string
|
||||
id int
|
||||
name string
|
||||
id int
|
||||
}
|
||||
|
||||
|
||||
pub fn (mut s Session) create() ! {
|
||||
// Check if session already exists
|
||||
cmd_check := "tmux has-session -t ${s.name}"
|
||||
check_result := osal.exec(cmd: cmd_check, stdout: false, ignore_error: true) or {
|
||||
// Session doesn't exist, this is expected
|
||||
osal.Job{}
|
||||
}
|
||||
|
||||
if check_result.exit_code == 0 {
|
||||
return error('duplicate session: ${s.name}')
|
||||
}
|
||||
|
||||
// Create new session
|
||||
cmd := "tmux new-session -d -s ${s.name}"
|
||||
osal.exec(cmd: cmd, stdout: false, name: 'tmux_session_create') or {
|
||||
return error("Can't create session ${s.name}: ${err}")
|
||||
}
|
||||
// Check if session already exists
|
||||
cmd_check := 'tmux has-session -t ${s.name}'
|
||||
check_result := osal.exec(cmd: cmd_check, stdout: false, ignore_error: true) or {
|
||||
// Session doesn't exist, this is expected
|
||||
osal.Job{}
|
||||
}
|
||||
|
||||
if check_result.exit_code == 0 {
|
||||
return error('duplicate session: ${s.name}')
|
||||
}
|
||||
|
||||
// Create new session
|
||||
cmd := 'tmux new-session -d -s ${s.name}'
|
||||
osal.exec(cmd: cmd, stdout: false, name: 'tmux_session_create') or {
|
||||
return error("Can't create session ${s.name}: ${err}")
|
||||
}
|
||||
}
|
||||
|
||||
//load info from reality
|
||||
// load info from reality
|
||||
pub fn (mut s Session) scan() ! {
|
||||
// Get current windows from tmux for this session
|
||||
cmd := "tmux list-windows -t ${s.name} -F '#{window_name}|#{window_id}|#{window_active}'"
|
||||
result := osal.execute_silent(cmd) or {
|
||||
if err.msg().contains('session not found') {
|
||||
return // Session doesn't exist anymore
|
||||
}
|
||||
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)
|
||||
}
|
||||
// Get current windows from tmux for this session
|
||||
cmd := "tmux list-windows -t ${s.name} -F '#{window_name}|#{window_id}|#{window_active}'"
|
||||
result := osal.execute_silent(cmd) or {
|
||||
if err.msg().contains('session not found') {
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// window_name is the name of the window in session main (will always be called session main)
|
||||
// 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
|
||||
w.create(args.cmd)!
|
||||
s.scan()!
|
||||
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// get all windows as found in a session
|
||||
pub fn (mut s Session) windows_get() []&Window {
|
||||
mut res := []&Window{}
|
||||
@@ -179,14 +174,14 @@ pub fn (mut s Session) str() string {
|
||||
}
|
||||
|
||||
pub fn (mut s Session) stats() !ProcessStats {
|
||||
mut total := ProcessStats{}
|
||||
for mut window in s.windows {
|
||||
stats := window.stats() or { continue }
|
||||
total.cpu_percent += stats.cpu_percent
|
||||
total.memory_bytes += stats.memory_bytes
|
||||
total.memory_percent += stats.memory_percent
|
||||
}
|
||||
return total
|
||||
mut total := ProcessStats{}
|
||||
for mut window in s.windows {
|
||||
stats := window.stats() or { continue }
|
||||
total.cpu_percent += stats.cpu_percent
|
||||
total.memory_bytes += stats.memory_bytes
|
||||
total.memory_percent += stats.memory_percent
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// 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 {
|
||||
mut args := args_
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
pub fn (mut s Session) restart() ! {
|
||||
s.stop()!
|
||||
s.create()!
|
||||
@@ -259,4 +251,4 @@ pub fn (mut s Session) stop() ! {
|
||||
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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,37 +45,37 @@ fn test_stop() ! {
|
||||
}
|
||||
|
||||
fn test_windows_get() ! {
|
||||
mut tmux := new()!
|
||||
tmux.start()!
|
||||
|
||||
// After start, scan to get the initial session
|
||||
tmux.scan()!
|
||||
|
||||
windows := tmux.windows_get()
|
||||
assert windows.len >= 0 // At least the default session should exist
|
||||
|
||||
tmux.stop()!
|
||||
mut tmux := new()!
|
||||
tmux.start()!
|
||||
|
||||
// After start, scan to get the initial session
|
||||
tmux.scan()!
|
||||
|
||||
windows := tmux.windows_get()
|
||||
assert windows.len >= 0 // At least the default session should exist
|
||||
|
||||
tmux.stop()!
|
||||
}
|
||||
|
||||
fn test_scan() ! {
|
||||
console.print_debug('-----Testing scan------')
|
||||
mut tmux := new()!
|
||||
tmux.start()!
|
||||
console.print_debug('-----Testing scan------')
|
||||
mut tmux := new()!
|
||||
tmux.start()!
|
||||
|
||||
// Test initial scan
|
||||
tmux.scan()!
|
||||
sessions_before := tmux.sessions.len
|
||||
|
||||
// Create a test session
|
||||
mut session := tmux.session_create(name: 'test_scan')!
|
||||
|
||||
// Scan again
|
||||
tmux.scan()!
|
||||
sessions_after := tmux.sessions.len
|
||||
|
||||
assert sessions_after >= sessions_before
|
||||
|
||||
tmux.stop()!
|
||||
// Test initial scan
|
||||
tmux.scan()!
|
||||
sessions_before := tmux.sessions.len
|
||||
|
||||
// Create a test session
|
||||
mut session := tmux.session_create(name: 'test_scan')!
|
||||
|
||||
// Scan again
|
||||
tmux.scan()!
|
||||
sessions_after := tmux.sessions.len
|
||||
|
||||
assert sessions_after >= sessions_before
|
||||
|
||||
tmux.stop()!
|
||||
}
|
||||
|
||||
// //TODO: fix test
|
||||
|
||||
@@ -13,7 +13,7 @@ pub mut:
|
||||
session &Session @[skip]
|
||||
name string
|
||||
id int
|
||||
panes []&Pane // windows contain multiple panes
|
||||
panes []&Pane // windows contain multiple panes
|
||||
active bool
|
||||
env map[string]string
|
||||
}
|
||||
@@ -22,105 +22,104 @@ pub mut:
|
||||
pub struct PaneNewArgs {
|
||||
pub mut:
|
||||
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
|
||||
env map[string]string
|
||||
env map[string]string
|
||||
}
|
||||
|
||||
|
||||
pub fn (mut w Window) scan() ! {
|
||||
// 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}'"
|
||||
result := osal.execute_silent(cmd) or {
|
||||
// Window might not exist anymore
|
||||
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)
|
||||
}
|
||||
// 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}'"
|
||||
result := osal.execute_silent(cmd) or {
|
||||
// Window might not exist anymore
|
||||
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)
|
||||
}
|
||||
|
||||
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) ! {
|
||||
mut final_cmd := cmd_
|
||||
if cmd_.contains('\n') {
|
||||
os.mkdir_all('/tmp/tmux/${w.session.name}')!
|
||||
// Fix: osal.exec_string doesn't exist, use file writing instead
|
||||
script_path := '/tmp/tmux/${w.session.name}/${w.name}.sh'
|
||||
script_content := '#!/bin/bash\n' + cmd_
|
||||
os.write_file(script_path, script_content)!
|
||||
os.chmod(script_path, 0o755)!
|
||||
final_cmd = script_path
|
||||
}
|
||||
mut final_cmd := cmd_
|
||||
if cmd_.contains('\n') {
|
||||
os.mkdir_all('/tmp/tmux/${w.session.name}')!
|
||||
// Fix: osal.exec_string doesn't exist, use file writing instead
|
||||
script_path := '/tmp/tmux/${w.session.name}/${w.name}.sh'
|
||||
script_content := '#!/bin/bash\n' + cmd_
|
||||
os.write_file(script_path, script_content)!
|
||||
os.chmod(script_path, 0o755)!
|
||||
final_cmd = script_path
|
||||
}
|
||||
|
||||
mut newcmd := '/bin/bash -c "${final_cmd}"'
|
||||
if cmd_ == "" {
|
||||
newcmd = '/bin/bash'
|
||||
}
|
||||
mut newcmd := '/bin/bash -c "${final_cmd}"'
|
||||
if cmd_ == '' {
|
||||
newcmd = '/bin/bash'
|
||||
}
|
||||
|
||||
// Build environment arguments
|
||||
mut env_args := ''
|
||||
for key, value in w.env {
|
||||
env_args += ' -e ${key}="${value}"'
|
||||
}
|
||||
// Build environment arguments
|
||||
mut env_args := ''
|
||||
for key, value in w.env {
|
||||
env_args += ' -e ${key}="${value}"'
|
||||
}
|
||||
|
||||
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}\''
|
||||
console.print_debug(cmd)
|
||||
|
||||
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}")
|
||||
}
|
||||
|
||||
line_arr := res.output.split('|')
|
||||
wid := line_arr[2] or { return error('cannot split line for window create.\n${line_arr}') }
|
||||
w.id = wid.replace('@', '').int()
|
||||
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}\''
|
||||
console.print_debug(cmd)
|
||||
|
||||
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}")
|
||||
}
|
||||
|
||||
line_arr := res.output.split('|')
|
||||
wid := line_arr[2] or { return error('cannot split line for window create.\n${line_arr}') }
|
||||
w.id = wid.replace('@', '').int()
|
||||
}
|
||||
|
||||
// stop the window
|
||||
@@ -143,14 +142,14 @@ pub fn (window Window) str() string {
|
||||
}
|
||||
|
||||
pub fn (mut w Window) stats() !ProcessStats {
|
||||
mut total := ProcessStats{}
|
||||
for mut pane in w.panes {
|
||||
stats := pane.stats() or { continue }
|
||||
total.cpu_percent += stats.cpu_percent
|
||||
total.memory_bytes += stats.memory_bytes
|
||||
total.memory_percent += stats.memory_percent
|
||||
}
|
||||
return total
|
||||
mut total := ProcessStats{}
|
||||
for mut pane in w.panes {
|
||||
stats := pane.stats() or { continue }
|
||||
total.cpu_percent += stats.cpu_percent
|
||||
total.memory_bytes += stats.memory_bytes
|
||||
total.memory_percent += stats.memory_percent
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// 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
|
||||
pub fn (mut w Window) pane_active() ?&Pane {
|
||||
for pane in w.panes {
|
||||
if pane.active {
|
||||
return pane
|
||||
}
|
||||
}
|
||||
return none
|
||||
for pane in w.panes {
|
||||
if pane.active {
|
||||
return pane
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
@@ -24,23 +24,23 @@ fn testsuite_end() {
|
||||
}
|
||||
|
||||
fn test_window_new() ! {
|
||||
mut tmux := new()!
|
||||
tmux.start()!
|
||||
mut tmux := new()!
|
||||
tmux.start()!
|
||||
|
||||
// Create session first
|
||||
mut session := tmux.session_create(name: 'main')!
|
||||
|
||||
// Test window creation
|
||||
mut window := session.window_new(
|
||||
name: 'TestWindow'
|
||||
cmd: 'bash'
|
||||
reset: true
|
||||
)!
|
||||
|
||||
assert window.name == 'testwindow' // name_fix converts to lowercase
|
||||
assert session.window_exist(name: 'testwindow')
|
||||
|
||||
tmux.stop()!
|
||||
// Create session first
|
||||
mut session := tmux.session_create(name: 'main')!
|
||||
|
||||
// Test window creation
|
||||
mut window := session.window_new(
|
||||
name: 'TestWindow'
|
||||
cmd: 'bash'
|
||||
reset: true
|
||||
)!
|
||||
|
||||
assert window.name == 'testwindow' // name_fix converts to lowercase
|
||||
assert session.window_exist(name: 'testwindow')
|
||||
|
||||
tmux.stop()!
|
||||
}
|
||||
|
||||
// tests creating duplicate windows
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
module datamodel
|
||||
|
||||
// I can bid for infra, and optionally get accepted
|
||||
@[heap]
|
||||
//I can bid for infra, and optionally get accepted
|
||||
pub struct Bid {
|
||||
pub mut:
|
||||
id u32
|
||||
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_slice f64 //price per 1 GB slice I want to accept
|
||||
storage_slices []u32
|
||||
status BidStatus
|
||||
obligation bool //if obligation then will be charged and money needs to be in escrow, otherwise its an intent
|
||||
start_date u32 //epoch
|
||||
end_date u32
|
||||
id u32
|
||||
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_slice f64 // price per 1 GB slice I want to accept
|
||||
storage_slices []u32
|
||||
status BidStatus
|
||||
obligation bool // if obligation then will be charged and money needs to be in escrow, otherwise its an intent
|
||||
start_date u32 // epoch
|
||||
end_date u32
|
||||
}
|
||||
|
||||
pub enum BidStatus {
|
||||
@@ -21,4 +21,4 @@ pub enum BidStatus {
|
||||
assigned
|
||||
cancelled
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ module datamodel
|
||||
@[heap]
|
||||
pub struct Reservation {
|
||||
pub mut:
|
||||
id u32
|
||||
customer_id u32 //links back to customer for this capacity
|
||||
id u32
|
||||
customer_id u32 // links back to customer for this capacity
|
||||
compute_slices []u32
|
||||
storage_slices []u32
|
||||
status ReservationStatus
|
||||
start_date u32 //epoch
|
||||
end_date u32
|
||||
status ReservationStatus
|
||||
start_date u32 // epoch
|
||||
end_date u32
|
||||
}
|
||||
|
||||
pub enum ReservationStatus {
|
||||
@@ -18,4 +18,4 @@ pub enum ReservationStatus {
|
||||
assigned
|
||||
cancelled
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module datamodel
|
||||
|
||||
import freeflowuniverse.herolib.threefold.grid4.datamodel { Node }
|
||||
|
||||
pub struct NodeSim {
|
||||
Node
|
||||
pub mut:
|
||||
|
||||
Reference in New Issue
Block a user