Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Boernsman authored and Boernsman committed Jul 10, 2024
0 parents commit 91ce20c
Show file tree
Hide file tree
Showing 19 changed files with 1,030 additions and 0 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Build and Upload

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.22

- name: Build
run: ./build.sh

- name: Archive production artifacts
uses: actions/upload-artifact@v4
with:
name: bon-voyage-agent
path: build
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Bon Voyage Agent

## Usage


39 changes: 39 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash

# Exit immediately if a command exits with a non-zero status
set -e

# Variables
BASEDIR=$(dirname $(realpath "$0"))
echo "Base Dir: $BASEDIR"
TARGET="bon-voyage-agent"
SRC_DIR="$BASEDIR/src"
PLUGIN_SRC_DIR="$BASEDIR/src/plugins"
BUILD_DIR="$BASEDIR/build"
PLUGIN_BUILD_DIR="$BUILD_DIR/plugins"

# Determine the operating system
OS="$(uname -s)"
case "$OS" in
Linux*) GOOS="linux";;
Darwin*) GOOS="darwin";;
*) echo "Unsupported OS: $OS"; exit 1;;
esac

# Clean previous builds
echo "Cleaning previous builds..."
rm -f $BUILD_DIR/$TARGET

# Build the agent
echo "Building agent ..."
cd $SRC_DIR

GIT_HASH=$(git rev-parse HEAD)
go build -o $BUILD_DIR/$TARGET -ldflags="-X main.Commit=$GIT_HASH"
cd $BASEDIR

# Build the plugins
cd $PLUGIN_SRC_DIR
./build_plugins.sh $PLUGIN_BUILD_DIR
cd $BASEDIR
echo "Build finished. See README.md for usage."
8 changes: 8 additions & 0 deletions example/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Bon Voyage Agent
agent:
name: "Voyager"
id: "60F8A8EF-0D07-4353-B238-A4D568100E3B"
server:
host: "192.168.1.148"
port: 6666
key: ""
69 changes: 69 additions & 0 deletions src/connection/agent_module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package connection

import (
"bon-voyage-agent/models"
"encoding/json"
"fmt"
)

const pluginName = "serial"

// Implements interface Plugin
type PluginAgent struct {
Name string
Information models.AgentInformation
Capabilities []string
Plugins []string
}

func (p PluginAgent) Init(params any) string {

switch v := params.(type) {
case models.AgentInformation:
PluginInstance.Information = v
default:
fmt.Printf("Invalid plugin params")
}
p.Name = pluginName
return p.Name
}

func AgentCall(request models.RPCRequest, response *models.RPCResponse) {

switch request.Method {
case "agent_get_information":
i, err := json.Marshal(PluginInstance.Information)
if err != nil {
response.Error = err.Error()
return
}
response.Result = string(i)
case "agent_set_name":
var name models.SetNameMethod
err := json.Unmarshal(request.Params, &name)
if err != nil {
response.Error = err.Error()
}
PluginInstance.Information.Name = name.Name

case "agent_get_capabilities":
i, err := json.Marshal(PluginInstance.Capabilities)
if err != nil {
response.Error = err.Error()
return
}
response.Result = string(i)
case "agent_get_plugins":
i, err := json.Marshal(PluginInstance.Plugins)
if err != nil {
response.Error = err.Error()
return
}
response.Result = string(i)
default:
response.Error = "Unknown method"
}
}

// Exported symbol
var PluginInstance PluginAgent
65 changes: 65 additions & 0 deletions src/connection/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package connection

import (
"bon-voyage-agent/models"
"encoding/json"
"fmt"
"strings"
)

type Handler func(models.RPCRequest, *models.RPCResponse)

type Route struct {
handler Handler
}

func NewRoute() *Route {
return &Route{}
}

type Router struct {
NotFoundHandler Handler
namedRoutes map[string]*Route
}

