feat: initial commit of vps-commander release v1.0.0

This commit is contained in:
kory
2026-09-28 07:48:01 +08:00
commit 467e28a6a1
48 changed files with 3851 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
package main
import (
"bufio"
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"github.com/wjyhk/vps-commander/internal/mcp"
)
type HubRemoteBackend struct {
BaseURL string
APIKey string
HTTPClient *http.Client
}
func (h *HubRemoteBackend) post(path string, reqBody any, out any) error {
b, err := json.Marshal(reqBody)
if err != nil {
return err
}
url := strings.TrimRight(h.BaseURL, "/") + path
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+h.APIKey)
resp, err := h.HTTPClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
bs, _ := io.ReadAll(resp.Body)
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(bs))
}
if out != nil {
return json.NewDecoder(resp.Body).Decode(out)
}
return nil
}
func (h *HubRemoteBackend) ListDevices(ctx context.Context) (any, error) {
url := strings.TrimRight(h.BaseURL, "/") + "/api/v1/devices"
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+h.APIKey)
resp, err := h.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
bs, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(bs))
}
var res any
err = json.NewDecoder(resp.Body).Decode(&res)
return res, err
}
func (h *HubRemoteBackend) ExecCommand(ctx context.Context, clientIP, device, command, workdir string, timeout int) (any, error) {
payload := map[string]any{
"device": device,
"command": command,
"workdir": workdir,
"timeout": timeout,
}
var res any
err := h.post("/api/v1/exec", payload, &res)
return res, err
}
func (h *HubRemoteBackend) ReadFile(ctx context.Context, clientIP, device, path string, offset, limit int64) (any, error) {
payload := map[string]any{
"device": device,
"path": path,
"offset": offset,
"limit": limit,
}
var res any
err := h.post("/api/v1/file/read", payload, &res)
return res, err
}
func (h *HubRemoteBackend) WriteFile(ctx context.Context, clientIP, device, path, content string) (any, error) {
payload := map[string]any{
"device": device,
"path": path,
"content": content,
}
var res any
err := h.post("/api/v1/file/write", payload, &res)
return res, err
}
func main() {
baseURL := flag.String("hub", "http://127.0.0.1:9521", "VPS-Commander Hub base URL")
apiKey := flag.String("key", os.Getenv("VPS_COMMANDER_API_KEY"), "VPS-Commander Hub API Key")
flag.Parse()
if *apiKey == "" {
fmt.Fprintln(os.Stderr, "Error: -key or VPS_COMMANDER_API_KEY environment variable is required")
os.Exit(1)
}
backend := &HubRemoteBackend{
BaseURL: *baseURL,
APIKey: *apiKey,
HTTPClient: &http.Client{Timeout: 310 * time.Second},
}
srv := mcp.NewServer(backend, nil)
scanner := bufio.NewScanner(os.Stdin)
// stdio line-by-line JSON-RPC
for scanner.Scan() {
line := scanner.Bytes()
if len(strings.TrimSpace(string(line))) == 0 {
continue
}
var req mcp.JSONRPCRequest
if err := json.Unmarshal(line, &req); err != nil {
continue
}
resp := srv.HandleDirectRequest(context.Background(), req)
respBytes, _ := json.Marshal(resp)
os.Stdout.Write(respBytes)
os.Stdout.WriteString("\n")
}
}