This commit is contained in:
2025-08-16 19:27:31 +02:00
parent 5825640c2c
commit f7d5415484
55 changed files with 782 additions and 2209 deletions

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.clients.zinit_rpc
import freeflowuniverse.herolib.clients.zinit
import freeflowuniverse.herolib.installers.infra.zinit_installer
import os
import time
@@ -9,34 +10,33 @@ import time
println('=== Zinit RPC Client Example ===\n')
// Start Zinit in the background
println('Starting Zinit in background...')
mut zinit_process := os.new_process('/usr/local/bin/zinit')
zinit_process.set_args(['init'])
zinit_process.set_redirect_stdio()
zinit_process.run()
// // Start Zinit in the background
// println('Starting Zinit in background...')
// mut zinit_process := os.new_process('/usr/local/bin/zinit')
// zinit_process.set_args(['init'])
// zinit_process.set_redirect_stdio()
// zinit_process.run()
// Wait a moment for Zinit to start up
time.sleep(2000 * time.millisecond)
println(' Zinit started')
// time.sleep(2000 * time.millisecond)
// println('✓ Zinit started')
// Ensure we clean up Zinit when done
defer {
println('\nCleaning up...')
zinit_process.signal_kill()
zinit_process.wait()
println(' Zinit stopped')
}
// defer {
// println('\nCleaning up...')
// zinit_process.signal_kill()
// zinit_process.wait()
// println('✓ Zinit stopped')
// }
// mut installer := zinit_installer.get()!
// installer.install()!
// installer.start()!
// Create a new client
mut client := zinit_rpc.new_client(
name: 'example_client'
socket_path: '/tmp/zinit.sock'
) or {
println('Failed to create client: ${err}')
println('Make sure Zinit is running and the socket exists at /tmp/zinit.sock')
exit(1)
}
mut client := zinit.new()!
println(client)
println(' Created Zinit RPC client')
@@ -66,7 +66,7 @@ for service_name, state in services {
// 3. Create a test service configuration
println('\n3. Creating a test service...')
test_service_name := 'test_echo_service'
config := zinit_rpc.ServiceConfig{
config := zinit.ServiceConfig{
exec: '/bin/echo "Hello from test service"'
oneshot: true
log: 'stdout'
@@ -147,7 +147,7 @@ println('\n8. Getting service statistics...')
stats := client.service_stats(test_service_name) or {
println('Failed to get service stats (service might not be running): ${err}')
// Continue anyway
zinit_rpc.ServiceStats{}
zinit.ServiceStats{}
}
if stats.name != '' {
println(' Service statistics:')
@@ -208,7 +208,7 @@ if subscription_id != 0 {
// Get fresh status to make sure service is still running
fresh_status := client.service_status(test_service_name) or {
println('\n12. Skipping signal test (cannot get service status)')
zinit_rpc.ServiceStatus{}
zinit.ServiceStatus{}
}
if fresh_status.state == 'Running' && fresh_status.pid > 0 {
println('\n12. Sending SIGTERM signal to service...')
@@ -258,7 +258,6 @@ server_result := client.system_start_http_server('127.0.0.1:9999') or {
}
if server_result != '' {
println(' HTTP server started: ${server_result}')
// Stop the HTTP server
client.system_stop_http_server() or { println('Failed to stop HTTP server: ${err}') }
println(' HTTP server stopped')

View File

@@ -4,7 +4,7 @@ import freeflowuniverse.herolib.core.generator.generic as generator
import freeflowuniverse.herolib.core.pathlib
mut args := generator.GeneratorArgs{
path: '~/code/github/freeflowuniverse/herolib/lib/installers'
path: '~/code/github/freeflowuniverse/herolib/lib/clients'
force: true
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&GiteaClient {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&GiteaClient {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&GiteaClient {
if r.hexists('context:giteaclient', args.name)! {
data := r.hget('context:giteaclient', args.name)!
if data.len == 0 {
return error('giteaclient with name: giteaclient does not exist, prob bug.')
return error('GiteaClient with name: giteaclient does not exist, prob bug.')
}
mut obj := json.decode(GiteaClient, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&GiteaClient {
// register the config for the future
pub fn set(o GiteaClient) ! {
set_in_mem(o)!
giteaclient_default = o.name
mut o2 := set_in_mem(o)!
giteaclient_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:giteaclient', o.name, json.encode(o))!
r.hset('context:giteaclient', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&GiteaClient {
}
// only sets in mem, does not set as config
fn set_in_mem(o GiteaClient) ! {
fn set_in_mem(o GiteaClient) !GiteaClient {
mut o2 := obj_init(o)!
giteaclient_global[o.name] = &o2
giteaclient_default = o.name
}
// switch instance to be used for giteaclient
pub fn switch(name string) {
giteaclient_default = name
giteaclient_global[o2.name] = &o2
giteaclient_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'giteaclient.') {
return
}
mut install_actions := plbook.find(filter: 'giteaclient.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for giteaclient
pub fn switch(name string) {
giteaclient_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&IPApi {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&IPApi {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&IPApi {
if r.hexists('context:ipapi', args.name)! {
data := r.hget('context:ipapi', args.name)!
if data.len == 0 {
return error('ipapi with name: ipapi does not exist, prob bug.')
return error('IPApi with name: ipapi does not exist, prob bug.')
}
mut obj := json.decode(IPApi, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&IPApi {
// register the config for the future
pub fn set(o IPApi) ! {
set_in_mem(o)!
ipapi_default = o.name
mut o2 := set_in_mem(o)!
ipapi_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:ipapi', o.name, json.encode(o))!
r.hset('context:ipapi', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&IPApi {
}
// only sets in mem, does not set as config
fn set_in_mem(o IPApi) ! {
fn set_in_mem(o IPApi) !IPApi {
mut o2 := obj_init(o)!
ipapi_global[o.name] = &o2
ipapi_default = o.name
}
// switch instance to be used for ipapi
pub fn switch(name string) {
ipapi_default = name
ipapi_global[o2.name] = &o2
ipapi_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'ipapi.') {
return
}
mut install_actions := plbook.find(filter: 'ipapi.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for ipapi
pub fn switch(name string) {
ipapi_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&Jina {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&Jina {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&Jina {
if r.hexists('context:jina', args.name)! {
data := r.hget('context:jina', args.name)!
if data.len == 0 {
return error('jina with name: jina does not exist, prob bug.')
return error('Jina with name: jina does not exist, prob bug.')
}
mut obj := json.decode(Jina, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&Jina {
// register the config for the future
pub fn set(o Jina) ! {
set_in_mem(o)!
jina_default = o.name
mut o2 := set_in_mem(o)!
jina_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:jina', o.name, json.encode(o))!
r.hset('context:jina', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&Jina {
}
// only sets in mem, does not set as config
fn set_in_mem(o Jina) ! {
fn set_in_mem(o Jina) !Jina {
mut o2 := obj_init(o)!
jina_global[o.name] = &o2
jina_default = o.name
}
// switch instance to be used for jina
pub fn switch(name string) {
jina_default = name
jina_global[o2.name] = &o2
jina_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'jina.') {
return
}
mut install_actions := plbook.find(filter: 'jina.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for jina
pub fn switch(name string) {
jina_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&LivekitClient {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&LivekitClient {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&LivekitClient {
if r.hexists('context:livekit', args.name)! {
data := r.hget('context:livekit', args.name)!
if data.len == 0 {
return error('livekit with name: livekit does not exist, prob bug.')
return error('LivekitClient with name: livekit does not exist, prob bug.')
}
mut obj := json.decode(LivekitClient, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&LivekitClient {
// register the config for the future
pub fn set(o LivekitClient) ! {
set_in_mem(o)!
livekit_default = o.name
mut o2 := set_in_mem(o)!
livekit_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:livekit', o.name, json.encode(o))!
r.hset('context:livekit', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&LivekitClient {
}
// only sets in mem, does not set as config
fn set_in_mem(o LivekitClient) ! {
fn set_in_mem(o LivekitClient) !LivekitClient {
mut o2 := obj_init(o)!
livekit_global[o.name] = &o2
livekit_default = o.name
}
// switch instance to be used for livekit
pub fn switch(name string) {
livekit_default = name
livekit_global[o2.name] = &o2
livekit_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'livekit.') {
return
}
mut install_actions := plbook.find(filter: 'livekit.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for livekit
pub fn switch(name string) {
livekit_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&MailClient {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&MailClient {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&MailClient {
if r.hexists('context:mailclient', args.name)! {
data := r.hget('context:mailclient', args.name)!
if data.len == 0 {
return error('mailclient with name: mailclient does not exist, prob bug.')
return error('MailClient with name: mailclient does not exist, prob bug.')
}
mut obj := json.decode(MailClient, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&MailClient {
// register the config for the future
pub fn set(o MailClient) ! {
set_in_mem(o)!
mailclient_default = o.name
mut o2 := set_in_mem(o)!
mailclient_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:mailclient', o.name, json.encode(o))!
r.hset('context:mailclient', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&MailClient {
}
// only sets in mem, does not set as config
fn set_in_mem(o MailClient) ! {
fn set_in_mem(o MailClient) !MailClient {
mut o2 := obj_init(o)!
mailclient_global[o.name] = &o2
mailclient_default = o.name
}
// switch instance to be used for mailclient
pub fn switch(name string) {
mailclient_default = name
mailclient_global[o2.name] = &o2
mailclient_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'mailclient.') {
return
}
mut install_actions := plbook.find(filter: 'mailclient.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for mailclient
pub fn switch(name string) {
mailclient_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&MeilisearchClient {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&MeilisearchClient {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&MeilisearchClient {
if r.hexists('context:meilisearch', args.name)! {
data := r.hget('context:meilisearch', args.name)!
if data.len == 0 {
return error('meilisearch with name: meilisearch does not exist, prob bug.')
return error('MeilisearchClient with name: meilisearch does not exist, prob bug.')
}
mut obj := json.decode(MeilisearchClient, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&MeilisearchClient {
// register the config for the future
pub fn set(o MeilisearchClient) ! {
set_in_mem(o)!
meilisearch_default = o.name
mut o2 := set_in_mem(o)!
meilisearch_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:meilisearch', o.name, json.encode(o))!
r.hset('context:meilisearch', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&MeilisearchClient {
}
// only sets in mem, does not set as config
fn set_in_mem(o MeilisearchClient) ! {
fn set_in_mem(o MeilisearchClient) !MeilisearchClient {
mut o2 := obj_init(o)!
meilisearch_global[o.name] = &o2
meilisearch_default = o.name
}
// switch instance to be used for meilisearch
pub fn switch(name string) {
meilisearch_default = name
meilisearch_global[o2.name] = &o2
meilisearch_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'meilisearch.') {
return
}
mut install_actions := plbook.find(filter: 'meilisearch.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for meilisearch
pub fn switch(name string) {
meilisearch_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&Mycelium {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&Mycelium {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&Mycelium {
if r.hexists('context:mycelium', args.name)! {
data := r.hget('context:mycelium', args.name)!
if data.len == 0 {
return error('mycelium with name: mycelium does not exist, prob bug.')
return error('Mycelium with name: mycelium does not exist, prob bug.')
}
mut obj := json.decode(Mycelium, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&Mycelium {
// register the config for the future
pub fn set(o Mycelium) ! {
set_in_mem(o)!
mycelium_default = o.name
mut o2 := set_in_mem(o)!
mycelium_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:mycelium', o.name, json.encode(o))!
r.hset('context:mycelium', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&Mycelium {
}
// only sets in mem, does not set as config
fn set_in_mem(o Mycelium) ! {
fn set_in_mem(o Mycelium) !Mycelium {
mut o2 := obj_init(o)!
mycelium_global[o.name] = &o2
mycelium_default = o.name
}
// switch instance to be used for mycelium
pub fn switch(name string) {
mycelium_default = name
mycelium_global[o2.name] = &o2
mycelium_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'mycelium.') {
return
}
mut install_actions := plbook.find(filter: 'mycelium.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,7 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for mycelium
pub fn switch(name string) {
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&MyceliumRPC {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&MyceliumRPC {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&MyceliumRPC {
if r.hexists('context:mycelium_rpc', args.name)! {
data := r.hget('context:mycelium_rpc', args.name)!
if data.len == 0 {
return error('mycelium_rpc with name: mycelium_rpc does not exist, prob bug.')
return error('MyceliumRPC with name: mycelium_rpc does not exist, prob bug.')
}
mut obj := json.decode(MyceliumRPC, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&MyceliumRPC {
// register the config for the future
pub fn set(o MyceliumRPC) ! {
set_in_mem(o)!
mycelium_rpc_default = o.name
mut o2 := set_in_mem(o)!
mycelium_rpc_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:mycelium_rpc', o.name, json.encode(o))!
r.hset('context:mycelium_rpc', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&MyceliumRPC {
}
// only sets in mem, does not set as config
fn set_in_mem(o MyceliumRPC) ! {
fn set_in_mem(o MyceliumRPC) !MyceliumRPC {
mut o2 := obj_init(o)!
mycelium_rpc_global[o.name] = &o2
mycelium_rpc_default = o.name
}
// switch instance to be used for mycelium_rpc
pub fn switch(name string) {
mycelium_rpc_default = name
mycelium_rpc_global[o2.name] = &o2
mycelium_rpc_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'mycelium_rpc.') {
return
}
mut install_actions := plbook.find(filter: 'mycelium_rpc.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,7 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for mycelium_rpc
pub fn switch(name string) {
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&OpenAI {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&OpenAI {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&OpenAI {
if r.hexists('context:openai', args.name)! {
data := r.hget('context:openai', args.name)!
if data.len == 0 {
return error('openai with name: openai does not exist, prob bug.')
return error('OpenAI with name: openai does not exist, prob bug.')
}
mut obj := json.decode(OpenAI, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&OpenAI {
// register the config for the future
pub fn set(o OpenAI) ! {
set_in_mem(o)!
openai_default = o.name
mut o2 := set_in_mem(o)!
openai_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:openai', o.name, json.encode(o))!
r.hset('context:openai', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&OpenAI {
}
// only sets in mem, does not set as config
fn set_in_mem(o OpenAI) ! {
fn set_in_mem(o OpenAI) !OpenAI {
mut o2 := obj_init(o)!
openai_global[o.name] = &o2
openai_default = o.name
}
// switch instance to be used for openai
pub fn switch(name string) {
openai_default = name
openai_global[o2.name] = &o2
openai_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'openai.') {
return
}
mut install_actions := plbook.find(filter: 'openai.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for openai
pub fn switch(name string) {
openai_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&PostgresqlClient {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&PostgresqlClient {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&PostgresqlClient {
if r.hexists('context:postgresql_client', args.name)! {
data := r.hget('context:postgresql_client', args.name)!
if data.len == 0 {
return error('postgresql_client with name: postgresql_client does not exist, prob bug.')
return error('PostgresqlClient with name: postgresql_client does not exist, prob bug.')
}
mut obj := json.decode(PostgresqlClient, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&PostgresqlClient {
// register the config for the future
pub fn set(o PostgresqlClient) ! {
set_in_mem(o)!
postgresql_client_default = o.name
mut o2 := set_in_mem(o)!
postgresql_client_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:postgresql_client', o.name, json.encode(o))!
r.hset('context:postgresql_client', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&PostgresqlClient {
}
// only sets in mem, does not set as config
fn set_in_mem(o PostgresqlClient) ! {
fn set_in_mem(o PostgresqlClient) !PostgresqlClient {
mut o2 := obj_init(o)!
postgresql_client_global[o.name] = &o2
postgresql_client_default = o.name
}
// switch instance to be used for postgresql_client
pub fn switch(name string) {
postgresql_client_default = name
postgresql_client_global[o2.name] = &o2
postgresql_client_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'postgresql_client.') {
return
}
mut install_actions := plbook.find(filter: 'postgresql_client.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for postgresql_client
pub fn switch(name string) {
postgresql_client_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&QDrantClient {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&QDrantClient {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&QDrantClient {
if r.hexists('context:qdrant', args.name)! {
data := r.hget('context:qdrant', args.name)!
if data.len == 0 {
return error('qdrant with name: qdrant does not exist, prob bug.')
return error('QDrantClient with name: qdrant does not exist, prob bug.')
}
mut obj := json.decode(QDrantClient, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&QDrantClient {
// register the config for the future
pub fn set(o QDrantClient) ! {
set_in_mem(o)!
qdrant_default = o.name
mut o2 := set_in_mem(o)!
qdrant_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:qdrant', o.name, json.encode(o))!
r.hset('context:qdrant', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&QDrantClient {
}
// only sets in mem, does not set as config
fn set_in_mem(o QDrantClient) ! {
fn set_in_mem(o QDrantClient) !QDrantClient {
mut o2 := obj_init(o)!
qdrant_global[o.name] = &o2
qdrant_default = o.name
}
// switch instance to be used for qdrant
pub fn switch(name string) {
qdrant_default = name
qdrant_global[o2.name] = &o2
qdrant_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'qdrant.') {
return
}
mut install_actions := plbook.find(filter: 'qdrant.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for qdrant
pub fn switch(name string) {
qdrant_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&RCloneClient {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&RCloneClient {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&RCloneClient {
if r.hexists('context:rclone', args.name)! {
data := r.hget('context:rclone', args.name)!
if data.len == 0 {
return error('rclone with name: rclone does not exist, prob bug.')
return error('RCloneClient with name: rclone does not exist, prob bug.')
}
mut obj := json.decode(RCloneClient, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&RCloneClient {
// register the config for the future
pub fn set(o RCloneClient) ! {
set_in_mem(o)!
rclone_default = o.name
mut o2 := set_in_mem(o)!
rclone_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:rclone', o.name, json.encode(o))!
r.hset('context:rclone', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&RCloneClient {
}
// only sets in mem, does not set as config
fn set_in_mem(o RCloneClient) ! {
fn set_in_mem(o RCloneClient) !RCloneClient {
mut o2 := obj_init(o)!
rclone_global[o.name] = &o2
rclone_default = o.name
}
// switch instance to be used for rclone
pub fn switch(name string) {
rclone_default = name
rclone_global[o2.name] = &o2
rclone_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'rclone.') {
return
}
mut install_actions := plbook.find(filter: 'rclone.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,7 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for rclone
pub fn switch(name string) {
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&RunPod {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&RunPod {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&RunPod {
if r.hexists('context:runpod', args.name)! {
data := r.hget('context:runpod', args.name)!
if data.len == 0 {
return error('runpod with name: runpod does not exist, prob bug.')
return error('RunPod with name: runpod does not exist, prob bug.')
}
mut obj := json.decode(RunPod, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&RunPod {
// register the config for the future
pub fn set(o RunPod) ! {
set_in_mem(o)!
runpod_default = o.name
mut o2 := set_in_mem(o)!
runpod_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:runpod', o.name, json.encode(o))!
r.hset('context:runpod', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&RunPod {
}
// only sets in mem, does not set as config
fn set_in_mem(o RunPod) ! {
fn set_in_mem(o RunPod) !RunPod {
mut o2 := obj_init(o)!
runpod_global[o.name] = &o2
runpod_default = o.name
}
// switch instance to be used for runpod
pub fn switch(name string) {
runpod_default = name
runpod_global[o2.name] = &o2
runpod_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'runpod.') {
return
}
mut install_actions := plbook.find(filter: 'runpod.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for runpod
pub fn switch(name string) {
runpod_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&SendGrid {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&SendGrid {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&SendGrid {
if r.hexists('context:sendgrid', args.name)! {
data := r.hget('context:sendgrid', args.name)!
if data.len == 0 {
return error('sendgrid with name: sendgrid does not exist, prob bug.')
return error('SendGrid with name: sendgrid does not exist, prob bug.')
}
mut obj := json.decode(SendGrid, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&SendGrid {
// register the config for the future
pub fn set(o SendGrid) ! {
set_in_mem(o)!
sendgrid_default = o.name
mut o2 := set_in_mem(o)!
sendgrid_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:sendgrid', o.name, json.encode(o))!
r.hset('context:sendgrid', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&SendGrid {
}
// only sets in mem, does not set as config
fn set_in_mem(o SendGrid) ! {
fn set_in_mem(o SendGrid) !SendGrid {
mut o2 := obj_init(o)!
sendgrid_global[o.name] = &o2
sendgrid_default = o.name
}
// switch instance to be used for sendgrid
pub fn switch(name string) {
sendgrid_default = name
sendgrid_global[o2.name] = &o2
sendgrid_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'sendgrid.') {
return
}
mut install_actions := plbook.find(filter: 'sendgrid.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,7 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for sendgrid
pub fn switch(name string) {
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&VastAI {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&VastAI {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&VastAI {
if r.hexists('context:vastai', args.name)! {
data := r.hget('context:vastai', args.name)!
if data.len == 0 {
return error('vastai with name: vastai does not exist, prob bug.')
return error('VastAI with name: vastai does not exist, prob bug.')
}
mut obj := json.decode(VastAI, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&VastAI {
// register the config for the future
pub fn set(o VastAI) ! {
set_in_mem(o)!
vastai_default = o.name
mut o2 := set_in_mem(o)!
vastai_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:vastai', o.name, json.encode(o))!
r.hset('context:vastai', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&VastAI {
}
// only sets in mem, does not set as config
fn set_in_mem(o VastAI) ! {
fn set_in_mem(o VastAI) !VastAI {
mut o2 := obj_init(o)!
vastai_global[o.name] = &o2
vastai_default = o.name
}
// switch instance to be used for vastai
pub fn switch(name string) {
vastai_default = name
vastai_global[o2.name] = &o2
vastai_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'vastai.') {
return
}
mut install_actions := plbook.find(filter: 'vastai.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,7 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for vastai
pub fn switch(name string) {
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&WireGuard {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&WireGuard {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&WireGuard {
if r.hexists('context:wireguard', args.name)! {
data := r.hget('context:wireguard', args.name)!
if data.len == 0 {
return error('wireguard with name: wireguard does not exist, prob bug.')
return error('WireGuard with name: wireguard does not exist, prob bug.')
}
mut obj := json.decode(WireGuard, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&WireGuard {
// register the config for the future
pub fn set(o WireGuard) ! {
set_in_mem(o)!
wireguard_default = o.name
mut o2 := set_in_mem(o)!
wireguard_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:wireguard', o.name, json.encode(o))!
r.hset('context:wireguard', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&WireGuard {
}
// only sets in mem, does not set as config
fn set_in_mem(o WireGuard) ! {
fn set_in_mem(o WireGuard) !WireGuard {
mut o2 := obj_init(o)!
wireguard_global[o.name] = &o2
wireguard_default = o.name
}
// switch instance to be used for wireguard
pub fn switch(name string) {
wireguard_default = name
wireguard_global[o2.name] = &o2
wireguard_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'wireguard.') {
return
}
mut install_actions := plbook.find(filter: 'wireguard.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for wireguard
pub fn switch(name string) {
wireguard_default = name
}

View File

@@ -25,7 +25,7 @@ pub fn new(args ArgsGet) !&ZeroDBClient {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&ZeroDBClient {
@@ -36,7 +36,7 @@ pub fn get(args ArgsGet) !&ZeroDBClient {
if r.hexists('context:zerodb_client', args.name)! {
data := r.hget('context:zerodb_client', args.name)!
if data.len == 0 {
return error('zerodb_client with name: zerodb_client does not exist, prob bug.')
return error('ZeroDBClient with name: zerodb_client does not exist, prob bug.')
}
mut obj := json.decode(ZeroDBClient, data)!
set_in_mem(obj)!
@@ -56,11 +56,11 @@ pub fn get(args ArgsGet) !&ZeroDBClient {
// register the config for the future
pub fn set(o ZeroDBClient) ! {
set_in_mem(o)!
zerodb_client_default = o.name
mut o2 := set_in_mem(o)!
zerodb_client_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:zerodb_client', o.name, json.encode(o))!
r.hset('context:zerodb_client', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -109,18 +109,17 @@ pub fn list(args ArgsList) ![]&ZeroDBClient {
}
// only sets in mem, does not set as config
fn set_in_mem(o ZeroDBClient) ! {
fn set_in_mem(o ZeroDBClient) !ZeroDBClient {
mut o2 := obj_init(o)!
zerodb_client_global[o.name] = &o2
zerodb_client_default = o.name
}
// switch instance to be used for zerodb_client
pub fn switch(name string) {
zerodb_client_default = name
zerodb_client_global[o2.name] = &o2
zerodb_client_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'zerodb_client.') {
return
}
mut install_actions := plbook.find(filter: 'zerodb_client.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
@@ -130,3 +129,7 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for zerodb_client
pub fn switch(name string) {
}

View File

@@ -1,8 +1,8 @@
!!hero_code.generate_client
name:'zinit_rpc'
name:'zinit'
classname:'ZinitRPC'
singleton:1
default:0
singleton:0
default:1
hasconfig:1
reset:0

View File

@@ -1,152 +1,219 @@
# Zinit OpenRPC Client
# Zinit RPC Client
This is a V language client for the Zinit service manager, implementing the OpenRPC specification.
This is a V language client for the Zinit process manager, implementing the JSON-RPC API specification for service management operations.
## Overview
Zinit is a service manager that allows you to manage and monitor services on your system. This client provides a comprehensive API to interact with Zinit via its JSON-RPC interface.
Zinit is a process manager that provides service monitoring, dependency management, and system control capabilities. This client provides a comprehensive API to interact with Zinit via its JSON-RPC interface for administrative tasks such as:
- Service lifecycle management (start, stop, monitor, forget)
- Service configuration management (create, delete, get)
- Service status and statistics monitoring
- System operations (shutdown, reboot, HTTP server control)
- Log streaming and monitoring
## Features
- Complete implementation of all methods in the Zinit OpenRPC specification
- Type-safe API with proper error handling
- Comprehensive documentation
- Helper functions for common operations
- Example code for all operations
- **✅ 100% API Coverage**: Complete implementation of all 18 methods in the Zinit JSON-RPC specification
- **✅ Production Tested**: All methods tested and working against real Zinit instances
- **✅ Type-safe API**: Proper V struct definitions with comprehensive error handling
- **✅ Subscription Support**: Proper handling of streaming/subscription methods
- **✅ Unix Socket Transport**: Reliable communication via Unix domain sockets
- **✅ Comprehensive Documentation**: Extensive documentation with working examples
## Usage
### Basic Example
```v
import freeflowuniverse.heroweb.clients.zinit
import freeflowuniverse.herolib.clients.zinit
fn main() {
// Create a new client with the default socket path
mut client := zinit.new_default_client()
// List all services
services := client.service_list() or {
println('Error: ${err}')
return
}
// Print the services
for name, state in services {
println('${name}: ${state}')
}
// Get status of a specific service
if services.len > 0 {
service_name := services.keys()[0]
status := client.service_status(service_name) or {
println('Error: ${err}')
return
}
println('Service: ${status.name}')
println('State: ${status.state}')
println('PID: ${status.pid}')
// Create a new client
mut client := zinit.get(create:true)!
// List all services
services := client.service_list()!
for service_name, state in services {
println('Service: ${service_name}, State: ${state}')
}
// Get detailed status of a specific service
status := client.service_status('redis')!
println('Service: ${status.name}')
println('PID: ${status.pid}')
println('State: ${status.state}')
println('Target: ${status.target}')
// Start a service
client.service_start('redis')!
// Stop a service
client.service_stop('redis')!
```
### Service Configuration Management
```v
import freeflowuniverse.herolib.clients.zinit
mut client := zinit.new_client()!
// Create a new service configuration
config := zinit.ServiceConfig{
exec: '/usr/bin/redis-server'
oneshot: false
log: 'stdout'
env: {
'REDIS_PORT': '6379'
'REDIS_HOST': '0.0.0.0'
}
shutdown_timeout: 30
}
// Create the service
path := client.service_create('redis', config)!
println('Service created at: ${path}')
// Get service configuration
retrieved_config := client.service_get('redis')!
println('Service exec: ${retrieved_config.exec}')
// Delete service configuration
result := client.service_delete('redis')!
println('Delete result: ${result}')
```
### Service Statistics
```v
import freeflowuniverse.herolib.clients.zinit
mut client := zinit.new_client()!
// Get service statistics
stats := client.service_stats('redis')!
println('Service: ${stats.name}')
println('PID: ${stats.pid}')
println('Memory Usage: ${stats.memory_usage} bytes')
println('CPU Usage: ${stats.cpu_usage}%')
// Print child process statistics
for child in stats.children {
println('Child PID: ${child.pid}, Memory: ${child.memory_usage}, CPU: ${child.cpu_usage}%')
}
```
### Creating and Managing Services
### Log Streaming
```v
import freeflowuniverse.heroweb.clients.zinit
import freeflowuniverse.herolib.clients.zinit
fn main() {
mut client := zinit.new_default_client()
// Create a new service configuration
config := zinit.ServiceConfig{
exec: '/bin/echo "Hello, World!"'
oneshot: true
log: zinit.log_stdout
env: {
'ENV_VAR': 'value'
}
}
// Create the service
client.service_create('hello', config) or {
println('Error creating service: ${err}')
return
}
// Start the service
client.service_start('hello') or {
println('Error starting service: ${err}')
return
}
// Get the service logs
logs := client.stream_current_logs('hello') or {
println('Error getting logs: ${err}')
return
}
for log in logs {
println(log)
}
// Clean up
client.service_stop('hello') or {}
client.service_forget('hello') or {}
client.service_delete('hello') or {}
mut client := zinit.new_client()!
// Get current logs for all services
logs := client.stream_current_logs(name: '')!
for log in logs {
println(log)
}
// Get current logs for a specific service
redis_logs := client.stream_current_logs(name: 'redis')!
for log in redis_logs {
println('Redis: ${log}')
}
// Subscribe to log stream (returns subscription ID)
subscription_id := client.stream_subscribe_logs(name: 'redis')!
println('Subscribed to logs with ID: ${subscription_id}')
```
## API Reference
### Client Creation
### Service Management Methods
- `new_client(socket_path string) &Client` - Create a new client with a custom socket path
- `new_default_client() &Client` - Create a new client with the default socket path (`/tmp/zinit.sock`)
- `service_list()` - List all services and their states
- `service_status(name)` - Get detailed status of a service
- `service_start(name)` - Start a service
- `service_stop(name)` - Stop a service
- `service_monitor(name)` - Start monitoring a service
- `service_forget(name)` - Stop monitoring a service
- `service_kill(name, signal)` - Send signal to a service
### Service Management
### Service Configuration Methods
- `service_list() !map[string]string` - List all services and their states
- `service_status(name string) !ServiceStatus` - Get detailed status of a service
- `service_start(name string) !` - Start a service
- `service_stop(name string) !` - Stop a service
- `service_monitor(name string) !` - Start monitoring a service
- `service_forget(name string) !` - Stop monitoring a service
- `service_kill(name string, signal string) !` - Send a signal to a service
- `service_create(name string, config ServiceConfig) !string` - Create a new service
- `service_delete(name string) !string` - Delete a service
- `service_get(name string) !ServiceConfig` - Get a service configuration
- `service_stats(name string) !ServiceStats` - Get memory and CPU usage statistics
- `service_create(name, config)` - Create service configuration
- `service_delete(name)` - Delete service configuration
- `service_get(name)` - Get service configuration
### System Operations
### Monitoring Methods
- `system_shutdown() !` - Stop all services and power off the system
- `system_reboot() !` - Stop all services and reboot the system
- `system_start_http_server(address string) !string` - Start an HTTP/RPC server
- `system_stop_http_server() !` - Stop the HTTP/RPC server
- `service_stats(name)` - Get service statistics
### Logs
### System Methods
- `stream_current_logs(name ?string) ![]string` - Get current logs
- `stream_subscribe_logs(name ?string) !string` - Subscribe to log messages
- `system_shutdown()` - Shutdown the system
- `system_reboot()` - Reboot the system
- `system_start_http_server(address)` - Start HTTP server
- `system_stop_http_server()` - Stop HTTP server
## Constants
### Streaming Methods
- `default_socket_path` - Default Unix socket path (`/tmp/zinit.sock`)
- `state_running`, `state_success`, `state_error`, etc. - Common service states
- `target_up`, `target_down` - Common service targets
- `log_null`, `log_ring`, `log_stdout` - Common log types
- `signal_term`, `signal_kill`, etc. - Common signals
- `stream_current_logs(args)` - Get current logs (returns array of log lines)
- `stream_subscribe_logs(args)` - Subscribe to log stream (returns subscription ID)
## Helper Functions
### Discovery Methods
- `new_service_config(exec string) ServiceConfig` - Create a basic service configuration
- `new_oneshot_service_config(exec string) ServiceConfig` - Create a oneshot service configuration
- `is_service_not_found_error(err IError) bool` - Check if an error is a "service not found" error
- `format_memory_usage(bytes i64) string` - Format memory usage in human-readable format
- `format_cpu_usage(cpu_percent f64) string` - Format CPU usage
- `rpc_discover()` - Get OpenRPC specification
## Configuration
### Using the Factory Pattern
```v
import freeflowuniverse.herolib.clients.zinit
// Get client using factory (recommended)
mut client := zinit.get()!
// Use the client
services := client.service_list()!
```
### Example Heroscript Configuration
```hero
!!zinit.configure
name: 'production'
socket_path: '/tmp/zinit.sock'
```
## Error Handling
The client provides comprehensive error handling for all Zinit-specific error codes:
- `-32000`: Service not found
- `-32001`: Service already monitored
- `-32002`: Service is up
- `-32003`: Service is down
- `-32004`: Invalid signal
- `-32005`: Config error
- `-32006`: Shutting down
- `-32007`: Service already exists
- `-32008`: Service file error
```v
import freeflowuniverse.herolib.clients.zinit
mut client := zinit.new_client()!
// Handle specific errors
client.service_start('nonexistent') or {
if err.msg().contains('Service not found') {
println('Service does not exist')
} else {
println('Other error: ${err}')
}
}
```
## License
MIT

View File

@@ -1,18 +0,0 @@
module zinit
// Request Types for Zinit API
//
// This file contains all the request types used by the Zinit API.
// ZinitError represents an error returned by the zinit API
pub struct ZinitError {
pub mut:
code int // Error code
message string // Error message
data string // Additional error data
}
// Error implements the error interface for ZinitError
pub fn (e ZinitError) msg() string {
return 'Zinit Error ${e.code}: ${e.message} - ${e.data}'
}

View File

@@ -1,23 +0,0 @@
module zinit
import freeflowuniverse.herolib.schemas.jsonrpc
// Client is an OpenRPC client for Zinit
pub struct Client {
mut:
rpc_client &jsonrpc.Client
}
@[params]
pub struct ClientParams {
path string = '/tmp/zinit.sock' // Path to the Zinit RPC socket
}
// new_client creates a new Zinit RPC client with a custom socket path
pub fn new_client(args_ ClientParams) &Client {
mut args := args_
mut cl := jsonrpc.new_unix_socket_client(args.path)
return &Client{
rpc_client: cl
}
}

View File

@@ -1,73 +0,0 @@
module zinit
// ServiceCreateResponse represents the response from service_create
pub struct ServiceCreateResponse {
pub mut:
path string // Path to the created service file
}
// ServiceDeleteResponse represents the response from service_delete
pub struct ServiceDeleteResponse {
pub mut:
result string // Result of the delete operation
}
// SystemStartHttpServerResponse represents the response from system_start_http_server
pub struct SystemStartHttpServerResponse {
pub mut:
result string // Result of starting the HTTP server
}
// StreamCurrentLogsResponse represents the response from stream_currentLogs
pub struct StreamCurrentLogsResponse {
pub mut:
logs []string // Log entries
}
// StreamSubscribeLogsResponse represents the response from stream_subscribeLogs
pub struct StreamSubscribeLogsResponse {
pub mut:
subscription_id string // ID of the log subscription
}
// Module version information
pub const version = '1.0.0'
pub const author = 'Hero Code'
pub const license = 'MIT'
// Default socket path for zinit
pub const default_socket_path = '/tmp/zinit.sock'
// Common service states
pub const state_running = 'Running'
pub const state_success = 'Success'
pub const state_error = 'Error'
pub const state_stopped = 'Stopped'
pub const state_failed = 'Failed'
// Common service targets
pub const target_up = 'Up'
pub const target_down = 'Down'
// Common log types
pub const log_null = 'null'
pub const log_ring = 'ring'
pub const log_stdout = 'stdout'
// Common signals
pub const signal_term = 'SIGTERM'
pub const signal_kill = 'SIGKILL'
pub const signal_hup = 'SIGHUP'
pub const signal_usr1 = 'SIGUSR1'
pub const signal_usr2 = 'SIGUSR2'
// JSON-RPC error codes as defined in the OpenRPC specification
pub const error_service_not_found = -32000
pub const error_service_already_monitored = -32001
pub const error_service_is_up = -32002
pub const error_service_is_down = -32003
pub const error_invalid_signal = -32004
pub const error_config_error = -32005
pub const error_shutting_down = -32006
pub const error_service_already_exists = -32007
pub const error_service_file_error = -32008

View File

@@ -0,0 +1,63 @@
module zinit
// ServiceStatus represents detailed status information for a service
pub struct ServiceStatus {
pub mut:
name string // Service name
pid u32 // Process ID of the running service (if running)
state string // Current state of the service (Running, Success, Error, etc.)
target string // Target state of the service (Up, Down)
after map[string]string // Dependencies of the service and their states
}
// ServiceConfig represents the configuration for a zinit service
pub struct ServiceConfig {
pub mut:
exec string // Command to run
test string // Test command (optional)
oneshot bool // Whether the service should be restarted (maps to one_shot in Zinit)
after []string // Services that must be running before this one starts
log string // How to handle service output (null, ring, stdout)
env map[string]string // Environment variables for the service
dir string // Working directory for the service
shutdown_timeout u64 // Maximum time to wait for service to stop during shutdown
}
// ServiceStats represents memory and CPU usage statistics for a service
pub struct ServiceStats {
pub mut:
name string // Service name
pid u32 // Process ID of the service
memory_usage u64 // Memory usage in bytes
cpu_usage f32 // CPU usage as a percentage (0-100)
children []ChildStats // Stats for child processes
}
// ChildStats represents statistics for a child process
pub struct ChildStats {
pub mut:
pid u32 // Process ID of the child process
memory_usage u64 // Memory usage in bytes
cpu_usage f32 // CPU usage as a percentage (0-100)
}
// ServiceCreateParams represents parameters for service_create method
pub struct ServiceCreateParams {
pub mut:
name string // Name of the service to create
content ServiceConfig // Configuration for the service
}
// ServiceKillParams represents parameters for service_kill method
pub struct ServiceKillParams {
pub mut:
name string // Name of the service to kill
signal string // Signal to send (e.g., SIGTERM, SIGKILL)
}
// LogParams represents parameters for log streaming methods
@[params]
pub struct LogParams {
pub mut:
name string // Optional service name filter
}

View File

@@ -1,175 +0,0 @@
module zinit
import freeflowuniverse.herolib.schemas.jsonrpc
// ServiceConfig represents the configuration for a zinit service
pub struct ServiceConfig {
pub mut:
exec string // Command to run
oneshot bool // Whether the service should be restarted
after []string // Services that must be running before this one starts
log string // How to handle service output (null, ring, stdout)
env map[string]string // Environment variables for the service
shutdown_timeout int // Maximum time to wait for service to stop during shutdown
}
// KillParams represents the parameters for the service_kill method
pub struct KillParams {
pub:
name string // Name of the service to kill
signal string // Signal to send (e.g., SIGTERM, SIGKILL)
}
// RpcDiscoverResponse represents the response from rpc.discover
pub struct RpcDiscoverResponse {
pub mut:
spec map[string]string // OpenRPC specification
}
// rpc_discover returns the OpenRPC specification for the API
pub fn (mut c Client) rpc_discover() !RpcDiscoverResponse {
request := jsonrpc.new_request_generic('rpc.discover', []string{})
response := c.rpc_client.send[[]string, map[string]string](request)!
return RpcDiscoverResponse{
spec: response
}
}
// // Response Models for Zinit API
// //
// // This file contains all the response models used by the Zinit API.
// // These models are used as type parameters in the response generics.
// // ServiceListResponse represents the response from service_list
// pub struct ServiceListResponse {
// pub mut:
// // Map of service names to their current states
// services map[string]string
// }
// service_list lists all services managed by Zinit
// Returns a map of service names to their current states
pub fn (mut c Client) service_list() !map[string]string {
request := jsonrpc.new_request_generic('service_list', map[string]string{})
services := c.rpc_client.send[map[string]string, map[string]string](request)!
// return ServiceListResponse{
// services: services
// }
return services
}
// ServiceStatusResponse represents the response from service_status
pub struct ServiceStatusResponse {
pub mut:
name string // Service name
pid int // Process ID of the running service (if running)
state string // Current state of the service (Running, Success, Error, etc.)
target string // Target state of the service (Up, Down)
after map[string]string // Dependencies of the service and their states
}
// service_status shows detailed status information for a specific service
// name: the name of the service
pub fn (mut c Client) service_status(name string) !ServiceStatusResponse {
request := jsonrpc.new_request_generic('service_status', name)
// Use a direct struct mapping instead of manual conversion
return c.rpc_client.send[string, ServiceStatusResponse](request)!
}
// service_start starts a service
// name: the name of the service to start
pub fn (mut c Client) service_start(name string) ! {
request := jsonrpc.new_request_generic('service_start', name)
c.rpc_client.send[string, string](request)!
}
// service_stop stops a service
// name: the name of the service to stop
pub fn (mut c Client) service_stop(name string) ! {
request := jsonrpc.new_request_generic('service_stop', name)
c.rpc_client.send[string, string](request)!
}
// service_monitor starts monitoring a service
// The service configuration is loaded from the config directory
// name: the name of the service to monitor
pub fn (mut c Client) service_monitor(name string) ! {
request := jsonrpc.new_request_generic('service_monitor', name)
c.rpc_client.send[string, string](request)!
}
// service_delete deletes a service configuration file
// name: the name of the service to delete
pub fn (mut c Client) service_delete(name string) !ServiceDeleteResponse {
request := jsonrpc.new_request_generic('service_delete', name)
result := c.rpc_client.send[string, string](request)!
return ServiceDeleteResponse{
result: result
}
}
// service_forget stops monitoring a service
// You can only forget a stopped service
// name: the name of the service to forget
pub fn (mut c Client) service_forget(name string) ! {
request := jsonrpc.new_request_generic('service_forget', name)
c.rpc_client.send[string, string](request)!
}
// TODO: make sure the signal is a valid signal and enumerator do as @[params] so its optional
// service_kill sends a signal to a running service
// name: the name of the service to send the signal to
// signal: the signal to send (e.g., SIGTERM, SIGKILL)
pub fn (mut c Client) service_kill(name string, signal string) ! {
params := KillParams{
name: name
signal: signal
}
request := jsonrpc.new_request_generic('service_kill', params)
c.rpc_client.send[KillParams, string](request)!
}
// CreateServiceParams represents the parameters for the service_create method
struct CreateServiceParams {
name string // Name of the service to create
content ServiceConfig // Configuration for the service
}
// service_create creates a new service configuration file
// name: the name of the service to create
// config: the service configuration
pub fn (mut c Client) service_create(name string, config ServiceConfig) !ServiceCreateResponse {
params := CreateServiceParams{
name: name
content: config
}
request := jsonrpc.new_request_generic('service_create', params)
path := c.rpc_client.send[CreateServiceParams, string](request)!
return ServiceCreateResponse{
path: path
}
}
// service_get gets a service configuration file
// name: the name of the service to get
pub fn (mut c Client) service_get(name string) !ServiceConfigResponse {
request := jsonrpc.new_request_generic('service_get', {
'name': name
})
// We need to handle the conversion from ServiceConfig to ServiceConfigResponse
config := c.rpc_client.send[map[string]string, ServiceConfig](request)!
return ServiceConfigResponse{
exec: config.exec
oneshot: config.oneshot
after: config.after
log: config.log
env: config.env
shutdown_timeout: config.shutdown_timeout
}
}

View File

@@ -1,33 +0,0 @@
module zinit
pub struct ServiceConfigResponse {
pub mut:
exec string // Command to run
oneshot bool // Whether the service should be restarted
after []string // Services that must be running before this one starts
log string // How to handle service output (null, ring, stdout)
env map[string]string // Environment variables for the service
shutdown_timeout int // Maximum time to wait for service to stop during shutdown
}
// Helper function to create a basic service configuration
pub fn new_service_config(exec string) ServiceConfig {
return ServiceConfig{
exec: exec
oneshot: false
log: log_stdout
env: map[string]string{}
shutdown_timeout: 30
}
}
// Helper function to create a oneshot service configuration
pub fn new_oneshot_service_config(exec string) ServiceConfig {
return ServiceConfig{
exec: exec
oneshot: true
log: log_stdout
env: map[string]string{}
shutdown_timeout: 30
}
}

View File

@@ -1,44 +0,0 @@
module zinit
import freeflowuniverse.herolib.schemas.jsonrpc
// ServiceStatsResponse represents the response from service_stats
pub struct ServiceStatsResponse {
pub mut:
name string // Service name
pid int // Process ID of the service
memory_usage i64 // Memory usage in bytes
cpu_usage f64 // CPU usage as a percentage (0-100)
children []ChildStatsResponse // Stats for child processes
}
// ChildStatsResponse represents statistics for a child process
pub struct ChildStatsResponse {
pub mut:
pid int // Process ID of the child process
memory_usage i64 // Memory usage in bytes
cpu_usage f64 // CPU usage as a percentage (0-100)
}
// Serv
// service_stats gets memory and CPU usage statistics for a service
// name: the name of the service to get stats for
pub fn (mut c Client) service_stats(name string) !ServiceStatsResponse {
request := jsonrpc.new_request_generic('service_stats', name)
// We need to handle the conversion from the raw response to our model
raw_stats := c.rpc_client.send[string, map[string]string](request)!
// Parse the raw stats into our response model
mut children := []ChildStatsResponse{}
// In a real implementation, we would parse the children from the raw response
return ServiceStatsResponse{
name: raw_stats['name'] or { '' }
pid: raw_stats['pid'].int()
memory_usage: raw_stats['memory_usage'].i64()
cpu_usage: raw_stats['cpu_usage'].f64()
children: children
}
}

View File

@@ -1,71 +0,0 @@
module zinit
import freeflowuniverse.herolib.schemas.jsonrpc
// system_shutdown stops all services and powers off the system
pub fn (mut c Client) system_shutdown() ! {
request := jsonrpc.new_request_generic('system_shutdown', []string{})
c.rpc_client.send[[]string, string](request)!
}
// system_reboot stops all services and reboots the system
pub fn (mut c Client) system_reboot() ! {
request := jsonrpc.new_request_generic('system_reboot', []string{})
c.rpc_client.send[[]string, string](request)!
}
// system_start_http_server starts an HTTP/RPC server at the specified address
// address: the network address to bind the server to (e.g., '127.0.0.1:8080')
pub fn (mut c Client) system_start_http_server(address string) !SystemStartHttpServerResponse {
request := jsonrpc.new_request_generic('system_start_http_server', address)
result := c.rpc_client.send[string, string](request)!
return SystemStartHttpServerResponse{
result: result
}
}
// system_stop_http_server stops the HTTP/RPC server if running
pub fn (mut c Client) system_stop_http_server() ! {
request := jsonrpc.new_request_generic('system_stop_http_server', []string{})
c.rpc_client.send[[]string, string](request)!
}
@[params]
pub struct LogParams {
name string
}
// stream_current_logs gets current logs from zinit and monitored services
// name: optional service name filter. If provided, only logs from this service will be returned
pub fn (mut c Client) stream_current_logs(args LogParams) ![]string {
mut logs := []string{}
if args.name != '' {
request := jsonrpc.new_request_generic('stream_currentLogs', {
'name': args.name
})
logs = c.rpc_client.send[map[string]string, map[string]string](request)!
} else {
request := jsonrpc.new_request_generic('stream_currentLogs', map[string]string{})
logs = c.rpc_client.send[[]map[string]string, map[string]string](request)!
}
return logs
}
// stream_subscribe_logs subscribes to log messages generated by zinit and monitored services
// name: optional service name filter. If provided, only logs from this service will be returned
pub fn (mut c Client) stream_subscribe_logs(name ?string) !StreamSubscribeLogsResponse {
mut subscription_id := ''
if service_name := name {
request := jsonrpc.new_request_generic('stream_subscribeLogs', service_name)
subscription_id = c.rpc_client.send[string, string](request)!
} else {
request := jsonrpc.new_request_generic('stream_subscribeLogs', []string{})
subscription_id = c.rpc_client.send[[]string, string](request)!
}
return StreamSubscribeLogsResponse{
subscription_id: subscription_id
}
}

View File

@@ -1,19 +0,0 @@
module zinit
// Helper function to format memory usage in human-readable format
pub fn format_memory_usage(bytes i64) string {
if bytes < 1024 {
return '${bytes} B'
} else if bytes < 1024 * 1024 {
return '${bytes / 1024} KB'
} else if bytes < 1024 * 1024 * 1024 {
return '${bytes / 1024 / 1024} MB'
} else {
return '${bytes / 1024 / 1024 / 1024} GB'
}
}
// Helper function to format CPU usage
pub fn format_cpu_usage(cpu_percent f64) string {
return '${cpu_percent:.1f}%'
}

View File

@@ -1,9 +1,10 @@
module zinit_rpc
module zinit
import freeflowuniverse.herolib.schemas.jsonrpc
import freeflowuniverse.herolib.schemas.jsonrpcmodel
// Helper function to get or create the RPC client
fn (mut c ZinitRPC) get_client() !&jsonrpc.Client {
fn (mut c ZinitRPC) client_() !&jsonrpc.Client {
if client := c.rpc_client {
return client
}
@@ -16,23 +17,23 @@ fn (mut c ZinitRPC) get_client() !&jsonrpc.Client {
// Admin methods
// rpc_discover returns the OpenRPC specification for the API
pub fn (mut c ZinitRPC) rpc_discover() !OpenRPCSpec {
mut client := c.get_client()!
pub fn (mut c ZinitRPC) rpc_discover() !jsonrpcmodel.OpenRPCSpec {
mut client := c.client_()!
request := jsonrpc.new_request_generic('rpc.discover', []string{})
return client.send[[]string, OpenRPCSpec](request)!
return client.send[[]string, jsonrpcmodel.OpenRPCSpec](request)!
}
// service_list lists all services managed by Zinit
// Returns a map of service names to their current states
pub fn (mut c ZinitRPC) service_list() !map[string]string {
mut client := c.get_client()!
mut client := c.client_()!
request := jsonrpc.new_request_generic('service_list', []string{})
return client.send[[]string, map[string]string](request)!
}
// service_status shows detailed status information for a specific service
pub fn (mut c ZinitRPC) service_status(name string) !ServiceStatus {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'name': name
}
@@ -42,7 +43,7 @@ pub fn (mut c ZinitRPC) service_status(name string) !ServiceStatus {
// service_start starts a service
pub fn (mut c ZinitRPC) service_start(name string) ! {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'name': name
}
@@ -52,7 +53,7 @@ pub fn (mut c ZinitRPC) service_start(name string) ! {
// service_stop stops a service
pub fn (mut c ZinitRPC) service_stop(name string) ! {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'name': name
}
@@ -63,7 +64,7 @@ pub fn (mut c ZinitRPC) service_stop(name string) ! {
// service_monitor starts monitoring a service
// The service configuration is loaded from the config directory
pub fn (mut c ZinitRPC) service_monitor(name string) ! {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'name': name
}
@@ -74,7 +75,7 @@ pub fn (mut c ZinitRPC) service_monitor(name string) ! {
// service_forget stops monitoring a service
// You can only forget a stopped service
pub fn (mut c ZinitRPC) service_forget(name string) ! {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'name': name
}
@@ -84,7 +85,7 @@ pub fn (mut c ZinitRPC) service_forget(name string) ! {
// service_kill sends a signal to a running service
pub fn (mut c ZinitRPC) service_kill(name string, signal string) ! {
mut client := c.get_client()!
mut client := c.client_()!
params := ServiceKillParams{
name: name
signal: signal
@@ -95,7 +96,7 @@ pub fn (mut c ZinitRPC) service_kill(name string, signal string) ! {
// service_create creates a new service configuration file
pub fn (mut c ZinitRPC) service_create(name string, config ServiceConfig) !string {
mut client := c.get_client()!
mut client := c.client_()!
params := ServiceCreateParams{
name: name
content: config
@@ -106,7 +107,7 @@ pub fn (mut c ZinitRPC) service_create(name string, config ServiceConfig) !strin
// service_delete deletes a service configuration file
pub fn (mut c ZinitRPC) service_delete(name string) !string {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'name': name
}
@@ -116,7 +117,7 @@ pub fn (mut c ZinitRPC) service_delete(name string) !string {
// service_get gets a service configuration file
pub fn (mut c ZinitRPC) service_get(name string) !ServiceConfig {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'name': name
}
@@ -126,7 +127,7 @@ pub fn (mut c ZinitRPC) service_get(name string) !ServiceConfig {
// service_stats gets memory and CPU usage statistics for a service
pub fn (mut c ZinitRPC) service_stats(name string) !ServiceStats {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'name': name
}
@@ -138,21 +139,21 @@ pub fn (mut c ZinitRPC) service_stats(name string) !ServiceStats {
// system_shutdown stops all services and powers off the system
pub fn (mut c ZinitRPC) system_shutdown() ! {
mut client := c.get_client()!
mut client := c.client_()!
request := jsonrpc.new_request_generic('system_shutdown', []string{})
client.send[[]string, string](request)!
}
// system_reboot stops all services and reboots the system
pub fn (mut c ZinitRPC) system_reboot() ! {
mut client := c.get_client()!
mut client := c.client_()!
request := jsonrpc.new_request_generic('system_reboot', []string{})
client.send[[]string, string](request)!
}
// system_start_http_server starts an HTTP/RPC server at the specified address
pub fn (mut c ZinitRPC) system_start_http_server(address string) !string {
mut client := c.get_client()!
mut client := c.client_()!
params := {
'address': address
}
@@ -162,7 +163,7 @@ pub fn (mut c ZinitRPC) system_start_http_server(address string) !string {
// system_stop_http_server stops the HTTP/RPC server if running
pub fn (mut c ZinitRPC) system_stop_http_server() ! {
mut client := c.get_client()!
mut client := c.client_()!
request := jsonrpc.new_request_generic('system_stop_http_server', []string{})
client.send[[]string, string](request)!
}
@@ -171,7 +172,7 @@ pub fn (mut c ZinitRPC) system_stop_http_server() ! {
// stream_current_logs gets current logs from zinit and monitored services
pub fn (mut c ZinitRPC) stream_current_logs(args LogParams) ![]string {
mut client := c.get_client()!
mut client := c.client_()!
if args.name != '' {
params := {
'name': args.name
@@ -187,7 +188,7 @@ pub fn (mut c ZinitRPC) stream_current_logs(args LogParams) ![]string {
// stream_subscribe_logs subscribes to log messages generated by zinit and monitored services
// Returns a subscription ID that can be used to manage the subscription
pub fn (mut c ZinitRPC) stream_subscribe_logs(args LogParams) !u64 {
mut client := c.get_client()!
mut client := c.client_()!
if args.name != '' {
params := {
'name': args.name

View File

@@ -1,4 +1,4 @@
module zinit_rpc
module zinit
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook { PlayBook }
@@ -6,8 +6,8 @@ import freeflowuniverse.herolib.ui.console
import json
__global (
zinit_rpc_global map[string]&ZinitRPC
zinit_rpc_default string
zinit_global map[string]&ZinitRPC
zinit_default string
)
/////////FACTORY
@@ -25,18 +25,18 @@ pub fn new(args ArgsGet) !&ZinitRPC {
name: args.name
}
set(obj)!
return &obj
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&ZinitRPC {
mut context := base.context()!
zinit_rpc_default = args.name
if args.fromdb || args.name !in zinit_rpc_global {
zinit_default = args.name
if args.fromdb || args.name !in zinit_global {
mut r := context.redis()!
if r.hexists('context:zinit_rpc', args.name)! {
data := r.hget('context:zinit_rpc', args.name)!
if r.hexists('context:zinit', args.name)! {
data := r.hget('context:zinit', args.name)!
if data.len == 0 {
return error('zinit_rpc with name: zinit_rpc does not exist, prob bug.')
return error('ZinitRPC with name: zinit does not exist, prob bug.')
}
mut obj := json.decode(ZinitRPC, data)!
set_in_mem(obj)!
@@ -44,36 +44,36 @@ pub fn get(args ArgsGet) !&ZinitRPC {
if args.create {
new(args)!
} else {
return error("ZinitRPC with name 'zinit_rpc' does not exist")
return error("ZinitRPC with name 'zinit' does not exist")
}
}
return get(name: args.name)! // no longer from db nor create
}
return zinit_rpc_global[args.name] or {
return error('could not get config for zinit_rpc with name:zinit_rpc')
return zinit_global[args.name] or {
return error('could not get config for zinit with name:zinit')
}
}
// register the config for the future
pub fn set(o ZinitRPC) ! {
set_in_mem(o)!
zinit_rpc_default = o.name
mut o2 := set_in_mem(o)!
zinit_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:zinit_rpc', o.name, json.encode(o))!
r.hset('context:zinit', o2.name, json.encode(o2))!
}
// does the config exists?
pub fn exists(args ArgsGet) !bool {
mut context := base.context()!
mut r := context.redis()!
return r.hexists('context:zinit_rpc', args.name)!
return r.hexists('context:zinit', args.name)!
}
pub fn delete(args ArgsGet) ! {
mut context := base.context()!
mut r := context.redis()!
r.hdel('context:zinit_rpc', args.name)!
r.hdel('context:zinit', args.name)!
}
@[params]
@@ -88,12 +88,12 @@ pub fn list(args ArgsList) ![]&ZinitRPC {
mut context := base.context()!
if args.fromdb {
// reset what is in mem
zinit_rpc_global = map[string]&ZinitRPC{}
zinit_rpc_default = ''
zinit_global = map[string]&ZinitRPC{}
zinit_default = ''
}
if args.fromdb {
mut r := context.redis()!
mut l := r.hkeys('context:zinit_rpc')!
mut l := r.hkeys('context:zinit')!
for name in l {
res << get(name: name, fromdb: true)!
@@ -101,7 +101,7 @@ pub fn list(args ArgsList) ![]&ZinitRPC {
return res
} else {
// load from memory
for _, client in zinit_rpc_global {
for _, client in zinit_global {
res << client
}
}
@@ -109,19 +109,18 @@ pub fn list(args ArgsList) ![]&ZinitRPC {
}
// only sets in mem, does not set as config
fn set_in_mem(o ZinitRPC) ! {
fn set_in_mem(o ZinitRPC) !ZinitRPC {
mut o2 := obj_init(o)!
zinit_rpc_global[o.name] = &o2
zinit_rpc_default = o.name
}
// switch instance to be used for zinit_rpc
pub fn switch(name string) {
zinit_rpc_default = name
zinit_global[o2.name] = &o2
zinit_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
mut install_actions := plbook.find(filter: 'zinit_rpc.configure')!
if !plbook.exists(filter: 'zinit.') {
return
}
mut install_actions := plbook.find(filter: 'zinit.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript := install_action.heroscript()
@@ -130,3 +129,8 @@ pub fn play(mut plbook PlayBook) ! {
}
}
}
// switch instance to be used for zinit
pub fn switch(name string) {
zinit_default = name
}

View File

@@ -0,0 +1,48 @@
module zinit
import freeflowuniverse.herolib.data.encoderhero
import freeflowuniverse.herolib.schemas.jsonrpc
import os
pub const version = '0.0.0'
const singleton = true
const default = false
// // Factory function to create a new ZinitRPC client instance
// @[params]
// pub struct NewClientArgs {
// pub mut:
// name string = 'default'
// socket_path string
// }
// pub fn new_client(args NewClientArgs) !&ZinitRPC {
// mut client := ZinitRPC{
// name: args.name
// socket_path: args.socket_path
// }
// client = obj_init(client)!
// return &client
// }
@[heap]
pub struct ZinitRPC {
pub mut:
name string = 'default'
socket_path string
rpc_client ?&jsonrpc.Client @[skip]
}
// your checking & initialization code if needed
fn obj_init(mycfg_ ZinitRPC) !ZinitRPC {
mut mycfg := mycfg_
if mycfg.socket_path == '' {
mycfg.socket_path = '/tmp/zinit.sock'
}
return mycfg
}
pub fn heroscript_loads(heroscript string) !ZinitRPC {
mut obj := encoderhero.decode[ZinitRPC](heroscript)!
return obj
}

View File

@@ -1,873 +0,0 @@
{
"openrpc": "1.2.6",
"info": {
"version": "1.0.0",
"title": "Zinit JSON-RPC API",
"description": "JSON-RPC 2.0 API for controlling and querying Zinit services",
"license": {
"name": "MIT"
}
},
"servers": [
{
"name": "Unix Socket",
"url": "unix:///tmp/zinit.sock"
}
],
"methods": [
{
"name": "rpc.discover",
"description": "Returns the OpenRPC specification for the API",
"params": [],
"result": {
"name": "OpenRPCSpec",
"description": "The OpenRPC specification",
"schema": {
"type": "object"
}
},
"examples": [
{
"name": "Get API specification",
"params": [],
"result": {
"name": "OpenRPCSpecResult",
"value": {
"openrpc": "1.2.6",
"info": {
"version": "1.0.0",
"title": "Zinit JSON-RPC API"
}
}
}
}
]
},
{
"name": "service_list",
"description": "Lists all services managed by Zinit",
"params": [],
"result": {
"name": "ServiceList",
"description": "A map of service names to their current states",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string",
"description": "Service state (Running, Success, Error, etc.)"
}
}
},
"examples": [
{
"name": "List all services",
"params": [],
"result": {
"name": "ServiceListResult",
"value": {
"service1": "Running",
"service2": "Success",
"service3": "Error"
}
}
}
]
},
{
"name": "service_status",
"description": "Shows detailed status information for a specific service",
"params": [
{
"name": "name",
"description": "The name of the service",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "ServiceStatus",
"description": "Detailed status information for the service",
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Service name"
},
"pid": {
"type": "integer",
"description": "Process ID of the running service (if running)"
},
"state": {
"type": "string",
"description": "Current state of the service (Running, Success, Error, etc.)"
},
"target": {
"type": "string",
"description": "Target state of the service (Up, Down)"
},
"after": {
"type": "object",
"description": "Dependencies of the service and their states",
"additionalProperties": {
"type": "string",
"description": "State of the dependency"
}
}
}
}
},
"examples": [
{
"name": "Get status of redis service",
"params": [
{
"name": "name",
"value": "redis"
}
],
"result": {
"name": "ServiceStatusResult",
"value": {
"name": "redis",
"pid": 1234,
"state": "Running",
"target": "Up",
"after": {
"dependency1": "Success",
"dependency2": "Running"
}
}
}
}
],
"errors": [
{
"code": -32000,
"message": "Service not found",
"data": "service name \"unknown\" unknown"
}
]
},
{
"name": "service_start",
"description": "Starts a service",
"params": [
{
"name": "name",
"description": "The name of the service to start",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "StartResult",
"description": "Result of the start operation",
"schema": {
"type": "null"
}
},
"examples": [
{
"name": "Start redis service",
"params": [
{
"name": "name",
"value": "redis"
}
],
"result": {
"name": "StartResult",
"value": null
}
}
],
"errors": [
{
"code": -32000,
"message": "Service not found",
"data": "service name \"unknown\" unknown"
}
]
},
{
"name": "service_stop",
"description": "Stops a service",
"params": [
{
"name": "name",
"description": "The name of the service to stop",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "StopResult",
"description": "Result of the stop operation",
"schema": {
"type": "null"
}
},
"examples": [
{
"name": "Stop redis service",
"params": [
{
"name": "name",
"value": "redis"
}
],
"result": {
"name": "StopResult",
"value": null
}
}
],
"errors": [
{
"code": -32000,
"message": "Service not found",
"data": "service name \"unknown\" unknown"
},
{
"code": -32003,
"message": "Service is down",
"data": "service \"redis\" is down"
}
]
},
{
"name": "service_monitor",
"description": "Starts monitoring a service. The service configuration is loaded from the config directory.",
"params": [
{
"name": "name",
"description": "The name of the service to monitor",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "MonitorResult",
"description": "Result of the monitor operation",
"schema": {
"type": "null"
}
},
"examples": [
{
"name": "Monitor redis service",
"params": [
{
"name": "name",
"value": "redis"
}
],
"result": {
"name": "MonitorResult",
"value": null
}
}
],
"errors": [
{
"code": -32001,
"message": "Service already monitored",
"data": "service \"redis\" already monitored"
},
{
"code": -32005,
"message": "Config error",
"data": "failed to load service configuration"
}
]
},
{
"name": "service_forget",
"description": "Stops monitoring a service. You can only forget a stopped service.",
"params": [
{
"name": "name",
"description": "The name of the service to forget",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "ForgetResult",
"description": "Result of the forget operation",
"schema": {
"type": "null"
}
},
"examples": [
{
"name": "Forget redis service",
"params": [
{
"name": "name",
"value": "redis"
}
],
"result": {
"name": "ForgetResult",
"value": null
}
}
],
"errors": [
{
"code": -32000,
"message": "Service not found",
"data": "service name \"unknown\" unknown"
},
{
"code": -32002,
"message": "Service is up",
"data": "service \"redis\" is up"
}
]
},
{
"name": "service_kill",
"description": "Sends a signal to a running service",
"params": [
{
"name": "name",
"description": "The name of the service to send the signal to",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "signal",
"description": "The signal to send (e.g., SIGTERM, SIGKILL)",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "KillResult",
"description": "Result of the kill operation",
"schema": {
"type": "null"
}
},
"examples": [
{
"name": "Send SIGTERM to redis service",
"params": [
{
"name": "name",
"value": "redis"
},
{
"name": "signal",
"value": "SIGTERM"
}
],
"result": {
"name": "KillResult",
"value": null
}
}
],
"errors": [
{
"code": -32000,
"message": "Service not found",
"data": "service name \"unknown\" unknown"
},
{
"code": -32003,
"message": "Service is down",
"data": "service \"redis\" is down"
},
{
"code": -32004,
"message": "Invalid signal",
"data": "invalid signal: INVALID"
}
]
},
{
"name": "system_shutdown",
"description": "Stops all services and powers off the system",
"params": [],
"result": {
"name": "ShutdownResult",
"description": "Result of the shutdown operation",
"schema": {
"type": "null"
}
},
"examples": [
{
"name": "Shutdown the system",
"params": [],
"result": {
"name": "ShutdownResult",
"value": null
}
}
],
"errors": [
{
"code": -32006,
"message": "Shutting down",
"data": "system is already shutting down"
}
]
},
{
"name": "system_reboot",
"description": "Stops all services and reboots the system",
"params": [],
"result": {
"name": "RebootResult",
"description": "Result of the reboot operation",
"schema": {
"type": "null"
}
},
"examples": [
{
"name": "Reboot the system",
"params": [],
"result": {
"name": "RebootResult",
"value": null
}
}
],
"errors": [
{
"code": -32006,
"message": "Shutting down",
"data": "system is already shutting down"
}
]
},
{
"name": "service_create",
"description": "Creates a new service configuration file",
"params": [
{
"name": "name",
"description": "The name of the service to create",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "content",
"description": "The service configuration content",
"required": true,
"schema": {
"type": "object",
"properties": {
"exec": {
"type": "string",
"description": "Command to run"
},
"oneshot": {
"type": "boolean",
"description": "Whether the service should be restarted"
},
"after": {
"type": "array",
"items": {
"type": "string"
},
"description": "Services that must be running before this one starts"
},
"log": {
"type": "string",
"enum": ["null", "ring", "stdout"],
"description": "How to handle service output"
},
"env": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Environment variables for the service"
},
"shutdown_timeout": {
"type": "integer",
"description": "Maximum time to wait for service to stop during shutdown"
}
}
}
}
],
"result": {
"name": "CreateServiceResult",
"description": "Result of the create operation",
"schema": {
"type": "string"
}
},
"errors": [
{
"code": -32007,
"message": "Service already exists",
"data": "Service 'name' already exists"
},
{
"code": -32008,
"message": "Service file error",
"data": "Failed to create service file"
}
]
},
{
"name": "service_delete",
"description": "Deletes a service configuration file",
"params": [
{
"name": "name",
"description": "The name of the service to delete",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "DeleteServiceResult",
"description": "Result of the delete operation",
"schema": {
"type": "string"
}
},
"errors": [
{
"code": -32000,
"message": "Service not found",
"data": "Service 'name' not found"
},
{
"code": -32008,
"message": "Service file error",
"data": "Failed to delete service file"
}
]
},
{
"name": "service_get",
"description": "Gets a service configuration file",
"params": [
{
"name": "name",
"description": "The name of the service to get",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "GetServiceResult",
"description": "The service configuration",
"schema": {
"type": "object"
}
},
"errors": [
{
"code": -32000,
"message": "Service not found",
"data": "Service 'name' not found"
},
{
"code": -32008,
"message": "Service file error",
"data": "Failed to read service file"
}
]
},
{
"name": "service_stats",
"description": "Get memory and CPU usage statistics for a service",
"params": [
{
"name": "name",
"description": "The name of the service to get stats for",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "ServiceStats",
"description": "Memory and CPU usage statistics for the service",
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Service name"
},
"pid": {
"type": "integer",
"description": "Process ID of the service"
},
"memory_usage": {
"type": "integer",
"description": "Memory usage in bytes"
},
"cpu_usage": {
"type": "number",
"description": "CPU usage as a percentage (0-100)"
},
"children": {
"type": "array",
"description": "Stats for child processes",
"items": {
"type": "object",
"properties": {
"pid": {
"type": "integer",
"description": "Process ID of the child process"
},
"memory_usage": {
"type": "integer",
"description": "Memory usage in bytes"
},
"cpu_usage": {
"type": "number",
"description": "CPU usage as a percentage (0-100)"
}
}
}
}
}
}
},
"examples": [
{
"name": "Get stats for redis service",
"params": [
{
"name": "name",
"value": "redis"
}
],
"result": {
"name": "ServiceStatsResult",
"value": {
"name": "redis",
"pid": 1234,
"memory_usage": 10485760,
"cpu_usage": 2.5,
"children": [
{
"pid": 1235,
"memory_usage": 5242880,
"cpu_usage": 1.2
}
]
}
}
}
],
"errors": [
{
"code": -32000,
"message": "Service not found",
"data": "service name \"unknown\" unknown"
},
{
"code": -32003,
"message": "Service is down",
"data": "service \"redis\" is down"
}
]
},
{
"name": "system_start_http_server",
"description": "Start an HTTP/RPC server at the specified address",
"params": [
{
"name": "address",
"description": "The network address to bind the server to (e.g., '127.0.0.1:8080')",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "StartHttpServerResult",
"description": "Result of the start HTTP server operation",
"schema": {
"type": "string"
}
},
"examples": [
{
"name": "Start HTTP server on localhost:8080",
"params": [
{
"name": "address",
"value": "127.0.0.1:8080"
}
],
"result": {
"name": "StartHttpServerResult",
"value": "HTTP server started at 127.0.0.1:8080"
}
}
],
"errors": [
{
"code": -32602,
"message": "Invalid address",
"data": "Invalid network address format"
}
]
},
{
"name": "system_stop_http_server",
"description": "Stop the HTTP/RPC server if running",
"params": [],
"result": {
"name": "StopHttpServerResult",
"description": "Result of the stop HTTP server operation",
"schema": {
"type": "null"
}
},
"examples": [
{
"name": "Stop the HTTP server",
"params": [],
"result": {
"name": "StopHttpServerResult",
"value": null
}
}
],
"errors": [
{
"code": -32602,
"message": "Server not running",
"data": "No HTTP server is currently running"
}
]
},
{
"name": "stream_currentLogs",
"description": "Get current logs from zinit and monitored services",
"params": [
{
"name": "name",
"description": "Optional service name filter. If provided, only logs from this service will be returned",
"required": false,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "LogsResult",
"description": "Array of log strings",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
"examples": [
{
"name": "Get all logs",
"params": [],
"result": {
"name": "LogsResult",
"value": [
"2023-01-01T12:00:00 redis: Starting service",
"2023-01-01T12:00:01 nginx: Starting service"
]
}
},
{
"name": "Get logs for a specific service",
"params": [
{
"name": "name",
"value": "redis"
}
],
"result": {
"name": "LogsResult",
"value": [
"2023-01-01T12:00:00 redis: Starting service",
"2023-01-01T12:00:02 redis: Service started"
]
}
}
]
},
{
"name": "stream_subscribeLogs",
"description": "Subscribe to log messages generated by zinit and monitored services",
"params": [
{
"name": "name",
"description": "Optional service name filter. If provided, only logs from this service will be returned",
"required": false,
"schema": {
"type": "string"
}
}
],
"result": {
"name": "LogSubscription",
"description": "A subscription to log messages",
"schema": {
"type": "string"
}
},
"examples": [
{
"name": "Subscribe to all logs",
"params": [],
"result": {
"name": "LogSubscription",
"value": "2023-01-01T12:00:00 redis: Service started"
}
},
{
"name": "Subscribe to filtered logs",
"params": [
{
"name": "name",
"value": "redis"
}
],
"result": {
"name": "LogSubscription",
"value": "2023-01-01T12:00:00 redis: Service started"
}
}
]
}
]
}

View File

@@ -1,222 +0,0 @@
# Zinit RPC Client
This is a V language client for the Zinit process manager, implementing the JSON-RPC API specification for service management operations.
## Overview
Zinit is a process manager that provides service monitoring, dependency management, and system control capabilities. This client provides a comprehensive API to interact with Zinit via its JSON-RPC interface for administrative tasks such as:
- Service lifecycle management (start, stop, monitor, forget)
- Service configuration management (create, delete, get)
- Service status and statistics monitoring
- System operations (shutdown, reboot, HTTP server control)
- Log streaming and monitoring
## Features
- **✅ 100% API Coverage**: Complete implementation of all 18 methods in the Zinit JSON-RPC specification
- **✅ Production Tested**: All methods tested and working against real Zinit instances
- **✅ Type-safe API**: Proper V struct definitions with comprehensive error handling
- **✅ Subscription Support**: Proper handling of streaming/subscription methods
- **✅ Unix Socket Transport**: Reliable communication via Unix domain sockets
- **✅ Comprehensive Documentation**: Extensive documentation with working examples
## Usage
### Basic Example
```v
import freeflowuniverse.herolib.clients.zinit_rpc
// Create a new client
mut client := zinit_rpc.new_client(
name: 'my_client'
socket_path: '/tmp/zinit.sock'
)!
// List all services
services := client.service_list()!
for service_name, state in services {
println('Service: ${service_name}, State: ${state}')
}
// Get detailed status of a specific service
status := client.service_status('redis')!
println('Service: ${status.name}')
println('PID: ${status.pid}')
println('State: ${status.state}')
println('Target: ${status.target}')
// Start a service
client.service_start('redis')!
// Stop a service
client.service_stop('redis')!
```
### Service Configuration Management
```v
import freeflowuniverse.herolib.clients.zinit_rpc
mut client := zinit_rpc.new_client()!
// Create a new service configuration
config := zinit_rpc.ServiceConfig{
exec: '/usr/bin/redis-server'
oneshot: false
log: 'stdout'
env: {
'REDIS_PORT': '6379'
'REDIS_HOST': '0.0.0.0'
}
shutdown_timeout: 30
}
// Create the service
path := client.service_create('redis', config)!
println('Service created at: ${path}')
// Get service configuration
retrieved_config := client.service_get('redis')!
println('Service exec: ${retrieved_config.exec}')
// Delete service configuration
result := client.service_delete('redis')!
println('Delete result: ${result}')
```
### Service Statistics
```v
import freeflowuniverse.herolib.clients.zinit_rpc
mut client := zinit_rpc.new_client()!
// Get service statistics
stats := client.service_stats('redis')!
println('Service: ${stats.name}')
println('PID: ${stats.pid}')
println('Memory Usage: ${stats.memory_usage} bytes')
println('CPU Usage: ${stats.cpu_usage}%')
// Print child process statistics
for child in stats.children {
println('Child PID: ${child.pid}, Memory: ${child.memory_usage}, CPU: ${child.cpu_usage}%')
}
```
### Log Streaming
```v
import freeflowuniverse.herolib.clients.zinit_rpc
mut client := zinit_rpc.new_client()!
// Get current logs for all services
logs := client.stream_current_logs(name: '')!
for log in logs {
println(log)
}
// Get current logs for a specific service
redis_logs := client.stream_current_logs(name: 'redis')!
for log in redis_logs {
println('Redis: ${log}')
}
// Subscribe to log stream (returns subscription ID)
subscription_id := client.stream_subscribe_logs(name: 'redis')!
println('Subscribed to logs with ID: ${subscription_id}')
```
## API Reference
### Service Management Methods
- `service_list()` - List all services and their states
- `service_status(name)` - Get detailed status of a service
- `service_start(name)` - Start a service
- `service_stop(name)` - Stop a service
- `service_monitor(name)` - Start monitoring a service
- `service_forget(name)` - Stop monitoring a service
- `service_kill(name, signal)` - Send signal to a service
### Service Configuration Methods
- `service_create(name, config)` - Create service configuration
- `service_delete(name)` - Delete service configuration
- `service_get(name)` - Get service configuration
### Monitoring Methods
- `service_stats(name)` - Get service statistics
### System Methods
- `system_shutdown()` - Shutdown the system
- `system_reboot()` - Reboot the system
- `system_start_http_server(address)` - Start HTTP server
- `system_stop_http_server()` - Stop HTTP server
### Streaming Methods
- `stream_current_logs(args)` - Get current logs (returns array of log lines)
- `stream_subscribe_logs(args)` - Subscribe to log stream (returns subscription ID)
### Discovery Methods
- `rpc_discover()` - Get OpenRPC specification
## Configuration
### Using the Factory Pattern
```v
import freeflowuniverse.herolib.clients.zinit_rpc
// Get client using factory (recommended)
mut client := zinit_rpc.get()!
// Use the client
services := client.service_list()!
```
### Example Heroscript Configuration
```hero
!!zinit_rpc.configure
name: 'production'
socket_path: '/tmp/zinit.sock'
```
## Error Handling
The client provides comprehensive error handling for all Zinit-specific error codes:
- `-32000`: Service not found
- `-32001`: Service already monitored
- `-32002`: Service is up
- `-32003`: Service is down
- `-32004`: Invalid signal
- `-32005`: Config error
- `-32006`: Shutting down
- `-32007`: Service already exists
- `-32008`: Service file error
```v
import freeflowuniverse.herolib.clients.zinit_rpc
mut client := zinit_rpc.new_client()!
// Handle specific errors
client.service_start('nonexistent') or {
if err.msg().contains('Service not found') {
println('Service does not exist')
} else {
println('Other error: ${err}')
}
}
```

View File

@@ -1,163 +0,0 @@
module zinit_rpc
import freeflowuniverse.herolib.data.encoderhero
import freeflowuniverse.herolib.schemas.jsonrpc
pub const version = '0.0.0'
const singleton = true
const default = false
// Default configuration for Zinit JSON-RPC API
pub const default_socket_path = '/tmp/zinit.sock'
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap]
pub struct ZinitRPC {
pub mut:
name string = 'default'
socket_path string = default_socket_path // Unix socket path for RPC server
rpc_client ?&jsonrpc.Client @[skip]
}
// your checking & initialization code if needed
fn obj_init(mycfg_ ZinitRPC) !ZinitRPC {
mut mycfg := mycfg_
if mycfg.socket_path == '' {
mycfg.socket_path = default_socket_path
}
// For now, we'll initialize the client when needed
// The actual client will be created in the factory
return mycfg
}
// Response structs based on OpenRPC specification
// OpenRPCSpec represents the OpenRPC specification structure
pub struct OpenRPCSpec {
pub mut:
openrpc string @[json: 'openrpc'] // OpenRPC version
info OpenRPCInfo @[json: 'info'] // API information
methods []OpenRPCMethod @[json: 'methods'] // Available methods
servers []OpenRPCServer @[json: 'servers'] // Server information
}
// OpenRPCInfo represents API information
pub struct OpenRPCInfo {
pub mut:
version string @[json: 'version'] // API version
title string @[json: 'title'] // API title
description string @[json: 'description'] // API description
license OpenRPCLicense @[json: 'license'] // License information
}
// OpenRPCLicense represents license information
pub struct OpenRPCLicense {
pub mut:
name string @[json: 'name'] // License name
}
// OpenRPCMethod represents an RPC method
pub struct OpenRPCMethod {
pub mut:
name string @[json: 'name'] // Method name
description string @[json: 'description'] // Method description
// Note: params and result are dynamic and would need more complex handling
}
// OpenRPCServer represents server information
pub struct OpenRPCServer {
pub mut:
name string @[json: 'name'] // Server name
url string @[json: 'url'] // Server URL
}
// ServiceStatus represents detailed status information for a service
pub struct ServiceStatus {
pub mut:
name string @[json: 'name'] // Service name
pid u32 @[json: 'pid'] // Process ID of the running service (if running)
state string @[json: 'state'] // Current state of the service (Running, Success, Error, etc.)
target string @[json: 'target'] // Target state of the service (Up, Down)
after map[string]string @[json: 'after'] // Dependencies of the service and their states
}
// ServiceConfig represents the configuration for a zinit service
pub struct ServiceConfig {
pub mut:
exec string @[json: 'exec'] // Command to run
test string @[json: 'test'] // Test command (optional)
oneshot bool @[json: 'oneshot'] // Whether the service should be restarted (maps to one_shot in Zinit)
after []string @[json: 'after'] // Services that must be running before this one starts
log string @[json: 'log'] // How to handle service output (null, ring, stdout)
env map[string]string @[json: 'env'] // Environment variables for the service
dir string @[json: 'dir'] // Working directory for the service
shutdown_timeout u64 @[json: 'shutdown_timeout'] // Maximum time to wait for service to stop during shutdown
}
// ServiceStats represents memory and CPU usage statistics for a service
pub struct ServiceStats {
pub mut:
name string @[json: 'name'] // Service name
pid u32 @[json: 'pid'] // Process ID of the service
memory_usage u64 @[json: 'memory_usage'] // Memory usage in bytes
cpu_usage f32 @[json: 'cpu_usage'] // CPU usage as a percentage (0-100)
children []ChildStats @[json: 'children'] // Stats for child processes
}
// ChildStats represents statistics for a child process
pub struct ChildStats {
pub mut:
pid u32 @[json: 'pid'] // Process ID of the child process
memory_usage u64 @[json: 'memory_usage'] // Memory usage in bytes
cpu_usage f32 @[json: 'cpu_usage'] // CPU usage as a percentage (0-100)
}
// ServiceCreateParams represents parameters for service_create method
pub struct ServiceCreateParams {
pub mut:
name string @[json: 'name'] // Name of the service to create
content ServiceConfig @[json: 'content'] // Configuration for the service
}
// ServiceKillParams represents parameters for service_kill method
pub struct ServiceKillParams {
pub mut:
name string @[json: 'name'] // Name of the service to kill
signal string @[json: 'signal'] // Signal to send (e.g., SIGTERM, SIGKILL)
}
// LogParams represents parameters for log streaming methods
@[params]
pub struct LogParams {
pub mut:
name string // Optional service name filter
}
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj ZinitRPC) !string {
return encoderhero.encode[ZinitRPC](obj)!
}
pub fn heroscript_loads(heroscript string) !ZinitRPC {
mut obj := encoderhero.decode[ZinitRPC](heroscript)!
return obj
}
// Factory function to create a new ZinitRPC client instance
@[params]
pub struct NewClientArgs {
pub mut:
name string = 'default'
socket_path string = default_socket_path
}
pub fn new_client(args NewClientArgs) !&ZinitRPC {
mut client := ZinitRPC{
name: args.name
socket_path: args.socket_path
}
client = obj_init(client)!
return &client
}

View File

@@ -14,12 +14,10 @@ import time
@end
@end
@if ! args.singleton
__global (
${args.name}_global map[string]&${args.classname}
${args.name}_default string
)
@end
/////////FACTORY
@@ -45,7 +43,7 @@ pub fn new(args ArgsGet) !&${args.classname} {
name: args.name
}
set(obj)!
return &obj
return get(name:args.name)!
}
pub fn get(args ArgsGet) !&${args.classname} {
@@ -76,11 +74,11 @@ pub fn get(args ArgsGet) !&${args.classname} {
// register the config for the future
pub fn set(o ${args.classname}) ! {
set_in_mem(o)!
${args.name}_default = o.name
mut o2:=set_in_mem(o)!
${args.name}_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:${args.name}', o.name, json.encode(o))!
r.hset('context:${args.name}', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -130,10 +128,11 @@ pub fn list(args ArgsList) ![]&${args.classname} {
// only sets in mem, does not set as config
fn set_in_mem(o ${args.classname}) ! {
fn set_in_mem(o ${args.classname}) ! ${args.classname} {
mut o2 := obj_init(o)!
${args.name}_global[o.name] = &o2
${args.name}_default = o.name
${args.name}_global[o2.name] = &o2
${args.name}_default = o2.name
return o2
}

View File

@@ -58,11 +58,11 @@ pub fn get(args ArgsGet) !&CometBFT {
// register the config for the future
pub fn set(o CometBFT) ! {
set_in_mem(o)!
cometbft_default = o.name
mut o2 := set_in_mem(o)!
cometbft_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:cometbft', o.name, json.encode(o))!
r.hset('context:cometbft', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -111,10 +111,11 @@ pub fn list(args ArgsList) ![]&CometBFT {
}
// only sets in mem, does not set as config
fn set_in_mem(o CometBFT) ! {
fn set_in_mem(o CometBFT) !CometBFT {
mut o2 := obj_init(o)!
cometbft_global[o.name] = &o2
cometbft_default = o.name
cometbft_global[o2.name] = &o2
cometbft_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -29,8 +29,7 @@ fn startupcmd() ![]startupmanager.ZProcessNewArgs {
if installer.production {
env = 'production'
}
res << startupmanager.ZProcessNewArgs
{
res << startupmanager.ZProcessNewArgs{
name: 'meilisearch'
cmd: 'meilisearch --no-analytics --http-addr ${installer.host}:${installer.port} --env ${env} --db-path ${installer.path} --master-key ${installer.masterkey}'
startuptype: .zinit

View File

@@ -58,11 +58,11 @@ pub fn get(args ArgsGet) !&MeilisearchInstaller {
// register the config for the future
pub fn set(o MeilisearchInstaller) ! {
set_in_mem(o)!
meilisearch_installer_default = o.name
mut o2 := set_in_mem(o)!
meilisearch_installer_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:meilisearch_installer', o.name, json.encode(o))!
r.hset('context:meilisearch_installer', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -111,10 +111,11 @@ pub fn list(args ArgsList) ![]&MeilisearchInstaller {
}
// only sets in mem, does not set as config
fn set_in_mem(o MeilisearchInstaller) ! {
fn set_in_mem(o MeilisearchInstaller) !MeilisearchInstaller {
mut o2 := obj_init(o)!
meilisearch_installer_global[o.name] = &o2
meilisearch_installer_default = o.name
meilisearch_installer_global[o2.name] = &o2
meilisearch_installer_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -15,8 +15,7 @@ fn startupcmd() ![]startupmanager.ZProcessNewArgs {
podman run --name ${cfg.container_name} -e POSTGRES_USER=${cfg.user} -e POSTGRES_PASSWORD=\"${cfg.password}\" -v ${cfg.volume_path}:/var/lib/postgresql/data -p ${cfg.port}:5432 --health-cmd=\"pg_isready -U ${cfg.user}\" postgres:latest
"
res << startupmanager.ZProcessNewArgs
{
res << startupmanager.ZProcessNewArgs{
name: 'postgresql'
cmd: cmd
startuptype: .zinit

View File

@@ -53,11 +53,11 @@ pub fn get(args ArgsGet) !&Postgresql {
// register the config for the future
pub fn set(o Postgresql) ! {
set_in_mem(o)!
postgresql_default = o.name
mut o2 := set_in_mem(o)!
postgresql_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:postgresql', o.name, json.encode(o))!
r.hset('context:postgresql', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -106,10 +106,11 @@ pub fn list(args ArgsList) ![]&Postgresql {
}
// only sets in mem, does not set as config
fn set_in_mem(o Postgresql) ! {
fn set_in_mem(o Postgresql) !Postgresql {
mut o2 := obj_init(o)!
postgresql_global[o.name] = &o2
postgresql_default = o.name
postgresql_global[o2.name] = &o2
postgresql_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -7,8 +7,7 @@ import os
fn startupcmd() ![]startupmanager.ZProcessNewArgs {
mut res := []startupmanager.ZProcessNewArgs{}
res << startupmanager.ZProcessNewArgs
{
res << startupmanager.ZProcessNewArgs{
name: 'qdrant'
cmd: 'sleep 5 && qdrant --config-path ${os.home_dir()}/hero/var/qdrant/config.yaml'
startuptype: .zinit

View File

@@ -58,11 +58,11 @@ pub fn get(args ArgsGet) !&QDrant {
// register the config for the future
pub fn set(o QDrant) ! {
set_in_mem(o)!
qdrant_installer_default = o.name
mut o2 := set_in_mem(o)!
qdrant_installer_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:qdrant_installer', o.name, json.encode(o))!
r.hset('context:qdrant_installer', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -111,10 +111,11 @@ pub fn list(args ArgsList) ![]&QDrant {
}
// only sets in mem, does not set as config
fn set_in_mem(o QDrant) ! {
fn set_in_mem(o QDrant) !QDrant {
mut o2 := obj_init(o)!
qdrant_installer_global[o.name] = &o2
qdrant_installer_default = o.name
qdrant_installer_global[o2.name] = &o2
qdrant_installer_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -22,8 +22,7 @@ fn startupcmd() ![]startupmanager.ZProcessNewArgs {
}
mut res := []startupmanager.ZProcessNewArgs{}
res << startupmanager.ZProcessNewArgs
{
res << startupmanager.ZProcessNewArgs{
name: 'zdb'
cmd: cmd
startuptype: .zinit

View File

@@ -53,11 +53,11 @@ pub fn get(args ArgsGet) !&ZeroDB {
// register the config for the future
pub fn set(o ZeroDB) ! {
set_in_mem(o)!
zerodb_default = o.name
mut o2 := set_in_mem(o)!
zerodb_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:zerodb', o.name, json.encode(o))!
r.hset('context:zerodb', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -106,10 +106,11 @@ pub fn list(args ArgsList) ![]&ZeroDB {
}
// only sets in mem, does not set as config
fn set_in_mem(o ZeroDB) ! {
fn set_in_mem(o ZeroDB) !ZeroDB {
mut o2 := obj_init(o)!
zerodb_global[o.name] = &o2
zerodb_default = o.name
zerodb_global[o2.name] = &o2
zerodb_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -16,8 +16,7 @@ fn startupcmd() ![]startupmanager.ZProcessNewArgs {
mut args := get()!
mut res := []startupmanager.ZProcessNewArgs{}
cmd := "coredns -conf '${args.config_path}'"
res << startupmanager.ZProcessNewArgs
{
res << startupmanager.ZProcessNewArgs{
name: 'coredns'
cmd: cmd
}

View File

@@ -53,11 +53,11 @@ pub fn get(args ArgsGet) !&CoreDNS {
// register the config for the future
pub fn set(o CoreDNS) ! {
set_in_mem(o)!
coredns_default = o.name
mut o2 := set_in_mem(o)!
coredns_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:coredns', o.name, json.encode(o))!
r.hset('context:coredns', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -106,10 +106,11 @@ pub fn list(args ArgsList) ![]&CoreDNS {
}
// only sets in mem, does not set as config
fn set_in_mem(o CoreDNS) ! {
fn set_in_mem(o CoreDNS) !CoreDNS {
mut o2 := obj_init(o)!
coredns_global[o.name] = &o2
coredns_default = o.name
coredns_global[o2.name] = &o2
coredns_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -89,8 +89,7 @@ fn upload() ! {}
fn startupcmd() ![]startupmanager.ZProcessNewArgs {
mut cfg := get()!
mut res := []startupmanager.ZProcessNewArgs{}
res << startupmanager.ZProcessNewArgs
{
res << startupmanager.ZProcessNewArgs{
name: 'gitea'
cmd: 'gitea server'
env: {

View File

@@ -53,11 +53,11 @@ pub fn get(args ArgsGet) !&GiteaServer {
// register the config for the future
pub fn set(o GiteaServer) ! {
set_in_mem(o)!
gitea_default = o.name
mut o2 := set_in_mem(o)!
gitea_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:gitea', o.name, json.encode(o))!
r.hset('context:gitea', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -106,10 +106,11 @@ pub fn list(args ArgsList) ![]&GiteaServer {
}
// only sets in mem, does not set as config
fn set_in_mem(o GiteaServer) ! {
fn set_in_mem(o GiteaServer) !GiteaServer {
mut o2 := obj_init(o)!
gitea_global[o.name] = &o2
gitea_default = o.name
gitea_global[o2.name] = &o2
gitea_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -48,8 +48,7 @@ fn generate_keys() ! {
fn startupcmd() ![]startupmanager.ZProcessNewArgs {
mut res := []startupmanager.ZProcessNewArgs{}
mut installer := get()!
res << startupmanager.ZProcessNewArgs
{
res << startupmanager.ZProcessNewArgs{
name: 'livekit'
cmd: 'livekit-server --config ${installer.configpath} --bind 0.0.0.0'
startuptype: .zinit

View File

@@ -58,11 +58,11 @@ pub fn get(args ArgsGet) !&LivekitServer {
// register the config for the future
pub fn set(o LivekitServer) ! {
set_in_mem(o)!
livekit_default = o.name
mut o2 := set_in_mem(o)!
livekit_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:livekit', o.name, json.encode(o))!
r.hset('context:livekit', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -111,10 +111,11 @@ pub fn list(args ArgsList) ![]&LivekitServer {
}
// only sets in mem, does not set as config
fn set_in_mem(o LivekitServer) ! {
fn set_in_mem(o LivekitServer) !LivekitServer {
mut o2 := obj_init(o)!
livekit_global[o.name] = &o2
livekit_default = o.name
livekit_global[o2.name] = &o2
livekit_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -16,7 +16,7 @@ fn startupcmd() ![]startupmanager.ZProcessNewArgs {
if core.is_linux()! {
res << startupmanager.ZProcessNewArgs{
name: 'zinit'
cmd: '/usr/local/bin/zinit init'
cmd: '/usr/local/bin/zinit init ~/hero/cfg/zinit'
startuptype: .systemd
start: true
restart: true

View File

@@ -58,11 +58,11 @@ pub fn get(args ArgsGet) !&MyceliumInstaller {
// register the config for the future
pub fn set(o MyceliumInstaller) ! {
set_in_mem(o)!
mycelium_installer_default = o.name
mut o2 := set_in_mem(o)!
mycelium_installer_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:mycelium_installer', o.name, json.encode(o))!
r.hset('context:mycelium_installer', o2.name, json.encode(o2))!
}
// does the config exists?
@@ -111,10 +111,11 @@ pub fn list(args ArgsList) ![]&MyceliumInstaller {
}
// only sets in mem, does not set as config
fn set_in_mem(o MyceliumInstaller) ! {
fn set_in_mem(o MyceliumInstaller) !MyceliumInstaller {
mut o2 := obj_init(o)!
mycelium_installer_global[o.name] = &o2
mycelium_installer_default = o.name
mycelium_installer_global[o2.name] = &o2
mycelium_installer_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {

View File

@@ -0,0 +1,42 @@
module jsonrpcmodel
// OpenRPCSpec represents the OpenRPC specification structure
pub struct OpenRPCSpec {
pub mut:
openrpc string @[json: 'openrpc'] // OpenRPC version
info OpenRPCInfo @[json: 'info'] // API information
methods []OpenRPCMethod @[json: 'methods'] // Available methods
servers []OpenRPCServer @[json: 'servers'] // Server information
}
// OpenRPCInfo represents API information
pub struct OpenRPCInfo {
pub mut:
version string @[json: 'version'] // API version
title string @[json: 'title'] // API title
description string @[json: 'description'] // API description
license OpenRPCLicense @[json: 'license'] // License information
}
// OpenRPCLicense represents license information
pub struct OpenRPCLicense {
pub mut:
name string @[json: 'name'] // License name
}
// OpenRPCMethod represents an RPC method
pub struct OpenRPCMethod {
pub mut:
name string @[json: 'name'] // Method name
description string @[json: 'description'] // Method description
// Note: params and result are dynamic and would need more complex handling
}
// OpenRPCServer represents server information
pub struct OpenRPCServer {
pub mut:
name string @[json: 'name'] // Server name
url string @[json: 'url'] // Server URL
}