diff --git a/examples/example.v b/examples/example_old.v similarity index 100% rename from examples/example.v rename to examples/example_old.v diff --git a/examples/zinit_client_example.vsh b/examples/zinit_client_example.vsh new file mode 100755 index 0000000..3c409a2 --- /dev/null +++ b/examples/zinit_client_example.vsh @@ -0,0 +1,124 @@ +#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run + +import freeflowuniverse.heroweb.clients.zinit +import x.json2 + +// Create a new Zinit client with the default socket path +mut client := zinit.new_default_client() + +println('Connected to Zinit via OpenRPC') + +// Example 1: Get the OpenRPC API specification +println('\n=== Getting API Specification ===') +api_spec_response := client.rpc_discover() or { + println('Error getting API spec: ${err}') + return +} +println('API Specification (first 100 chars): ${json2.encode(api_spec_response.spec)[..100]}...') + +// Example 2: List all services +println('\n=== Listing Services ===') +service_list_response := client.service_list() or { + println('Error listing services: ${err}') + return +} +println('Services:') +for name, state in service_list_response.services { + println('- ${name}: ${state}') +} + +// Example 3: Get detailed status of a service (if any exist) +if service_list_response.services.len > 0 { + service_name := service_list_response.services.keys()[0] + println('\n=== Getting Status for Service: ${service_name} ===') + + status := client.service_status(service_name) or { + println('Error getting status: ${err}') + return + } + + println('Service Status:') + println('- Name: ${status.name}') + println('- PID: ${status.pid}') + println('- State: ${status.state}') + println('- Target: ${status.target}') + println('- Dependencies:') + for dep_name, dep_state in status.after { + println(' - ${dep_name}: ${dep_state}') + } + + // Example 4: Get service stats + println('\n=== Getting Stats for Service: ${service_name} ===') + stats := client.service_stats(service_name) or { + println('Error getting stats: ${err}') + println('Note: Stats are only available for running services') + return + } + + println('Service Stats:') + println('- Memory Usage: ${stats.memory_usage} bytes (${zinit.format_memory_usage(stats.memory_usage)})') + println('- CPU Usage: ${stats.cpu_usage}% (${zinit.format_cpu_usage(stats.cpu_usage)})') + if stats.children.len > 0 { + println('- Child Processes:') + for child in stats.children { + println(' - PID: ${child.pid}, Memory: ${zinit.format_memory_usage(child.memory_usage)}, CPU: ${zinit.format_cpu_usage(child.cpu_usage)}') + } + } + + // Example 5: Get logs for a service + println('\n=== Getting Logs for Service: ${service_name} ===') + logs_response := client.stream_current_logs(service_name) or { + println('Error getting logs: ${err}') + return + } + println('Service logs:') + for log in logs_response.logs { + println('- ${log}') + } +} else { + println('\nNo services found to query') +} + +// Example 6: Create a new service (commented out for safety) +/* +println('\n=== Creating a New Service ===') +new_service_config := zinit.ServiceConfig{ + exec: '/bin/echo "Hello from Zinit"' + oneshot: true + after: []string{} + log: zinit.log_stdout + env: { + 'ENV_VAR': 'value' + } +} + +create_response := client.service_create('example_service', new_service_config) or { + println('Error creating service: ${err}') + return +} +println('Service created: ${create_response.path}') + +// Start the service +client.service_start('example_service') or { + println('Error starting service: ${err}') + return +} +println('Service started') + +// Delete the service when done +client.service_stop('example_service') or { + println('Error stopping service: ${err}') + return +} +client.service_forget('example_service') or { + println('Error forgetting service: ${err}') + return +} +delete_response := client.service_delete('example_service') or { + println('Error deleting service: ${err}') + return +} +println('Service deleted: ${delete_response.result}') +*/ + +println('\nZinit OpenRPC client example completed') diff --git a/examples/zinit_rpc_example.vsh b/examples/zinit_rpc_example.vsh new file mode 100755 index 0000000..b43f904 --- /dev/null +++ b/examples/zinit_rpc_example.vsh @@ -0,0 +1,66 @@ +#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run + +import freeflowuniverse.herolib.schemas.jsonrpc +import freeflowuniverse.herolib.clients.zinit +import json + +// We'll use the ServiceStatusResponse from the zinit module +// No need to define our own struct + +// Create a client using the Unix socket transport +mut cl := jsonrpc.new_unix_socket_client("/tmp/zinit.sock") + +// Example 1: Discover the API using rpc_discover +// Create a request for rpc_discover method with empty parameters +discover_request := jsonrpc.new_request_generic('rpc.discover', []string{}) + +// Send the request and receive the OpenRPC specification as a JSON string +println('Sending rpc_discover request...') +println('This will return the OpenRPC specification for the API') + +// Use map[string]string for the result to avoid json2.Any issues +api_spec_raw := cl.send[[]string, string](discover_request)! + +// Parse the JSON string manually +println('API Specification (raw):') +println(api_spec_raw) + +// Example 2: List all services +// Create a request for service_list method with empty parameters +list_request := jsonrpc.new_request_generic('service_list', []string{}) + +// Send the request and receive a map of service names to states +println('\nSending service_list request...') +service_list := cl.send[[]string, map[string]string](list_request)! + +// Display the service list +println('Service List:') +println(service_list) + +// Example 3: Get status of a specific service +// First, check if we have any services to query +if service_list.len > 0 { + // Get the first service name from the list + service_name := service_list.keys()[0] + + // Create a request for service_status method with the service name as parameter + // The parameter for service_status is a single string (service name) + status_request := jsonrpc.new_request_generic('service_status', service_name) + + // Send the request and receive a ServiceStatusResponse object + println('\nSending service_status request for service: $service_name') + service_status := cl.send[string, zinit.ServiceStatusResponse](status_request)! + + // Display the service status details + println('Service Status:') + println('- Name: ${service_status.name}') + println('- PID: ${service_status.pid}') + println('- State: ${service_status.state}') + println('- Target: ${service_status.target}') + println('- Dependencies:') + for dep_name, dep_state in service_status.after { + println(' - $dep_name: $dep_state') + } +} else { + println('\nNo services found to query status') +} diff --git a/src/agentui/controllers/fake_data.v b/src/agentui/controllers/fake_data.v new file mode 100644 index 0000000..e69de29 diff --git a/src/agentui/controllers/process_ctrl.v b/src/agentui/controllers/process_ctrl.v index 2288852..8245268 100644 --- a/src/agentui/controllers/process_ctrl.v +++ b/src/agentui/controllers/process_ctrl.v @@ -7,13 +7,13 @@ pub: } // get_all_processes returns all processes in the system -pub fn (pc &ProcessController) get_all_processes() []Process { +pub fn (pc &ProcessController) get_all_processes() ![]Process { // For now using fake data, will be replaced with openrpc calls return get_all_processes() } // get_process_by_pid returns a specific process by PID -pub fn (pc &ProcessController) get_process_by_pid(pid int) ?Process { +pub fn (pc &ProcessController) get_process_by_pid(pid int) !Process { processes := get_all_processes() for process in processes { if process.pid == pid { diff --git a/src/agentui/controllers/rpc_ctrl.v b/src/agentui/controllers/rpc_ctrl.v index fe3ffad..53636fc 100644 --- a/src/agentui/controllers/rpc_ctrl.v +++ b/src/agentui/controllers/rpc_ctrl.v @@ -1,4 +1,6 @@ module controllers +import freeflowuniverse.herolib.schemas.openrpc + // OpenRPCController handles OpenRPC-related operations pub struct OpenRPCController { pub: @@ -12,7 +14,7 @@ pub fn (oc &OpenRPCController) get_all_specs() []OpenRPCSpec { } // get_spec_by_name returns a specific OpenRPC specification by name -pub fn (oc &OpenRPCController) get_spec_by_name(name string) ?OpenRPCSpec { +pub fn (oc &OpenRPCController) get_spec_by_name(name string) !openrpc.OpenRPC { specs := get_all_openrpc_specs() for spec in specs { if spec.title.to_lower() == name.to_lower() { diff --git a/src/clients/zinit/README.md b/src/clients/zinit/README.md deleted file mode 100644 index 2aee592..0000000 --- a/src/clients/zinit/README.md +++ /dev/null @@ -1,406 +0,0 @@ -# Zinit Client Module - -A well-documented V (Vlang) client library for interacting with the Zinit service manager via JSON-RPC over Unix socket. - -## Overview - -This module provides a complete client implementation for the Zinit JSON-RPC API, allowing you to manage services, monitor system resources, and control the Zinit service manager programmatically. - -## Features - -- **Service Management**: Start, stop, monitor, and forget services -- **Service Configuration**: Create, delete, and retrieve service configurations -- **System Control**: Shutdown, reboot, and HTTP server management -- **Monitoring**: Get service status, statistics, and resource usage -- **Logging**: Stream current logs and subscribe to log updates -- **Error Handling**: Comprehensive error handling with detailed error messages -- **Type Safety**: Strongly typed structs for all API responses -- **Connection Management**: Automatic connection handling with proper cleanup - -## Installation - -Simply import the module in your V project: - -```v -import zinit -``` - -## Quick Start - -```v -import zinit - -fn main() { - // Create a client with default socket path (/tmp/zinit.sock) - mut client := zinit.new_default_client() - - // Or specify a custom socket path - // mut client := zinit.new_client('/custom/path/to/zinit.sock') - - defer { - client.disconnect() - } - - // List all services - services := client.service_list() or { - eprintln('Error: ${err}') - return - } - - for name, state in services { - println('${name}: ${state}') - } -} -``` - -## API Reference - -### Client Creation - -#### `new_client(socket_path string) &Client` -Creates a new zinit client with a custom socket path. - -#### `new_default_client() &Client` -Creates a new zinit client with the default socket path (`/tmp/zinit.sock`). - -### Connection Management - -#### `connect() !` -Establishes a connection to the zinit Unix socket. Called automatically by API methods. - -#### `disconnect()` -Closes the connection to the zinit Unix socket. Should be called when done with the client. - -### Service Management - -#### `service_list() !map[string]string` -Lists all services managed by Zinit. - -**Returns**: A map of service names to their current states. - -```v -services := client.service_list()! -for name, state in services { - println('${name}: ${state}') -} -``` - -#### `service_status(name string) !ServiceStatus` -Shows detailed status information for a specific service. - -**Parameters**: -- `name`: The name of the service - -**Returns**: `ServiceStatus` struct with detailed information. - -```v -status := client.service_status('redis')! -println('Service: ${status.name}') -println('State: ${status.state}') -println('PID: ${status.pid}') -``` - -#### `service_start(name string) !` -Starts a service. - -**Parameters**: -- `name`: The name of the service to start - -#### `service_stop(name string) !` -Stops a service. - -**Parameters**: -- `name`: The name of the service to stop - -#### `service_monitor(name string) !` -Starts monitoring a service. The service configuration is loaded from the config directory. - -**Parameters**: -- `name`: The name of the service to monitor - -#### `service_forget(name string) !` -Stops monitoring a service. You can only forget a stopped service. - -**Parameters**: -- `name`: The name of the service to forget - -#### `service_kill(name string, signal string) !` -Sends a signal to a running service. - -**Parameters**: -- `name`: The name of the service to send the signal to -- `signal`: The signal to send (e.g., "SIGTERM", "SIGKILL") - -### Service Configuration - -#### `service_create(name string, config ServiceConfig) !string` -Creates a new service configuration file. - -**Parameters**: -- `name`: The name of the service to create -- `config`: The service configuration - -**Returns**: Result message. - -```v -config := zinit.ServiceConfig{ - exec: '/usr/bin/redis-server' - oneshot: false - log: 'stdout' - env: { - 'REDIS_PORT': '6379' - } - shutdown_timeout: 30 -} - -result := client.service_create('redis', config)! -println('Service created: ${result}') -``` - -#### `service_delete(name string) !string` -Deletes a service configuration file. - -**Parameters**: -- `name`: The name of the service to delete - -**Returns**: Result message. - -#### `service_get(name string) !ServiceConfig` -Gets a service configuration file. - -**Parameters**: -- `name`: The name of the service to get - -**Returns**: `ServiceConfig` struct with the service configuration. - -### Service Statistics - -#### `service_stats(name string) !ServiceStats` -Gets memory and CPU usage statistics for a service. - -**Parameters**: -- `name`: The name of the service to get stats for - -**Returns**: `ServiceStats` struct with usage information. - -```v -stats := client.service_stats('redis')! -println('Memory Usage: ${stats.memory_usage / 1024 / 1024} MB') -println('CPU Usage: ${stats.cpu_usage}%') -``` - -### System Operations - -#### `system_shutdown() !` -Stops all services and powers off the system. - -⚠️ **Warning**: This will actually shut down the system! - -#### `system_reboot() !` -Stops all services and reboots the system. - -⚠️ **Warning**: This will actually reboot the system! - -#### `system_start_http_server(address string) !string` -Starts an HTTP/RPC server at the specified address. - -**Parameters**: -- `address`: The network address to bind the server to (e.g., "127.0.0.1:8080") - -**Returns**: Result message. - -#### `system_stop_http_server() !` -Stops the HTTP/RPC server if running. - -### Logging - -#### `stream_current_logs(name ?string) ![]string` -Gets current logs from zinit and monitored services. - -**Parameters**: -- `name`: Optional service name filter. If provided, only logs from this service will be returned. - -**Returns**: Array of log strings. - -```v -// Get all logs -all_logs := client.stream_current_logs(none)! - -// Get logs for a specific service -redis_logs := client.stream_current_logs('redis')! -``` - -#### `stream_subscribe_logs(name ?string) !string` -Subscribes to log messages generated by zinit and monitored services. - -**Parameters**: -- `name`: Optional service name filter. - -**Returns**: A single log message. - -**Note**: For continuous streaming, call this method repeatedly. - -### API Discovery - -#### `rpc_discover() !map[string]interface{}` -Returns the OpenRPC specification for the API. - -**Returns**: The complete OpenRPC specification as a map. - -## Data Types - -### ServiceConfig -Represents the configuration for a zinit service. - -```v -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 -} -``` - -### ServiceStatus -Represents the detailed status information for a service. - -```v -struct ServiceStatus { -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 -} -``` - -### ServiceStats -Represents memory and CPU usage statistics for a service. - -```v -struct ServiceStats { -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 []ChildStats // Stats for child processes -} -``` - -### ChildStats -Represents statistics for a child process. - -```v -struct ChildStats { -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) -} -``` - -## Error Handling - -The client provides comprehensive error handling through V's error system. All API methods that can fail return a result type (`!`). - -### ZinitError -Custom error type for zinit-specific errors. - -```v -struct ZinitError { -pub mut: - code int // Error code - message string // Error message - data string // Additional error data -} -``` - -Common 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 - -## Examples - -See `example.v` for comprehensive usage examples covering all API methods. - -### Basic Service Management - -```v -import zinit - -fn manage_service() ! { - mut client := zinit.new_default_client() - defer { client.disconnect() } - - // Create a service - config := zinit.ServiceConfig{ - exec: '/usr/bin/nginx' - oneshot: false - log: 'stdout' - after: ['network'] - } - - client.service_create('nginx', config)! - client.service_monitor('nginx')! - client.service_start('nginx')! - - // Check status - status := client.service_status('nginx')! - println('Nginx is ${status.state}') - - // Get statistics - stats := client.service_stats('nginx')! - println('Memory: ${stats.memory_usage / 1024 / 1024} MB') -} -``` - -### Log Monitoring - -```v -import zinit -import time - -fn monitor_logs() ! { - mut client := zinit.new_default_client() - defer { client.disconnect() } - - // Get current logs - logs := client.stream_current_logs(none)! - for log in logs { - println(log) - } - - // Subscribe to new logs (simplified example) - for i in 0..10 { - log_entry := client.stream_subscribe_logs(none) or { continue } - println('New log: ${log_entry}') - time.sleep(1 * time.second) - } -} -``` - -## Thread Safety - -The client is not thread-safe. If you need to use it from multiple threads, you should create separate client instances or implement your own synchronization. - -## Requirements - -- V compiler -- Unix-like operating system (for Unix socket support) -- Running Zinit service manager - -## License - -This module follows the same license as the parent project. diff --git a/src/clients/zinit/client.v b/src/clients/zinit/client.v deleted file mode 100644 index 7ca54e3..0000000 --- a/src/clients/zinit/client.v +++ /dev/null @@ -1,211 +0,0 @@ -module zinit - -import net.unix -import json -import time - -// 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) !ServiceStatus { - params := [name] - response := c.send_request('service_status', params)! - - result_map := response.result as map[string]interface{} - - mut after_map := map[string]string{} - if after_raw := result_map['after'] { - if after_obj := after_raw as map[string]interface{} { - for key, value in after_obj { - after_map[key] = value.str() - } - } - } - - return ServiceStatus{ - name: result_map['name'] or { '' }.str() - pid: result_map['pid'] or { 0 }.int() - state: result_map['state'] or { '' }.str() - target: result_map['target'] or { '' }.str() - after: after_map - } -} - -// service_start starts a service -// name: the name of the service to start -pub fn (mut c Client) service_start(name string) ! { - params := [name] - c.send_request('service_start', params)! -} - -// service_stop stops a service -// name: the name of the service to stop -pub fn (mut c Client) service_stop(name string) ! { - params := [name] - c.send_request('service_stop', params)! -} - -// 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) ! { - params := [name] - c.send_request('service_monitor', params)! -} - -// 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) ! { - params := [name] - c.send_request('service_forget', params)! -} - -// 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 := [name, signal] - c.send_request('service_kill', params)! -} - -// 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) !string { - params := [name, config] - response := c.send_request('service_create', params)! - return response.result.str() -} - -// service_delete deletes a service configuration file -// name: the name of the service to delete -pub fn (mut c Client) service_delete(name string) !string { - params := [name] - response := c.send_request('service_delete', params)! - return response.result.str() -} - -// service_get gets a service configuration file -// name: the name of the service to get -pub fn (mut c Client) service_get(name string) !ServiceConfig { - params := [name] - response := c.send_request('service_get', params)! - - result_map := response.result as map[string]interface{} - - mut after_list := []string{} - if after_raw := result_map['after'] { - if after_array := after_raw as []interface{} { - for item in after_array { - after_list << item.str() - } - } - } - - mut env_map := map[string]string{} - if env_raw := result_map['env'] { - if env_obj := env_raw as map[string]interface{} { - for key, value in env_obj { - env_map[key] = value.str() - } - } - } - - return ServiceConfig{ - exec: result_map['exec'] or { '' }.str() - oneshot: result_map['oneshot'] or { false }.bool() - after: after_list - log: result_map['log'] or { '' }.str() - env: env_map - shutdown_timeout: result_map['shutdown_timeout'] or { 0 }.int() - } -} - -// 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) !ServiceStats { - params := [name] - response := c.send_request('service_stats', params)! - - result_map := response.result as map[string]interface{} - - mut children_list := []ChildStats{} - if children_raw := result_map['children'] { - if children_array := children_raw as []interface{} { - for child_raw in children_array { - if child_map := child_raw as map[string]interface{} { - children_list << ChildStats{ - pid: child_map['pid'] or { 0 }.int() - memory_usage: child_map['memory_usage'] or { i64(0) }.i64() - cpu_usage: child_map['cpu_usage'] or { 0.0 }.f64() - } - } - } - } - } - - return ServiceStats{ - name: result_map['name'] or { '' }.str() - pid: result_map['pid'] or { 0 }.int() - memory_usage: result_map['memory_usage'] or { i64(0) }.i64() - cpu_usage: result_map['cpu_usage'] or { 0.0 }.f64() - children: children_list - } -} - -// system_shutdown stops all services and powers off the system -pub fn (mut c Client) system_shutdown() ! { - c.send_request('system_shutdown', []interface{})! -} - -// system_reboot stops all services and reboots the system -pub fn (mut c Client) system_reboot() ! { - c.send_request('system_reboot', []interface{})! -} - -// 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) !string { - params := [address] - response := c.send_request('system_start_http_server', params)! - return response.result.str() -} - -// system_stop_http_server stops the HTTP/RPC server if running -pub fn (mut c Client) system_stop_http_server() ! { - c.send_request('system_stop_http_server', []interface{})! -} - -// 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(name ?string) ![]string { - mut params := []interface{} - if service_name := name { - params << service_name - } - - response := c.send_request('stream_currentLogs', params)! - - if logs_array := response.result as []interface{} { - mut logs := []string{} - for log_entry in logs_array { - logs << log_entry.str() - } - return logs - } - - return []string{} -} - -// 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 -// Note: This method returns a single log message. For continuous streaming, call this method repeatedly -pub fn (mut c Client) stream_subscribe_logs(name ?string) !string { - mut params := []interface{} - if service_name := name { - params << service_name - } - - response := c.send_request('stream_subscribeLogs', params)! - return response.result.str() -} diff --git a/src/clients/zinit/openrpc.json b/src/clients/zinit/openrpc.json deleted file mode 100644 index 65cc7af..0000000 --- a/src/clients/zinit/openrpc.json +++ /dev/null @@ -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" - } - } - ] - } - ] -} \ No newline at end of file diff --git a/src/clients/zinit/openrpc.v b/src/clients/zinit/openrpc.v deleted file mode 100644 index 0172eb0..0000000 --- a/src/clients/zinit/openrpc.v +++ /dev/null @@ -1,161 +0,0 @@ -module zinit - -import freeflowuniverse.herolib.schemas.jsonrpc -import net.unix -import time -import json - -// UnixSocketTransport implements the jsonrpc.IRPCTransportClient interface for Unix domain sockets -struct UnixSocketTransport { -mut: - socket_path string -} - -// new_unix_socket_transport creates a new Unix socket transport -fn new_unix_socket_transport(socket_path string) &UnixSocketTransport { - return &UnixSocketTransport{ - socket_path: socket_path - } -} - -// send implements the jsonrpc.IRPCTransportClient interface -fn (mut t UnixSocketTransport) send(request string, params jsonrpc.SendParams) !string { - // Create a Unix domain socket client - mut socket := unix.connect_stream(t.socket_path)! - defer { socket.close() or {} } - - // Set timeout if specified - if params.timeout > 0 { - socket.set_read_timeout(params.timeout * time.second) - socket.set_write_timeout(params.timeout * time.second) - } - - // Send the request - socket.write_string(request + '\n')! - - // Read the response - mut response := '' - mut buf := []u8{len: 4096} - - for { - bytes_read := socket.read(mut buf)! - if bytes_read <= 0 { - break - } - response += buf[..bytes_read].bytestr() - - // Check if we've received a complete JSON response - if response.ends_with('}') { - break - } - } - - return response -} - -// Client provides a client interface to the zinit JSON-RPC API over Unix socket -@[heap] -pub struct Client { -mut: - socket_path string - rpc_client &jsonrpc.Client - request_id int -} - -// new_client creates a new zinit client instance -// socket_path: path to the Unix socket (default: /tmp/zinit.sock) -pub fn new_client(socket_path string) &Client { - mut transport := new_unix_socket_transport(socket_path) - mut rpc_client := jsonrpc.new_client(transport) - - return &Client{ - socket_path: socket_path - rpc_client: rpc_client - request_id: 0 - } -} - -// new_default_client creates a new zinit client with default socket path -pub fn new_default_client() &Client { - return new_client('/tmp/zinit.sock') -} - -// rpc_discover returns the OpenRPC specification for the API -pub fn (mut c Client) rpc_discover() !map[string]json.Any { - send_params := jsonrpc.SendParams{ - timeout: 30 - retry: 1 - } - - request := jsonrpc.new_request_generic('rpc_discover', []string{}) - result := c.rpc_client.send[[]string, map[string]json.Any](request, send_params)! - - return result -} - -// 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 { - send_params := jsonrpc.SendParams{ - timeout: 30 - retry: 1 - } - - request := jsonrpc.new_request_generic('service_list', []string{}) - result := c.rpc_client.send[[]string, map[string]string](request, send_params)! - - return result -} - -// 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) !ServiceStatus { - send_params := jsonrpc.SendParams{ - timeout: 30 - retry: 1 - } - - request := jsonrpc.new_request_generic('service_status', [name]) - result_map := c.rpc_client.send[[string], map[string]json.Any](request, send_params)! - - mut after_map := map[string]string{} - if after_raw := result_map['after'] { - if after_raw is map[string]json.Any { - for key, value in after_raw { - after_map[key] = value.str() - } - } - } - - return ServiceStatus{ - name: result_map['name'].str() - pid: result_map['pid'].int() - state: result_map['state'].str() - target: result_map['target'].str() - after: after_map - } -} - -// service_start starts a service -// name: the name of the service to start -pub fn (mut c Client) service_start(name string) ! { - send_params := jsonrpc.SendParams{ - timeout: 30 - retry: 1 - } - - request := jsonrpc.new_request_generic('service_start', [name]) - c.rpc_client.send[[string], json.Any](request, send_params)! -} - -// service_stop stops a service -// name: the name of the service to stop -pub fn (mut c Client) service_stop(name string) ! { - send_params := jsonrpc.SendParams{ - timeout: 30 - retry: 1 - } - - request := jsonrpc.new_request_generic('service_stop', [name]) - c.rpc_client.send[[string], json.Any](request, send_params)! -} diff --git a/src/clients/zinit/test.v b/src/clients/zinit/test.v deleted file mode 100644 index 299e9d3..0000000 --- a/src/clients/zinit/test.v +++ /dev/null @@ -1,204 +0,0 @@ -module zinit - -import time - -// Basic tests for the zinit client module -// Note: These tests require a running zinit instance at /tmp/zinit.sock - -// test_client_creation tests basic client creation -fn test_client_creation() { - // Test default client creation - client1 := new_default_client() - assert client1.socket_path == '/tmp/zinit.sock' - - // Test custom client creation - client2 := new_client('/custom/path/zinit.sock') - assert client2.socket_path == '/custom/path/zinit.sock' - - println('✓ Client creation tests passed') -} - -// test_service_config tests ServiceConfig struct -fn test_service_config() { - config := ServiceConfig{ - exec: '/usr/bin/test' - oneshot: true - after: ['dependency1', 'dependency2'] - log: 'stdout' - env: { - 'TEST_VAR': 'test_value' - 'PATH': '/usr/bin:/bin' - } - shutdown_timeout: 30 - } - - assert config.exec == '/usr/bin/test' - assert config.oneshot == true - assert config.after.len == 2 - assert config.after[0] == 'dependency1' - assert config.env['TEST_VAR'] == 'test_value' - assert config.shutdown_timeout == 30 - - println('✓ ServiceConfig tests passed') -} - -// test_service_status tests ServiceStatus struct -fn test_service_status() { - mut after_map := map[string]string{} - after_map['dep1'] = 'Running' - after_map['dep2'] = 'Success' - - status := ServiceStatus{ - name: 'test_service' - pid: 1234 - state: 'Running' - target: 'Up' - after: after_map - } - - assert status.name == 'test_service' - assert status.pid == 1234 - assert status.state == 'Running' - assert status.target == 'Up' - assert status.after['dep1'] == 'Running' - - println('✓ ServiceStatus tests passed') -} - -// test_service_stats tests ServiceStats and ChildStats structs -fn test_service_stats() { - child1 := ChildStats{ - pid: 1235 - memory_usage: 1024 * 1024 // 1MB - cpu_usage: 2.5 - } - - child2 := ChildStats{ - pid: 1236 - memory_usage: 2 * 1024 * 1024 // 2MB - cpu_usage: 1.2 - } - - stats := ServiceStats{ - name: 'test_service' - pid: 1234 - memory_usage: 10 * 1024 * 1024 // 10MB - cpu_usage: 5.0 - children: [child1, child2] - } - - assert stats.name == 'test_service' - assert stats.pid == 1234 - assert stats.memory_usage == 10 * 1024 * 1024 - assert stats.cpu_usage == 5.0 - assert stats.children.len == 2 - assert stats.children[0].pid == 1235 - assert stats.children[1].memory_usage == 2 * 1024 * 1024 - - println('✓ ServiceStats tests passed') -} - -// test_zinit_error tests ZinitError struct and error message -fn test_zinit_error() { - error := ZinitError{ - code: -32000 - message: 'Service not found' - data: 'service name "unknown" unknown' - } - - assert error.code == -32000 - assert error.message == 'Service not found' - assert error.data == 'service name "unknown" unknown' - - error_msg := error.msg() - assert error_msg.contains('Zinit Error -32000') - assert error_msg.contains('Service not found') - assert error_msg.contains('service name "unknown" unknown') - - println('✓ ZinitError tests passed') -} - -// test_connection_handling tests connection management (without actual connection) -fn test_connection_handling() { - mut client := new_default_client() - - // Initially no connection - assert client.conn == none - - // Test disconnect on non-connected client (should not panic) - client.disconnect() - assert client.conn == none - - println('✓ Connection handling tests passed') -} - -// integration_test_basic performs basic integration tests if zinit is available -fn integration_test_basic() { - mut client := new_default_client() - defer { - client.disconnect() - } - - println('Running integration tests (requires running zinit)...') - - // Test RPC discovery - spec := client.rpc_discover() or { - println('⚠ Integration test skipped: zinit not available (${err})') - return - } - - println('✓ RPC discovery successful') - - // Test service list - services := client.service_list() or { - println('✗ Service list failed: ${err}') - return - } - - println('✓ Service list successful (${services.len} services)') - - // If there are services, test getting status of the first one - if services.len > 0 { - service_name := services.keys()[0] - status := client.service_status(service_name) or { - println('⚠ Could not get status for ${service_name}: ${err}') - return - } - println('✓ Service status for ${service_name}: ${status.state}') - } - - // Test getting current logs - logs := client.stream_current_logs(none) or { - println('⚠ Could not get logs: ${err}') - return - } - - println('✓ Log streaming successful (${logs.len} log entries)') - - println('✓ All integration tests passed') -} - -// run_all_tests runs all test functions -pub fn run_all_tests() { - println('Running Zinit Client Tests...\n') - - // Unit tests - test_client_creation() - test_service_config() - test_service_status() - test_service_stats() - test_zinit_error() - test_connection_handling() - - println('\n--- Unit Tests Complete ---\n') - - // Integration tests (optional, requires running zinit) - integration_test_basic() - - println('\n--- All Tests Complete ---') -} - -// main function for running tests directly -fn main() { - run_all_tests() -} diff --git a/src/clients/zinit/types.v b/src/clients/zinit/types.v deleted file mode 100644 index 62adf03..0000000 --- a/src/clients/zinit/types.v +++ /dev/null @@ -1,53 +0,0 @@ -module zinit - -// 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 -} - -// ServiceStatus represents the detailed status information for a service -pub struct ServiceStatus { -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 -} - -// ServiceStats represents memory and CPU usage statistics for a service -pub struct ServiceStats { -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 []ChildStats // Stats for child processes -} - -// ChildStats represents statistics for a child process -pub struct ChildStats { -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) -} - -// 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}' -} diff --git a/src/clients/zinit/zinit.v b/src/clients/zinit/zinit.v deleted file mode 100644 index 26c7957..0000000 --- a/src/clients/zinit/zinit.v +++ /dev/null @@ -1,145 +0,0 @@ -module zinit - -// Zinit Client Module -// -// This module provides a comprehensive V (Vlang) client library for interacting -// with the Zinit service manager via JSON-RPC over Unix socket. -// -// The module includes: -// - Complete type definitions for all API structures -// - Full client implementation with all OpenRPC methods -// - Comprehensive error handling -// - Connection management -// - Well-documented API with examples -// -// Usage: -// import zinit -// -// mut client := zinit.new_default_client() -// defer { client.disconnect() } -// -// services := client.service_list()! -// for name, state in services { -// println('${name}: ${state}') -// } -// -// For detailed documentation, see README.md -// For usage examples, see example.v -// For tests, see test.v - -// Module version information -pub const ( - version = '1.0.0' - author = 'Hero Code' - license = 'MIT' -) - -// Default socket path for zinit -pub const default_socket_path = '/tmp/zinit.sock' - -// Common service states -pub const ( - state_running = 'Running' - state_success = 'Success' - state_error = 'Error' - state_stopped = 'Stopped' - state_failed = 'Failed' -) - -// Common service targets -pub const ( - target_up = 'Up' - target_down = 'Down' -) - -// Common log types -pub const ( - log_null = 'null' - log_ring = 'ring' - log_stdout = 'stdout' -) - -// Common signals -pub const ( - signal_term = 'SIGTERM' - signal_kill = 'SIGKILL' - signal_hup = 'SIGHUP' - signal_usr1 = 'SIGUSR1' - signal_usr2 = 'SIGUSR2' -) - -// JSON-RPC error codes as defined in the OpenRPC specification -pub const ( - error_service_not_found = -32000 - error_service_already_monitored = -32001 - error_service_is_up = -32002 - error_service_is_down = -32003 - error_invalid_signal = -32004 - error_config_error = -32005 - error_shutting_down = -32006 - error_service_already_exists = -32007 - error_service_file_error = -32008 -) - -// 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 - } -} - -// Helper function to check if an error is a specific zinit error code -pub fn is_zinit_error_code(err IError, code int) bool { - if zinit_err := err as ZinitError { - return zinit_err.code == code - } - return false -} - -// Helper function to check if service is not found error -pub fn is_service_not_found_error(err IError) bool { - return is_zinit_error_code(err, error_service_not_found) -} - -// Helper function to check if service is already monitored error -pub fn is_service_already_monitored_error(err IError) bool { - return is_zinit_error_code(err, error_service_already_monitored) -} - -// Helper function to check if service is down error -pub fn is_service_down_error(err IError) bool { - return is_zinit_error_code(err, error_service_is_down) -} - -// 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}%' -}