Skip to content

Commit b5f7a9a

Browse files
authored
Merge pull request #2 from mutablelogic/v1
Added a cli command and OpenAI client
2 parents fcc4a92 + 8b0c1da commit b5f7a9a

File tree

18 files changed

+515
-34
lines changed

18 files changed

+515
-34
lines changed

Makefile

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# Paths to tools needed in dependencies
22
GO := $(shell which go)
3-
NPM := $(shell which npm)
43

54
# Build flags
65
BUILD_MODULE := $(shell go list -m)
@@ -13,19 +12,13 @@ BUILD_FLAGS = -ldflags "-s -w $(BUILD_LD_FLAGS)"
1312

1413
# Paths to locations, etc
1514
BUILD_DIR := "build"
16-
PLUGIN_DIR := $(filter-out $(wildcard plugin/*.go), $(wildcard plugin/*))
17-
NPM_DIR := $(wildcard npm/*)
1815
CMD_DIR := $(wildcard cmd/*)
1916

2017
# Targets
21-
all: clean cmd npm plugins
18+
all: clean cmd
2219

2320
cmd: $(CMD_DIR)
2421

25-
plugins: $(PLUGIN_DIR)
26-
27-
npm: $(NPM_DIR)
28-
2922
test:
3023
@${GO} mod tidy
3124
@${GO} test -v ./pkg/...
@@ -34,32 +27,17 @@ $(CMD_DIR): dependencies mkdir
3427
@echo Build cmd $(notdir $@)
3528
@${GO} build ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@) ./$@
3629

37-
$(PLUGIN_DIR): dependencies mkdir
38-
@echo Build plugin $(notdir $@)
39-
@${GO} build -buildmode=plugin ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@).plugin ./$@
40-
41-
$(NPM_DIR): dependencies-npm
42-
@echo Build npm $(notdir $@)
43-
@cd $@ && npm install && npm run build
44-
4530
FORCE:
4631

4732
dependencies:
4833
@test -f "${GO}" && test -x "${GO}" || (echo "Missing go binary" && exit 1)
4934

50-
dependencies-npm:
51-
@test -f "${NPM}" && test -x "${NPM}" || (echo "Missing npm binary" && exit 1)
52-
5335
mkdir:
5436
@echo Mkdir ${BUILD_DIR}
5537
@install -d ${BUILD_DIR}
5638

5739
clean:
5840
@echo Clean
5941
@rm -fr $(BUILD_DIR)
60-
@find ${NPM_DIR} -name dist -type d -prune -exec rm -fr {} \;
6142
@${GO} mod tidy
6243
@${GO} clean
63-
64-
distclean: clean
65-
@find ${NPM_DIR} -name node_modules -type d -prune -exec rm -fr {} \;

build/cli

5.16 MB
Binary file not shown.

cmd/cli/command.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"golang.org/x/exp/maps"
7+
)
8+
9+
///////////////////////////////////////////////////////////////////////////////
10+
// TYPES
11+
12+
type Client struct {
13+
ns string
14+
cmd []Command
15+
}
16+
17+
type Command struct {
18+
Name string
19+
Description string
20+
MinArgs int
21+
MaxArgs int
22+
Fn CommandFn
23+
}
24+
25+
type CommandFn func() error
26+
27+
///////////////////////////////////////////////////////////////////////////////
28+
// RUN COMMAND
29+
30+
func Run(clients []Client, flags *Flags) error {
31+
var ns = map[string][]Command{}
32+
for _, client := range clients {
33+
ns[client.ns] = client.cmd
34+
}
35+
36+
if flags.NArg() == 0 {
37+
return fmt.Errorf("no command specified, available commands are: %q", maps.Keys(ns))
38+
}
39+
cmd, exists := ns[flags.Arg(0)]
40+
if !exists {
41+
return fmt.Errorf("unknown command: %q, available commands are: %q", flags.Arg(0), maps.Keys(ns))
42+
}
43+
44+
var fn CommandFn
45+
for _, cmd := range cmd {
46+
// Match on minimum number of arguments
47+
if flags.NArg() < cmd.MinArgs {
48+
continue
49+
}
50+
// Match on maximum number of arguments
51+
if cmd.MaxArgs >= cmd.MinArgs && flags.NArg() > cmd.MaxArgs {
52+
continue
53+
}
54+
// Match on name
55+
if cmd.Name != "" && cmd.Name != flags.Arg(1) {
56+
continue
57+
}
58+
59+
// Here we have matched, so break loop
60+
fn = cmd.Fn
61+
break
62+
}
63+
64+
// Run function
65+
if fn == nil {
66+
return fmt.Errorf("no command matched for: %q", flags.Args())
67+
} else {
68+
return fn()
69+
}
70+
}

cmd/cli/elevenlabs.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
8+
// Packages
9+
"github.com/mutablelogic/go-client/pkg/client"
10+
"github.com/mutablelogic/go-client/pkg/elevenlabs"
11+
)
12+
13+
func ElevenlabsFlags(flags *Flags) {
14+
flags.String("elevenlabs-api-key", "${ELEVENLABS_API_KEY}", "ElevenLabs API key")
15+
flags.String("elevenlabs-voice", "", "Voice")
16+
}
17+
18+
func ElevenlabsRegister(cmd []Client, opts []client.ClientOpt, flags *Flags) ([]Client, error) {
19+
// Get API key
20+
key, err := flags.GetString("elevenlabs-api-key")
21+
if err != nil {
22+
return nil, err
23+
}
24+
25+
// Create client
26+
elevenlabs, err := elevenlabs.New(key, opts...)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
// Register commands
32+
cmd = append(cmd, Client{
33+
ns: "elevenlabs",
34+
cmd: []Command{
35+
{Name: "voices", Description: "Return registered voices", MinArgs: 2, MaxArgs: 2, Fn: ElevenlabsVoices(elevenlabs, flags)},
36+
{Name: "voice", Description: "Return a voice", MinArgs: 3, MaxArgs: 3, Fn: ElevenlabsVoice(elevenlabs, flags)},
37+
{Name: "tts", Description: "Text-to-speech", MinArgs: 3, MaxArgs: 3, Fn: ElevenlabsTextToSpeech(elevenlabs, flags)},
38+
},
39+
})
40+
41+
// Return success
42+
return cmd, nil
43+
}
44+
45+
func ElevenlabsVoices(client *elevenlabs.Client, flags *Flags) CommandFn {
46+
return func() error {
47+
if voices, err := client.Voices(); err != nil {
48+
return err
49+
} else if data, err := json.MarshalIndent(voices, "", " "); err != nil {
50+
return err
51+
} else {
52+
fmt.Printf("Voices: %s\n", data)
53+
}
54+
return nil
55+
}
56+
}
57+
58+
func ElevenlabsVoice(client *elevenlabs.Client, flags *Flags) CommandFn {
59+
return func() error {
60+
if voice, err := client.Voice(flags.Arg(2)); err != nil {
61+
return err
62+
} else if data, err := json.MarshalIndent(voice, "", " "); err != nil {
63+
return err
64+
} else {
65+
fmt.Printf("%s\n", data)
66+
}
67+
return nil
68+
}
69+
}
70+
71+
func ElevenlabsTextToSpeech(client *elevenlabs.Client, flags *Flags) CommandFn {
72+
return func() error {
73+
// Determine the voice to use
74+
voice, err := flags.GetString("elevenlabs-voice")
75+
if err != nil {
76+
return err
77+
} else if voice == "" {
78+
return fmt.Errorf("missing argument: -elevenlabs-voice")
79+
}
80+
81+
data, err := client.TextToSpeech(flags.Arg(2), voice)
82+
if err != nil {
83+
return err
84+
} else if _, err := os.Stdout.Write(data); err != nil {
85+
return err
86+
}
87+
return nil
88+
}
89+
}

cmd/cli/flags.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"os"
6+
"time"
7+
8+
"github.com/djthorpe/go-errors"
9+
)
10+
11+
///////////////////////////////////////////////////////////////////////////////
12+
// TYPES
13+
14+
type Flags struct {
15+
*flag.FlagSet
16+
}
17+
18+
type FlagsRegister func(*Flags)
19+
20+
///////////////////////////////////////////////////////////////////////////////
21+
// LIFECYCLE
22+
23+
func NewFlags(name string, args []string, register ...FlagsRegister) (*Flags, error) {
24+
flags := new(Flags)
25+
flags.FlagSet = flag.NewFlagSet(name, flag.ContinueOnError)
26+
27+
// Register flags
28+
flags.Bool("debug", false, "Enable debug logging")
29+
flags.Duration("timeout", 0, "Timeout")
30+
for _, fn := range register {
31+
fn(flags)
32+
}
33+
34+
// Parse command line
35+
if err := flags.Parse(args); err != nil {
36+
return nil, err
37+
}
38+
39+
// Return success
40+
return flags, nil
41+
}
42+
43+
///////////////////////////////////////////////////////////////////////////////
44+
// PUBLIC METHODS
45+
46+
func (flags *Flags) IsDebug() bool {
47+
return flags.Lookup("debug").Value.(flag.Getter).Get().(bool)
48+
}
49+
50+
func (flags *Flags) Timeout() time.Duration {
51+
return flags.Lookup("timeout").Value.(flag.Getter).Get().(time.Duration)
52+
}
53+
54+
func (flags *Flags) GetString(key string) (string, error) {
55+
if flag := flags.Lookup(key); flag == nil {
56+
return "", errors.ErrNotFound.With(key)
57+
} else {
58+
return os.ExpandEnv(flag.Value.String()), nil
59+
}
60+
}

cmd/cli/ipify.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
// Packages
7+
"github.com/mutablelogic/go-client/pkg/client"
8+
"github.com/mutablelogic/go-client/pkg/ipify"
9+
)
10+
11+
func IpifyRegister(cmd []Client, opts []client.ClientOpt, flags *Flags) ([]Client, error) {
12+
// Create ipify client
13+
ipify, err := ipify.New(opts...)
14+
if err != nil {
15+
return nil, err
16+
}
17+
18+
// Register commands for router
19+
cmd = append(cmd, Client{
20+
ns: "ipify",
21+
cmd: []Command{
22+
{MinArgs: 1, MaxArgs: 1, Description: "Get external IP address", Fn: IpifyGet(ipify, flags)},
23+
},
24+
})
25+
26+
// Return success
27+
return cmd, nil
28+
}
29+
30+
func IpifyGet(ipify *ipify.Client, flags *Flags) CommandFn {
31+
return func() error {
32+
if addr, err := ipify.Get(); err != nil {
33+
return err
34+
} else {
35+
fmt.Printf("IP Address: %s\n", addr)
36+
}
37+
return nil
38+
}
39+
}

cmd/cli/main.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"os"
7+
"path"
8+
9+
"github.com/mutablelogic/go-client/pkg/client"
10+
)
11+
12+
func main() {
13+
name := path.Base(os.Args[0])
14+
flags, err := NewFlags(name, os.Args[1:], ElevenlabsFlags)
15+
if err != nil {
16+
if err != flag.ErrHelp {
17+
fmt.Fprintln(os.Stderr, err)
18+
os.Exit(1)
19+
} else {
20+
os.Exit(0)
21+
}
22+
}
23+
24+
// Add client options
25+
opts := []client.ClientOpt{}
26+
if flags.IsDebug() {
27+
opts = append(opts, client.OptTrace(os.Stderr, true))
28+
}
29+
if timeout := flags.Timeout(); timeout > 0 {
30+
opts = append(opts, client.OptTimeout(timeout))
31+
}
32+
33+
// Register commands
34+
var cmd []Client
35+
cmd, err = IpifyRegister(cmd, opts, flags)
36+
if err != nil {
37+
fmt.Fprintln(os.Stderr, err)
38+
os.Exit(1)
39+
}
40+
cmd, err = ElevenlabsRegister(cmd, opts, flags)
41+
if err != nil {
42+
fmt.Fprintln(os.Stderr, err)
43+
os.Exit(1)
44+
}
45+
46+
// Run command
47+
if err := Run(cmd, flags); err != nil {
48+
fmt.Fprintln(os.Stderr, err)
49+
os.Exit(1)
50+
}
51+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.20
55
require (
66
github.com/djthorpe/go-errors v1.0.3
77
github.com/stretchr/testify v1.8.4
8+
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
89
)
910

1011
require (

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
66
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
77
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
88
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
9+
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
10+
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
911
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1012
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1113
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

0 commit comments

Comments
 (0)