diff --git a/examples/installers/horus/horus_start_all.vsh b/examples/installers/horus/horus_start_all.vsh index 3b640f1e..f9d8a251 100755 --- a/examples/installers/horus/horus_start_all.vsh +++ b/examples/installers/horus/horus_start_all.vsh @@ -24,7 +24,7 @@ if coordinator_installer.running()! { // Step 2: Start Supervisor println('\n▶️ Step 2/5: Starting Supervisor...') -mut supervisor_inst := supervisor.get()! +mut supervisor_inst := supervisor.get(create: true)! supervisor_inst.start()! if supervisor_inst.running()! { println('✅ Supervisor is running on HTTP:${supervisor_inst.http_port} WS:${supervisor_inst.ws_port}') @@ -34,7 +34,7 @@ if supervisor_inst.running()! { // Step 3: Start Hero Runner println('\n▶️ Step 3/5: Starting Hero Runner...') -mut hero_runner := herorunner.get()! +mut hero_runner := herorunner.get(create: true)! hero_runner.start()! if hero_runner.running()! { println('✅ Hero Runner is running') @@ -44,7 +44,7 @@ if hero_runner.running()! { // Step 4: Start Osiris Runner println('\n▶️ Step 4/5: Starting Osiris Runner...') -mut osiris_runner := osirisrunner.get()! +mut osiris_runner := osirisrunner.get(create: true)! osiris_runner.start()! if osiris_runner.running()! { println('✅ Osiris Runner is running') @@ -54,7 +54,7 @@ if osiris_runner.running()! { // Step 5: Start SAL Runner println('\n▶️ Step 5/5: Starting SAL Runner...') -mut sal_runner := salrunner.get()! +mut sal_runner := salrunner.get(create: true)! sal_runner.start()! if sal_runner.running()! { println('✅ SAL Runner is running') diff --git a/lib/installers/horus/coordinator/coordinator_actions.v b/lib/installers/horus/coordinator/coordinator_actions.v index 294b0177..96b74fbd 100644 --- a/lib/installers/horus/coordinator/coordinator_actions.v +++ b/lib/installers/horus/coordinator/coordinator_actions.v @@ -27,12 +27,14 @@ fn (self &Coordinator) startupcmd() ![]startupmanager.ZProcessNewArgs { fn (self &Coordinator) running_check() !bool { // Check if the process is running by checking the HTTP port + // The coordinator returns 405 for GET requests (requires POST), so we check if we get any response res := osal.exec( - cmd: 'curl -fsSL http://127.0.0.1:${self.http_port} || exit 1' + cmd: 'curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${self.http_port}' stdout: false raise_error: false )! - return res.exit_code == 0 + // Any HTTP response code (including 405) means the server is running + return res.output.len > 0 && res.output.int() > 0 } fn (self &Coordinator) start_pre() ! { diff --git a/lib/installers/horus/herorunner/herorunner_actions.v b/lib/installers/horus/herorunner/herorunner_actions.v index 38f43a96..22387ebd 100644 --- a/lib/installers/horus/herorunner/herorunner_actions.v +++ b/lib/installers/horus/herorunner/herorunner_actions.v @@ -11,10 +11,17 @@ import os fn (self &Herorunner) startupcmd() ![]startupmanager.ZProcessNewArgs { mut res := []startupmanager.ZProcessNewArgs{} - + + // Ensure redis_addr has the redis:// prefix + redis_url := if self.redis_addr.starts_with('redis://') { + self.redis_addr + } else { + 'redis://${self.redis_addr}' + } + res << startupmanager.ZProcessNewArgs{ name: 'herorunner' - cmd: '${self.binary_path} --redis-addr ${self.redis_addr}' + cmd: '${self.binary_path} --redis-url ${redis_url} 12001' env: { 'HOME': os.home_dir() 'RUST_LOG': self.log_level @@ -52,7 +59,7 @@ fn (self &Herorunner) installed() !bool { if !binary.exists() { return false } - + return true } @@ -66,7 +73,6 @@ fn ulist_get() !ulist.UList { fn upload() ! { } - @[params] pub struct InstallArgs { pub mut: @@ -81,7 +87,7 @@ fn (mut self Herorunner) install(args InstallArgs) ! { fn (mut self Herorunner) build() ! { console.print_header('build herorunner') - + // Ensure rust is installed console.print_debug('Checking if Rust is installed...') mut rust_installer := rust.get()! @@ -92,7 +98,7 @@ fn (mut self Herorunner) build() ! { } else { console.print_debug('Rust is already installed: ${res.output.trim_space()}') } - + // Clone or get the repository console.print_debug('Cloning/updating horus repository...') mut gs := gittools.new()! @@ -101,40 +107,40 @@ fn (mut self Herorunner) build() ! { pull: true reset: false )! - + repo_path := repo.path() console.print_debug('Repository path: ${repo_path}') - + // Build the herorunner binary from the horus workspace console.print_header('Building herorunner binary (this may take several minutes)...') console.print_debug('Running: cargo build -p runner-hero --release') console.print_debug('Build output:') - + cmd := 'cd ${repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p runner-hero --release' osal.execute_stdout(cmd)! - + console.print_debug('Build completed successfully') - + // Ensure binary directory exists and copy the binary console.print_debug('Preparing binary directory: ${self.binary_path}') mut binary_path_obj := pathlib.get(self.binary_path) osal.dir_ensure(binary_path_obj.path_dir())! - + // Copy the built binary to the configured location source_binary := '${repo_path}/target/release/herorunner' console.print_debug('Copying binary from: ${source_binary}') console.print_debug('Copying binary to: ${self.binary_path}') mut source_file := pathlib.get_file(path: source_binary)! source_file.copy(dest: self.binary_path, rsync: false)! - + console.print_header('herorunner built successfully at ${self.binary_path}') } fn (mut self Herorunner) destroy() ! { self.stop()! - + osal.process_kill_recursive(name: 'herorunner')! - + // Remove the built binary osal.rm(self.binary_path)! } diff --git a/lib/installers/horus/osirisrunner/osirisrunner_actions.v b/lib/installers/horus/osirisrunner/osirisrunner_actions.v index 0eff803b..b0a5f913 100644 --- a/lib/installers/horus/osirisrunner/osirisrunner_actions.v +++ b/lib/installers/horus/osirisrunner/osirisrunner_actions.v @@ -11,10 +11,17 @@ import os fn (self &Osirisrunner) startupcmd() ![]startupmanager.ZProcessNewArgs { mut res := []startupmanager.ZProcessNewArgs{} - + + // Ensure redis_addr has the redis:// prefix + redis_url := if self.redis_addr.starts_with('redis://') { + self.redis_addr + } else { + 'redis://${self.redis_addr}' + } + res << startupmanager.ZProcessNewArgs{ name: 'runner_osiris' - cmd: '${self.binary_path} --redis-addr ${self.redis_addr}' + cmd: '${self.binary_path} --redis-url ${redis_url} 12002' env: { 'HOME': os.home_dir() 'RUST_LOG': self.log_level @@ -52,7 +59,7 @@ fn (self &Osirisrunner) installed() !bool { if !binary.exists() { return false } - + return true } @@ -66,7 +73,6 @@ fn ulist_get() !ulist.UList { fn upload() ! { } - @[params] pub struct InstallArgs { pub mut: @@ -81,7 +87,7 @@ fn (mut self Osirisrunner) install(args InstallArgs) ! { fn (mut self Osirisrunner) build() ! { console.print_header('build osirisrunner') - + // Ensure rust is installed console.print_debug('Checking if Rust is installed...') mut rust_installer := rust.get()! @@ -92,7 +98,7 @@ fn (mut self Osirisrunner) build() ! { } else { console.print_debug('Rust is already installed: ${res.output.trim_space()}') } - + // Clone or get the repository console.print_debug('Cloning/updating horus repository...') mut gs := gittools.new()! @@ -101,42 +107,42 @@ fn (mut self Osirisrunner) build() ! { pull: true reset: false )! - + // Update the path to the actual cloned repo self.repo_path = repo.path() set(self)! console.print_debug('Repository path: ${self.repo_path}') - + // Build the osirisrunner binary from the horus workspace console.print_header('Building osirisrunner binary (this may take several minutes)...') console.print_debug('Running: cargo build -p runner-osiris --release') console.print_debug('Build output:') - + cmd := 'cd ${self.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p runner-osiris --release' osal.execute_stdout(cmd)! - + console.print_debug('Build completed successfully') - + // Ensure binary directory exists and copy the binary console.print_debug('Preparing binary directory: ${self.binary_path}') mut binary_path_obj := pathlib.get(self.binary_path) osal.dir_ensure(binary_path_obj.path_dir())! - + // Copy the built binary to the configured location source_binary := '${self.repo_path}/target/release/runner_osiris' console.print_debug('Copying binary from: ${source_binary}') console.print_debug('Copying binary to: ${self.binary_path}') mut source_file := pathlib.get_file(path: source_binary)! source_file.copy(dest: self.binary_path, rsync: false)! - + console.print_header('osirisrunner built successfully at ${self.binary_path}') } fn (mut self Osirisrunner) destroy() ! { self.stop()! - + osal.process_kill_recursive(name: 'runner_osiris')! - + // Remove the built binary osal.rm(self.binary_path)! } diff --git a/lib/installers/horus/salrunner/salrunner_actions.v b/lib/installers/horus/salrunner/salrunner_actions.v index 6ed141e8..9c19d2a3 100644 --- a/lib/installers/horus/salrunner/salrunner_actions.v +++ b/lib/installers/horus/salrunner/salrunner_actions.v @@ -11,10 +11,17 @@ import os fn (self &Salrunner) startupcmd() ![]startupmanager.ZProcessNewArgs { mut res := []startupmanager.ZProcessNewArgs{} - + + // Ensure redis_addr has the redis:// prefix + redis_url := if self.redis_addr.starts_with('redis://') { + self.redis_addr + } else { + 'redis://${self.redis_addr}' + } + res << startupmanager.ZProcessNewArgs{ name: 'runner_sal' - cmd: '${self.binary_path} --redis-addr ${self.redis_addr}' + cmd: '${self.binary_path} --redis-url ${redis_url} 12003' env: { 'HOME': os.home_dir() 'RUST_LOG': self.log_level @@ -52,7 +59,7 @@ fn (self &Salrunner) installed() !bool { if !binary.exists() { return false } - + return true } @@ -66,7 +73,6 @@ fn ulist_get() !ulist.UList { fn upload() ! { } - @[params] pub struct InstallArgs { pub mut: @@ -81,7 +87,7 @@ fn (mut self Salrunner) install(args InstallArgs) ! { fn (mut self Salrunner) build() ! { console.print_header('build salrunner') - + // Ensure rust is installed console.print_debug('Checking if Rust is installed...') mut rust_installer := rust.get()! @@ -92,7 +98,7 @@ fn (mut self Salrunner) build() ! { } else { console.print_debug('Rust is already installed: ${res.output.trim_space()}') } - + // Clone or get the repository console.print_debug('Cloning/updating horus repository...') mut gs := gittools.new()! @@ -101,42 +107,42 @@ fn (mut self Salrunner) build() ! { pull: true reset: false )! - + // Update the path to the actual cloned repo self.repo_path = repo.path() set(self)! console.print_debug('Repository path: ${self.repo_path}') - + // Build the salrunner binary from the horus workspace console.print_header('Building salrunner binary (this may take several minutes)...') console.print_debug('Running: cargo build -p runner-sal --release') console.print_debug('Build output:') - + cmd := 'cd ${self.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p runner-sal --release' osal.execute_stdout(cmd)! - + console.print_debug('Build completed successfully') - + // Ensure binary directory exists and copy the binary console.print_debug('Preparing binary directory: ${self.binary_path}') mut binary_path_obj := pathlib.get(self.binary_path) osal.dir_ensure(binary_path_obj.path_dir())! - + // Copy the built binary to the configured location source_binary := '${self.repo_path}/target/release/runner_sal' console.print_debug('Copying binary from: ${source_binary}') console.print_debug('Copying binary to: ${self.binary_path}') mut source_file := pathlib.get_file(path: source_binary)! source_file.copy(dest: self.binary_path, rsync: false)! - + console.print_header('salrunner built successfully at ${self.binary_path}') } fn (mut self Salrunner) destroy() ! { self.stop()! - + osal.process_kill_recursive(name: 'runner_sal')! - + // Remove the built binary osal.rm(self.binary_path)! } diff --git a/lib/installers/horus/supervisor/supervisor_actions.v b/lib/installers/horus/supervisor/supervisor_actions.v index 59e6ade5..33acc5cc 100644 --- a/lib/installers/horus/supervisor/supervisor_actions.v +++ b/lib/installers/horus/supervisor/supervisor_actions.v @@ -12,10 +12,17 @@ import os fn (self &Supervisor) startupcmd() ![]startupmanager.ZProcessNewArgs { mut res := []startupmanager.ZProcessNewArgs{} - + + // Ensure redis_addr has the redis:// prefix + redis_url := if self.redis_addr.starts_with('redis://') { + self.redis_addr + } else { + 'redis://${self.redis_addr}' + } + res << startupmanager.ZProcessNewArgs{ name: 'supervisor' - cmd: '${self.binary_path} --redis-addr ${self.redis_addr} --api-http-port ${self.http_port} --api-ws-port ${self.ws_port}' + cmd: '${self.binary_path} --redis-url ${redis_url} --port ${self.http_port} --admin-secret mysecret' env: { 'HOME': os.home_dir() 'RUST_LOG': self.log_level @@ -28,8 +35,14 @@ fn (self &Supervisor) startupcmd() ![]startupmanager.ZProcessNewArgs { fn (self &Supervisor) running_check() !bool { // Check if the process is running by checking the HTTP port - res := osal.exec(cmd: 'curl -fsSL http://127.0.0.1:${self.http_port} || exit 1', stdout: false, raise_error: false)! - return res.exit_code == 0 + // The supervisor returns 405 for GET requests (requires POST), so we check if we get any response + res := osal.exec( + cmd: 'curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${self.http_port}' + stdout: false + raise_error: false + )! + // Any HTTP response code (including 405) means the server is running + return res.output.len > 0 && res.output.int() > 0 } fn (self &Supervisor) start_pre() ! { @@ -53,7 +66,7 @@ fn (self &Supervisor) installed() !bool { if !binary.exists() { return false } - + return true } @@ -71,7 +84,6 @@ fn upload() ! { // )! } - @[params] pub struct InstallArgs { pub mut: @@ -88,7 +100,7 @@ fn (mut self Supervisor) install(args InstallArgs) ! { pub fn build_supervisor() ! { console.print_header('build supervisor') println('📦 Starting supervisor build process...\n') - + // Use default config instead of getting from factory println('⚙️ Initializing configuration...') mut cfg := Supervisor{} @@ -97,10 +109,10 @@ pub fn build_supervisor() ! { println(' - Redis address: ${cfg.redis_addr}') println(' - HTTP port: ${cfg.http_port}') println(' - WS port: ${cfg.ws_port}\n') - + // Ensure Redis is installed and running (required for supervisor) println('🔍 Step 1/4: Checking Redis dependency...') - + // First check if redis-server is installed if !osal.cmd_exists_profile('redis-server') { println('⚠️ Redis is not installed') @@ -110,7 +122,7 @@ pub fn build_supervisor() ! { } else { println('✅ Redis is already installed') } - + // Now check if it's running println('🔍 Checking if Redis is running...') redis_check := osal.exec(cmd: 'redis-cli -c -p 6379 ping', stdout: false, raise_error: false)! @@ -122,7 +134,7 @@ pub fn build_supervisor() ! { } else { println('✅ Redis is already running\n') } - + // Ensure rust is installed println('🔍 Step 2/4: Checking Rust dependency...') mut rust_installer := rust.get()! @@ -134,7 +146,7 @@ pub fn build_supervisor() ! { } else { println('✅ Rust is already installed: ${res.output.trim_space()}\n') } - + // Clone or get the repository println('🔍 Step 3/4: Cloning/updating horus repository...') mut gs := gittools.new()! @@ -143,40 +155,40 @@ pub fn build_supervisor() ! { pull: true reset: false )! - + // Update the path to the actual cloned repo cfg.repo_path = repo.path() println('✅ Repository ready at: ${cfg.repo_path}\n') - + // Build the supervisor binary from the horus workspace println('🔍 Step 4/4: Building supervisor binary...') println('⚠️ This may take several minutes (compiling Rust code)...') println('📝 Running: cargo build -p hero-supervisor --release\n') - + cmd := 'cd ${cfg.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p hero-supervisor --release' osal.execute_stdout(cmd)! - + println('\n✅ Build completed successfully') - + // Ensure binary directory exists and copy the binary println('📁 Preparing binary directory: ${cfg.binary_path}') mut binary_path_obj := pathlib.get(cfg.binary_path) osal.dir_ensure(binary_path_obj.path_dir())! - + // Copy the built binary to the configured location source_binary := '${cfg.repo_path}/target/release/supervisor' println('📋 Copying binary from: ${source_binary}') println('📋 Copying binary to: ${cfg.binary_path}') mut source_file := pathlib.get_file(path: source_binary)! source_file.copy(dest: cfg.binary_path, rsync: false)! - + println('\n🎉 Supervisor built successfully!') println('📍 Binary location: ${cfg.binary_path}') } fn (mut self Supervisor) build() ! { console.print_header('build supervisor') - + // Ensure Redis is installed and running (required for supervisor) console.print_debug('Checking if Redis is installed and running...') redis_check := osal.exec(cmd: 'redis-cli -c -p 6379 ping', stdout: false, raise_error: false)! @@ -192,7 +204,7 @@ fn (mut self Supervisor) build() ! { } else { console.print_debug('Redis is already running') } - + // Ensure rust is installed console.print_debug('Checking if Rust is installed...') mut rust_installer := rust.get()! @@ -203,7 +215,7 @@ fn (mut self Supervisor) build() ! { } else { console.print_debug('Rust is already installed: ${res.output.trim_space()}') } - + // Clone or get the repository console.print_debug('Cloning/updating horus repository...') mut gs := gittools.new()! @@ -212,42 +224,42 @@ fn (mut self Supervisor) build() ! { pull: true reset: false )! - + // Update the path to the actual cloned repo self.repo_path = repo.path() set(self)! console.print_debug('Repository path: ${self.repo_path}') - + // Build the supervisor binary from the horus workspace console.print_header('Building supervisor binary (this may take several minutes)...') console.print_debug('Running: cargo build -p hero-supervisor --release') console.print_debug('Build output:') - + cmd := 'cd ${self.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p hero-supervisor --release' osal.execute_stdout(cmd)! - + console.print_debug('Build completed successfully') - + // Ensure binary directory exists and copy the binary console.print_debug('Preparing binary directory: ${self.binary_path}') mut binary_path_obj := pathlib.get(self.binary_path) osal.dir_ensure(binary_path_obj.path_dir())! - + // Copy the built binary to the configured location source_binary := '${self.repo_path}/target/release/supervisor' console.print_debug('Copying binary from: ${source_binary}') console.print_debug('Copying binary to: ${self.binary_path}') mut source_file := pathlib.get_file(path: source_binary)! source_file.copy(dest: self.binary_path, rsync: false)! - + console.print_header('supervisor built successfully at ${self.binary_path}') } fn (mut self Supervisor) destroy() ! { self.stop()! - + osal.process_kill_recursive(name: 'supervisor')! - + // Remove the built binary osal.rm(self.binary_path)! } diff --git a/lib/installers/horus/supervisor/supervisor_factory_.v b/lib/installers/horus/supervisor/supervisor_factory_.v index cd5ef0be..14f2cb2d 100644 --- a/lib/installers/horus/supervisor/supervisor_factory_.v +++ b/lib/installers/horus/supervisor/supervisor_factory_.v @@ -44,14 +44,19 @@ pub fn new(args ArgsGet) !&Supervisor { pub fn get(args ArgsGet) !&Supervisor { mut context := base.context()! - supervisor_default = args.name - if args.fromdb || args.name !in supervisor_global { + mut name := if args.name == 'default' && supervisor_default.len > 0 { + supervisor_default + } else { + args.name + } + supervisor_default = name + if args.fromdb || name !in supervisor_global { mut r := context.redis()! - if r.hexists('context:supervisor', args.name)! { - data := r.hget('context:supervisor', args.name)! + if r.hexists('context:supervisor', name)! { + data := r.hget('context:supervisor', name)! if data.len == 0 { print_backtrace() - return error('Supervisor with name: ${args.name} does not exist, prob bug.') + return error('Supervisor with name: ${name} does not exist, prob bug.') } mut obj := json.decode(Supervisor, data)! set_in_mem(obj)! @@ -60,14 +65,14 @@ pub fn get(args ArgsGet) !&Supervisor { new(args)! } else { print_backtrace() - return error("Supervisor with name '${args.name}' does not exist") + return error("Supervisor with name '${name}' does not exist") } } - return get(name: args.name)! // no longer from db nor create + return get(name: name)! // no longer from db nor create } - return supervisor_global[args.name] or { + return supervisor_global[name] or { print_backtrace() - return error('could not get config for supervisor with name:${args.name}') + return error('could not get config for supervisor with name:${name}') } } @@ -154,7 +159,7 @@ pub fn play(mut plbook PlayBook) ! { reset := p.get_default_false('reset') mut supervisor_obj := get(name: name)! console.print_debug('action object:\n${supervisor_obj}') - + if other_action.name == 'destroy' || reset { console.print_debug('install action supervisor.destroy') supervisor_obj.destroy()! @@ -168,7 +173,8 @@ pub fn play(mut plbook PlayBook) ! { supervisor_obj.build()! } } - if other_action.name in ['start', 'stop', 'restart', 'start_pre', 'start_post', 'stop_pre', 'stop_post'] { + if other_action.name in ['start', 'stop', 'restart', 'start_pre', 'start_post', 'stop_pre', + 'stop_post'] { mut p := other_action.params name := p.get('name')! mut supervisor_obj := get(name: name)! @@ -261,8 +267,6 @@ pub fn (mut self Supervisor) start() ! { for zprocess in self.startupcmd()! { mut sm := startupmanager_get(zprocess.startuptype)! - println('debugzo ${sm}') - console.print_debug('installer: supervisor starting with ${zprocess.startuptype}...') sm.new(zprocess)! @@ -319,7 +323,6 @@ pub fn (mut self Supervisor) running() !bool { return self.running_check()! } - // switch instance to be used for supervisor pub fn switch(name string) { supervisor_default = name diff --git a/lib/osal/startupmanager/startupmanager.v b/lib/osal/startupmanager/startupmanager.v index 843caf35..9c65aed7 100644 --- a/lib/osal/startupmanager/startupmanager.v +++ b/lib/osal/startupmanager/startupmanager.v @@ -102,19 +102,43 @@ pub fn (mut sm StartupManager) new(args ZProcessNewArgs) ! { shutdown_timeout: 0 // Default, or add to ZProcessNewArgs if needed } + // Check if service already exists + existing_service := zinit_client.service_get(args.name) or { zinit.ServiceConfig{} } + + // If service exists, stop monitoring, stop, and delete it first + if existing_service.exec.len > 0 { + console.print_debug('startupmanager: service ${args.name} already exists, cleaning up...') + // Stop the service first + zinit_client.service_stop(args.name) or { + console.print_debug('startupmanager: failed to stop service ${args.name}: ${err}') + } + // Forget (stop monitoring) the service + zinit_client.service_forget(args.name) or { + console.print_debug('startupmanager: failed to forget service ${args.name}: ${err}') + } + // Delete the service configuration + zinit_client.service_delete(args.name) or { + console.print_debug('startupmanager: failed to delete service ${args.name}: ${err}') + } + } + // Create the service configuration file in zinit zinit_client.service_create(args.name, service_config) or { return error('startupmanager: failed to create zinit service ${args.name}: ${err}') } + + // If 'start' is true, monitor and start the service immediately after creation + if args.start { + // Monitor loads the config and starts monitoring the service + zinit_client.service_monitor(args.name) or { + return error('startupmanager: failed to monitor zinit service ${args.name}: ${err}') + } + } } else { panic('to implement, startup manager only support screen & systemd for now: ${mycat}') } } - // If 'start' is true, also monitor and start the service - if args.start { - sm.start(args.name)! - } } pub fn (mut sm StartupManager) start(name string) ! { @@ -139,11 +163,6 @@ pub fn (mut sm StartupManager) start(name string) ! { zinit_client.service_start(name) or { return error('startupmanager: Failed to start zinit service ${name}: ${err}') } - // Monitor loads the config, if it's new it starts it. - // If the service is already managed, this will bring it back up. - zinit_client.service_monitor(name) or { - return error('startupmanager: Failed to monitor zinit service ${name}: ${err}') - } } else { panic('to implement, startup manager only support screen, systemd and zinit for now')