Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions .compat/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package api

import (
"context"
"net/http"

"github.com/gin-gonic/gin"
)

type RouteDescription struct {
Method string `json:"method,omitempty"`
Path string `json:"path,omitempty"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
Tags []string `json:"tags,omitempty"`
RequestBody map[string]any `json:"requestBody,omitempty"`
Responses map[string]any `json:"responses,omitempty"`
}

type RouteGroup interface {
Name() string
BasePath() string
RegisterRoutes(*gin.RouterGroup)
}

type DescribableGroup interface {
RouteGroup
Describe() []RouteDescription
}

type response struct {
Success bool `json:"success"`
Data any `json:"data,omitempty"`
Error string `json:"error,omitempty"`
Code string `json:"code,omitempty"`
Details any `json:"details,omitempty"`
}

func OK(data any) any {
return response{Success: true, Data: data}
}

func Fail(code, message string) any {
return response{Success: false, Code: code, Error: message}
}

func FailWithDetails(code, message string, details any) any {
return response{Success: false, Code: code, Error: message, Details: details}
}

type Option func(*Engine)

func WithAddr(addr string) Option {
return func(e *Engine) { e.addr = addr }
}

func WithWSPath(path string) Option {
return func(e *Engine) { e.wsPath = path }
}

func WithWSHandler(handler http.HandlerFunc) Option {
return func(e *Engine) { e.wsHandler = handler }
}

type Engine struct {
router *gin.Engine
addr string
wsPath string
wsHandler http.HandlerFunc
}

func New(opts ...Option) (*Engine, error) {
engine := &Engine{router: gin.New()}
for _, opt := range opts {
opt(engine)
}
if engine.wsPath != "" && engine.wsHandler != nil {
engine.router.GET(engine.wsPath, gin.WrapF(engine.wsHandler))
}
return engine, nil
}

func (e *Engine) Register(group RouteGroup) {
if e == nil || group == nil {
return
}
group.RegisterRoutes(e.router.Group(group.BasePath()))
}

func (e *Engine) Serve(ctx context.Context) error {
if e == nil {
return nil
}
server := &http.Server{Addr: e.addr, Handler: e.router}
errCh := make(chan error, 1)
go func() { errCh <- server.ListenAndServe() }()
select {
case <-ctx.Done():
if err := server.Shutdown(context.Background()); err != nil {
return err
}
return ctx.Err()
case err := <-errCh:
return err
}
}

func (e *Engine) Handler() http.Handler {
if e == nil {
return http.NewServeMux()
}
return e.router
}

type ToolDescriptor struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Group string `json:"group,omitempty"`
}

type ToolBridge struct {
basePath string
tools []toolRoute
}

type toolRoute struct {
descriptor ToolDescriptor
handler gin.HandlerFunc
}

func NewToolBridge(basePath string) *ToolBridge {
return &ToolBridge{basePath: basePath}
}

func (b *ToolBridge) Name() string { return "mcp" }

func (b *ToolBridge) BasePath() string {
if b == nil {
return ""
}
return b.basePath
}

func (b *ToolBridge) Add(descriptor ToolDescriptor, handler gin.HandlerFunc) {
if b == nil {
return
}
b.tools = append(b.tools, toolRoute{descriptor: descriptor, handler: handler})
}

func (b *ToolBridge) RegisterRoutes(group *gin.RouterGroup) {
if b == nil {
return
}
for _, tool := range b.tools {
group.POST("/"+tool.descriptor.Name, tool.handler)
}
}

func (b *ToolBridge) Tools() []ToolDescriptor {
if b == nil {
return nil
}
out := make([]ToolDescriptor, 0, len(b.tools))
for _, tool := range b.tools {
out = append(out, tool.descriptor)
}
return out
}
5 changes: 5 additions & 0 deletions .compat/api/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module dappco.re/go/api

go 1.26.0

require github.com/gin-gonic/gin v1.12.0
77 changes: 77 additions & 0 deletions .compat/api/pkg/provider/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package provider

import "github.com/gin-gonic/gin"

type Provider interface {
Name() string
BasePath() string
RegisterRoutes(*gin.RouterGroup)
}

type Streamable interface {
Provider
Channels() []string
}

type Describable interface {
Provider
}

type Renderable interface {
Provider
Element() ElementSpec
}

type ElementSpec struct {
Tag string `json:"tag"`
Source string `json:"source"`
}

type Registry struct {
providers map[string]Provider
order []string
}

func NewRegistry() *Registry {
return &Registry{providers: make(map[string]Provider)}
}

func (r *Registry) Add(provider Provider) {
if r == nil || provider == nil {
return
}
name := provider.Name()
if _, exists := r.providers[name]; !exists {
r.order = append(r.order, name)
}
r.providers[name] = provider
}

func (r *Registry) Get(name string) Provider {
if r == nil {
return nil
}
return r.providers[name]
}

func (r *Registry) Info() []map[string]any {
if r == nil {
return nil
}
info := make([]map[string]any, 0, len(r.order))
for _, name := range r.order {
p := r.providers[name]
entry := map[string]any{
"name": p.Name(),
"base_path": p.BasePath(),
}
if streamable, ok := p.(Streamable); ok {
entry["channels"] = streamable.Channels()
}
if renderable, ok := p.(Renderable); ok {
entry["element"] = renderable.Element()
}
info = append(info, entry)
}
return info
}
3 changes: 3 additions & 0 deletions .compat/cli/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module dappco.re/go/cli

go 1.26.0
109 changes: 109 additions & 0 deletions .compat/cli/pkg/cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package cli

import (
"context"
"errors"
"fmt"
"io"
"os"
)

type Style struct{}

func (Style) Render(text string) string { return text }

var (
TitleStyle Style
ValueStyle Style
SuccessStyle Style
ErrorStyle Style
DimStyle Style
RepoStyle Style
)

var (
stdout io.Writer = os.Stdout
stderr io.Writer = os.Stderr
)

func SetStdout(w io.Writer) {
if w == nil {
stdout = os.Stdout
return
}
stdout = w
}

func SetStderr(w io.Writer) {
if w == nil {
stderr = os.Stderr
return
}
stderr = w
}

func Print(format string, args ...any) {
_, _ = fmt.Fprintf(stdout, format, args...)
}

func Text(text string) {
_, _ = fmt.Fprintln(stdout, text)
}

func Blank() {
_, _ = fmt.Fprintln(stdout)
}

func Err(format string, args ...any) error {
return fmt.Errorf(format, args...)
}

func Wrap(err error, message string) error {
if err == nil {
return nil
}
if message == "" {
return err
}
return fmt.Errorf("%s: %w", message, err)
}

func WrapVerb(err error, verb, subject string) error {
if err == nil {
return nil
}
return fmt.Errorf("failed to %s %s: %w", verb, subject, err)
}

type ExitError struct {
Code int
Err error
}

func (e *ExitError) Error() string {
if e == nil {
return ""
}
if e.Err != nil {
return e.Err.Error()
}
return fmt.Sprintf("exit %d", e.Code)
}

func (e *ExitError) Unwrap() error {
if e == nil {
return nil
}
return e.Err
}

func Exit(code int, err error) error {
if err == nil {
err = errors.New("exit")
}
return &ExitError{Code: code, Err: err}
}

func Context() context.Context {
return context.Background()
}
3 changes: 3 additions & 0 deletions .compat/i18n/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module dappco.re/go/i18n

go 1.26.0
Loading