func NewRouter() *Router {
r := Router{namedRoutes: make(map[string]*Route)}
r.HandleFunc("agent", AgentCall)
return &r
}

func (r *Router) HandleFunc(name string, f func(request models.RPCRequest, response *models.RPCResponse)) *Route {
route := &Route{handler: f}
r.namedRoutes[name] = route
return route
}

func (r *Router) ParseMessage(data []byte) ([]byte, error) {

var request models.RPCRequest
err := json.Unmarshal(data, &request)
if err != nil {
return nil, fmt.Errorf("unmarshal error: %v", err)
}
if request.Jsonrpc != "2.0" {
return nil, fmt.Errorf("jsonrpc field not '2.0'")
}

response := models.RPCResponse{
Jsonrpc: "2.0",
ID: request.ID,
}
if len(strings.Split(request.Method, "_")) == 0 {
return nil, fmt.Errorf("method malformed")
}
routeName := strings.Split(request.Method, "_")[0]

route := r.namedRoutes[routeName]
route.handler(request, &response)

resBytes, err := json.Marshal(response)
if err != nil {
return nil, fmt.Errorf("marshal error: %v", err)
}
return resBytes, nil
}
110 changes: 110 additions & 0 deletions src/connection/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package connection

import (
"fmt"
"net/http"
"net/url"

"github.com/gorilla/websocket"
)

type Connection struct {
Host string
Port string
Name string
Uuid string
Key string
Token string
}

func (c Connection) serverUrl(path string) *url.URL {
return &url.URL{
Scheme: "ws",
Host: c.Host + ":" + c.Port,
Path: path,
}
}

func (c Connection) header() http.Header {
h := http.Header{}
h.Add("X-Device-UUID", c.Uuid)
h.Add("X-Device-Name", c.Name)
if c.Key != "" {
h.Add("X-Device-Api-Key", c.Key)
}
return h
}

func (c Connection) ConnectDataSocket() (*websocket.Conn, error) {

d := websocket.DefaultDialer
d.Subprotocols = []string{"serial-tunnel-v1"}

socket, _, err := d.Dial(c.serverUrl("/api/device/data").String(), c.header())
if err != nil {
return nil, fmt.Errorf("dial: %v", err)
}

return socket, nil

// done := make(chan struct{})

// go func() {
// defer close(done)
// for {
// _, message, err := socket.ReadMessage()
// if err != nil {
// if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
// return
// }
// fmt.Println("read:", err)
// return
// }
// fmt.Printf("Received: %s\n", message)
// }
// }()

// go func() {
// scanner := bufio.NewScanner(os.Stdin)
// for scanner.Scan() {
// text := scanner.Text()
// err := socket.WriteMessage(websocket.TextMessage, []byte(text))
// if err != nil {
// fmt.Println("write:", err)
// return
// }
// }
// if err := scanner.Err(); err != nil {
// fmt.Println("Error reading from stdin:", err)
// }
// }()

// select {
// case <-done:
// fmt.Println("WebSocket connection closed")
// case <-interrupt:
// fmt.Println("\nInterrupt signal received, closing connection...")
// err := socket.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
// if err != nil {
// fmt.Println("write close:", err)
// return err
// }
// select {
// case <-done:
// case <-time.After(time.Second):
// }
// }
}

func (c Connection) ConnectConfigSocket() (*websocket.Conn, error) {

d := websocket.DefaultDialer
d.Subprotocols = []string{"config-tunnel-v1"}

socket, _, err := d.Dial(c.serverUrl("/api/device/config").String(), c.header())
if err != nil {
return nil, fmt.Errorf("dial: %v", err)
}

return socket, nil
}
33 changes: 33 additions & 0 deletions src/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module bon-voyage-agent

go 1.22.4

require (
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/spf13/viper v1.19.0
go.bug.st/serial v1.6.2
)

require (
github.com/creack/goselect v0.1.2 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 91ce20c

Please sign in to comment.