4 Commits

Author SHA1 Message Date
c5609d07e9 update dep 2025-04-30 10:26:33 -07:00
Jose Ramirez
d79325ad71 add WithHTTPClient (#13) 2025-04-30 10:21:50 -07:00
fba03ed0be update error code for 400 2025-03-13 20:39:03 -07:00
aa701237ff add new error type 2025-03-13 14:04:06 -07:00
11 changed files with 76 additions and 52 deletions

View File

@@ -2,6 +2,7 @@ package client
import (
"errors"
"net/http"
)
const apiEndpoint = "https://api.elevenlabs.io"
@@ -12,14 +13,16 @@ var (
)
type Client struct {
apiKey string
endpoint string
apiKey string
endpoint string
httpClient *http.Client
}
func New(apiKey string) Client {
return Client{
apiKey: apiKey,
endpoint: apiEndpoint,
apiKey: apiKey,
endpoint: apiEndpoint,
httpClient: &http.Client{},
}
}
@@ -27,3 +30,14 @@ func (c Client) WithEndpoint(endpoint string) Client {
c.endpoint = endpoint
return c
}
func (c Client) WithAPIKey(apiKey string) Client {
c.apiKey = apiKey
return c
}
// WithHTTPClient allows users to provide their own http.Client
func (c Client) WithHTTPClient(hc *http.Client) Client {
c.httpClient = hc
return c
}

View File

@@ -15,8 +15,6 @@ import (
func (c Client) HistoryDelete(ctx context.Context, historyItemID string) (bool, error) {
url := fmt.Sprintf(c.endpoint+"/v1/history/%s", historyItemID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
if err != nil {
return false, err
@@ -24,7 +22,7 @@ func (c Client) HistoryDelete(ctx context.Context, historyItemID string) (bool,
req.Header.Set("accept", "application/json")
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
switch res.StatusCode {
case 401:
@@ -55,7 +53,6 @@ func (c Client) HistoryDownloadZipWriter(ctx context.Context, w io.Writer, id1,
toDownload := types.HistoryPost{
HistoryItemIds: downloads,
}
client := &http.Client{}
body, _ := json.Marshal(toDownload)
bodyReader := bytes.NewReader(body)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bodyReader)
@@ -66,7 +63,7 @@ func (c Client) HistoryDownloadZipWriter(ctx context.Context, w io.Writer, id1,
req.Header.Set("accept", "archive/zip")
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
switch res.StatusCode {
case 401:
@@ -99,7 +96,6 @@ func (c Client) HistoryDownloadZip(ctx context.Context, id1, id2 string, additio
toDownload := types.HistoryPost{
HistoryItemIds: downloads,
}
client := &http.Client{}
body, _ := json.Marshal(toDownload)
bodyReader := bytes.NewReader(body)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bodyReader)
@@ -110,7 +106,7 @@ func (c Client) HistoryDownloadZip(ctx context.Context, id1, id2 string, additio
req.Header.Set("accept", "archive/zip")
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
switch res.StatusCode {
case 401:
@@ -142,7 +138,6 @@ func (c Client) HistoryDownloadZip(ctx context.Context, id1, id2 string, additio
func (c Client) HistoryDownloadAudioWriter(ctx context.Context, w io.Writer, ID string) error {
url := fmt.Sprintf(c.endpoint+"/v1/history/%s/audio", ID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
@@ -150,7 +145,7 @@ func (c Client) HistoryDownloadAudioWriter(ctx context.Context, w io.Writer, ID
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "audio/mpeg")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return err
}
@@ -179,7 +174,6 @@ func (c Client) HistoryDownloadAudioWriter(ctx context.Context, w io.Writer, ID
func (c Client) HistoryDownloadAudio(ctx context.Context, ID string) ([]byte, error) {
url := fmt.Sprintf(c.endpoint+"/v1/history/%s/audio", ID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return []byte{}, err
@@ -187,7 +181,7 @@ func (c Client) HistoryDownloadAudio(ctx context.Context, ID string) ([]byte, er
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "audio/mpeg")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return []byte{}, err
}
@@ -219,7 +213,6 @@ func (c Client) HistoryDownloadAudio(ctx context.Context, ID string) ([]byte, er
func (c Client) GetHistoryItemList(ctx context.Context) ([]types.HistoryItemList, error) {
url := c.endpoint + "/v1/history"
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return []types.HistoryItemList{}, err
@@ -227,7 +220,7 @@ func (c Client) GetHistoryItemList(ctx context.Context) ([]types.HistoryItemList
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return []types.HistoryItemList{}, err
}

View File

@@ -15,7 +15,6 @@ import (
func (c Client) DeleteVoiceSample(ctx context.Context, voiceID, sampleID string) (bool, error) {
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s", voiceID, sampleID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
if err != nil {
return false, err
@@ -24,7 +23,7 @@ func (c Client) DeleteVoiceSample(ctx context.Context, voiceID, sampleID string)
req.Header.Set("accept", "application/json")
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return false, err
}
@@ -50,7 +49,6 @@ func (c Client) DeleteVoiceSample(ctx context.Context, voiceID, sampleID string)
func (c Client) DownloadVoiceSampleWriter(ctx context.Context, w io.Writer, voiceID, sampleID string) error {
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s/audio", voiceID, sampleID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
@@ -58,7 +56,7 @@ func (c Client) DownloadVoiceSampleWriter(ctx context.Context, w io.Writer, voic
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "audio/mpeg")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return err
}
@@ -87,7 +85,6 @@ func (c Client) DownloadVoiceSampleWriter(ctx context.Context, w io.Writer, voic
func (c Client) DownloadVoiceSample(ctx context.Context, voiceID, sampleID string) ([]byte, error) {
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/samples/%s/audio", voiceID, sampleID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return []byte{}, err
@@ -95,7 +92,7 @@ func (c Client) DownloadVoiceSample(ctx context.Context, voiceID, sampleID strin
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "audio/mpeg")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return []byte{}, err
}

View File

@@ -68,8 +68,6 @@ func (c Client) SoundGeneration(ctx context.Context, text string, durationSecond
func (c Client) requestSoundGeneration(ctx context.Context, params types.SoundGeneration) (io.ReadCloser, error) {
url := c.endpoint + "/v1/sound-generation"
client := &http.Client{}
b, err := json.Marshal(params)
if err != nil {
return nil, err
@@ -84,7 +82,7 @@ func (c Client) requestSoundGeneration(ctx context.Context, params types.SoundGe
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "audio/mpeg")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}

View File

@@ -75,7 +75,6 @@ func (c *Client) ConvertSpeechToTextFromReader(ctx context.Context, reader io.Re
return nil, fmt.Errorf("failed to close multipart writer: %w", err)
}
client := &http.Client{}
url := fmt.Sprintf(c.endpoint + "/v1/speech-to-text")
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
if err != nil {
@@ -86,7 +85,7 @@ func (c *Client) ConvertSpeechToTextFromReader(ctx context.Context, reader io.Re
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("xi-api-key", c.apiKey)
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
@@ -102,9 +101,19 @@ func (c *Client) ConvertSpeechToTextFromReader(ctx context.Context, reader io.Re
return &sttResponse, nil
case 422:
ve := types.ValidationError{}
defer res.Body.Close()
jerr := json.NewDecoder(res.Body).Decode(&ve)
if jerr != nil {
err = errors.Join(err, jerr)
} else {
err = errors.Join(err, ve)
}
return nil, err
case 400:
fallthrough
default:
ve := types.ValidationError{}
ve := types.ParamError{}
defer res.Body.Close()
jerr := json.NewDecoder(res.Body).Decode(&ve)
if jerr != nil {

View File

@@ -88,7 +88,6 @@ func (c Client) requestTTS(ctx context.Context, params types.TTS, options types.
if params.Stream {
url += "/stream"
}
client := &http.Client{}
b, _ := json.Marshal(params)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(b))
if err != nil {
@@ -97,7 +96,7 @@ func (c Client) requestTTS(ctx context.Context, params types.TTS, options types.
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "audio/mpeg")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}

View File

@@ -202,6 +202,17 @@ func (ve ValidationError) Error() string {
return fmt.Sprintf("%s %s: ", ve.Type_, ve.Msg)
}
type ParamError struct {
Detail struct {
Status string `json:"status"`
Message string `json:"message"`
} `json:"detail"`
}
func (pe ParamError) Error() string {
return fmt.Sprintf("%s %s: ", pe.Detail.Status, pe.Detail.Message)
}
type VerificationAttemptResponseModel struct {
Text string `json:"text"`
DateUnix int32 `json:"date_unix"`

View File

@@ -11,7 +11,6 @@ import (
func (c Client) GetUserInfo(ctx context.Context) (types.UserResponseModel, error) {
url := c.endpoint + "/v1/user"
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return types.UserResponseModel{}, err
@@ -19,7 +18,7 @@ func (c Client) GetUserInfo(ctx context.Context) (types.UserResponseModel, error
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return types.UserResponseModel{}, err
}

View File

@@ -17,8 +17,6 @@ import (
func (c Client) CreateVoice(ctx context.Context, name, description string, labels []string, files []*os.File) error {
url := c.endpoint + "/v1/voices/add"
client := &http.Client{}
var b bytes.Buffer
w := multipart.NewWriter(&b)
for _, r := range files {
@@ -43,7 +41,7 @@ func (c Client) CreateVoice(ctx context.Context, name, description string, label
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return err
}
@@ -69,7 +67,6 @@ func (c Client) CreateVoice(ctx context.Context, name, description string, label
func (c Client) DeleteVoice(ctx context.Context, voiceID string) error {
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s", voiceID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
if err != nil {
return err
@@ -77,7 +74,7 @@ func (c Client) DeleteVoice(ctx context.Context, voiceID string) error {
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return err
}
@@ -103,7 +100,7 @@ func (c Client) DeleteVoice(ctx context.Context, voiceID string) error {
func (c Client) EditVoiceSettings(ctx context.Context, voiceID string, settings types.SynthesisOptions) error {
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/settings/edit", voiceID)
client := &http.Client{}
b, _ := json.Marshal(settings)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(b))
@@ -113,7 +110,7 @@ func (c Client) EditVoiceSettings(ctx context.Context, voiceID string, settings
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return err
}
@@ -145,7 +142,6 @@ func (c Client) EditVoiceSettings(ctx context.Context, voiceID string, settings
func (c Client) EditVoice(ctx context.Context, voiceID, name, description string, labels []string, files []*os.File) error {
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/edit", voiceID)
client := &http.Client{}
var b bytes.Buffer
w := multipart.NewWriter(&b)
@@ -171,7 +167,7 @@ func (c Client) EditVoice(ctx context.Context, voiceID, name, description string
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return err
}
@@ -197,7 +193,7 @@ func (c Client) EditVoice(ctx context.Context, voiceID, name, description string
func (c Client) GetVoiceSettings(ctx context.Context, voiceID string) (types.SynthesisOptions, error) {
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s/settings", voiceID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return types.SynthesisOptions{}, err
@@ -205,7 +201,7 @@ func (c Client) GetVoiceSettings(ctx context.Context, voiceID string) (types.Syn
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return types.SynthesisOptions{}, err
}
@@ -237,7 +233,7 @@ func (c Client) GetVoiceSettings(ctx context.Context, voiceID string) (types.Syn
func (c Client) GetVoice(ctx context.Context, voiceID string) (types.VoiceResponseModel, error) {
url := fmt.Sprintf(c.endpoint+"/v1/voices/%s", voiceID)
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return types.VoiceResponseModel{}, err
@@ -245,7 +241,7 @@ func (c Client) GetVoice(ctx context.Context, voiceID string) (types.VoiceRespon
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
switch res.StatusCode {
case 401:
return types.VoiceResponseModel{}, ErrUnauthorized
@@ -279,7 +275,7 @@ func (c Client) GetVoice(ctx context.Context, voiceID string) (types.VoiceRespon
func (c Client) GetVoices(ctx context.Context) ([]types.VoiceResponseModel, error) {
url := c.endpoint + "/v1/voices"
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return []types.VoiceResponseModel{}, err
@@ -287,7 +283,7 @@ func (c Client) GetVoices(ctx context.Context) ([]types.VoiceResponseModel, erro
req.Header.Set("xi-api-key", c.apiKey)
req.Header.Set("User-Agent", "github.com/taigrr/elevenlabs")
req.Header.Set("accept", "application/json")
res, err := client.Do(req)
res, err := c.httpClient.Do(req)
switch res.StatusCode {
case 401:
return []types.VoiceResponseModel{}, ErrUnauthorized

8
go.mod
View File

@@ -11,8 +11,8 @@ require (
github.com/hajimehoshi/oto v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
golang.org/x/exp/shiny v0.0.0-20250228200357-dead58393ab7 // indirect
golang.org/x/image v0.24.0 // indirect
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/image v0.26.0 // indirect
golang.org/x/mobile v0.0.0-20250408133729-978277e7eaf7 // indirect
golang.org/x/sys v0.32.0 // indirect
)

8
go.sum
View File

@@ -36,18 +36,24 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMD
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp/shiny v0.0.0-20250228200357-dead58393ab7 h1:VxTRg3kpOpYQ+S2PlDH9x2j/ZOQMxVsPgdYYRvkErNY=
golang.org/x/exp/shiny v0.0.0-20250228200357-dead58393ab7/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 h1:tMSqXTK+AQdW3LpCbfatHSRPHeW6+2WuxaVQuHftn80=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk=
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20230906132913-2077a3224571 h1:QDvQ2KLFHHQWRID6IkZOBf6uLIh9tZ0G+mw61pFQxuo=
golang.org/x/mobile v0.0.0-20230906132913-2077a3224571/go.mod h1:wEyOn6VvNW7tcf+bW/wBz1sehi2s2BZ4TimyR7qZen4=
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3 h1:0V/7Y1FEaFdAzb9DkVDh4QFp4vL4yYCiJ5cjk80lZyA=
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3/go.mod h1:j5VYNgQ6lZYZlzHFjdgS2UeqRSZunDk+/zXVTAIA3z4=
golang.org/x/mobile v0.0.0-20250408133729-978277e7eaf7 h1:8MGTx39304caZ/OMsjPfuxUoDGI2tRas92F5x97tIYc=
golang.org/x/mobile v0.0.0-20250408133729-978277e7eaf7/go.mod h1:ftACcHgQ7vaOnQbHOHvXt9Y6bEPHrs5Ovk67ClwrPJA=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -76,6 +82,8 @@ golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=