diff --git a/examples/schemas/openrpc/openrpc.json b/examples/schemas/openrpc/openrpc.json index 65cc7af7..6e3e3eee 100644 --- a/examples/schemas/openrpc/openrpc.json +++ b/examples/schemas/openrpc/openrpc.json @@ -16,7 +16,7 @@ ], "methods": [ { - "name": "rpc_discover", + "name": "rpc.discover", "description": "Returns the OpenRPC specification for the API", "params": [], "result": { diff --git a/examples/schemas/openrpc/zinit_rpc_example.vsh b/examples/schemas/openrpc/zinit_rpc_example.vsh index aa8ae2f1..5cd14c25 100755 --- a/examples/schemas/openrpc/zinit_rpc_example.vsh +++ b/examples/schemas/openrpc/zinit_rpc_example.vsh @@ -1,6 +1,7 @@ #!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run import freeflowuniverse.herolib.schemas.jsonrpc +import json // Define the service status response structure based on the OpenRPC schema struct ServiceStatus { @@ -14,19 +15,34 @@ struct ServiceStatus { // Create a client using the Unix socket transport mut cl := jsonrpc.new_unix_socket_client("/tmp/zinit.sock") -// Example 1: List all services +// 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('Sending service_list request...') +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 2: Get status of a specific service +// 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 @@ -34,11 +50,11 @@ if service_list.len > 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', {"name": service_name}) + status_request := jsonrpc.new_request_generic('service_status', service_name) // Send the request and receive a ServiceStatus object println('\nSending service_status request for service: $service_name') - service_status := cl.send[ map[string]string, ServiceStatus](status_request)! + service_status := cl.send[string, ServiceStatus](status_request)! // Display the service status details println('Service Status:') @@ -53,22 +69,3 @@ if service_list.len > 0 { } else { println('\nNo services found to query status') } - -// // Example 3: Alternative approach using a string array for the parameter -// // Some JSON-RPC servers expect parameters as an array, even for a single parameter -// println('\nAlternative approach using array parameter:') -// if service_list.len > 0 { -// service_name := service_list.keys()[0] - -// // Create a request with the service name in an array -// status_request_alt := jsonrpc.new_request_generic('service_status', service_name) - -// // Send the request and receive a ServiceStatus object -// println('Sending service_status request for service: $service_name (array parameter)') -// service_status_alt := cl.send[[]string, ServiceStatus](status_request_alt)! - -// // Display the service status details -// println('Service Status (alternative):') -// println('- Name: ${service_status_alt.name}') -// println('- State: ${service_status_alt.state}') -// } diff --git a/lib/osal/zinit/openrpc.json b/lib/osal/zinit/openrpc.json new file mode 100644 index 00000000..6e3e3eee --- /dev/null +++ b/lib/osal/zinit/openrpc.json @@ -0,0 +1,873 @@ +{ + "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