From 992c7296b0bb7481bb98bc5bbd6bdd7c564c1a2b Mon Sep 17 00:00:00 2001 From: jamiesteven Date: Mon, 12 Sep 2022 17:21:04 -0700 Subject: [PATCH] Initial commit. --- .gitignore | 21 +++++ LICENSE.txt | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 62 +++++++++++++++ TODO | 20 +++++ cmd/root.go | 37 +++++++++ cmd/run.go | 95 +++++++++++++++++++++++ cmd/versions.go | 49 ++++++++++++ go.mod | 22 ++++++ go.sum | 48 ++++++++++++ lib/token.go | 22 ++++++ replicate.go | 7 ++ 11 files changed, 584 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 TODO create mode 100644 cmd/root.go create mode 100644 cmd/run.go create mode 100644 cmd/versions.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 lib/token.go create mode 100644 replicate.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9763ae3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +vendor/ + +# Go workspace file +go.work + +# Binary +replicate \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c45495 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# replicate-cli +Command line interface for the Replicate API, powered by Go. + +### Version 0.1.0 - Experimental Support + +Run models and retrieves model version IDs. See TODO for proposed feature list. Currently only supports models that accept a single "image" input. **Note: Stable Diffusion and other text input models are not yet supported, but will be soon. Executable name may change to replicate, from replicate-cli.** + +## Requirements +* Go 1.19 +* Replicate API Token, [get yours here](https://replicate.com/account). + +## Install, Build & Run + +### Install +``` +go install github.com/jamiesteven/replicate-cli@latest +``` + +### Run +``` +replicate-cli +``` + +### Uninstall +``` +rm $GOPATH/bin/replicate-cli +``` + +## How to use + +### Authentication +The Replicate API requires an API token. [Get one here.](https://replicate.com/account). + +Set a ```REPLICATE_TOKEN``` environment variable, or use ```--token ```. +``` +replicate-cli --token +``` + +### Get model versions +``` +replicate-cli versions jingyunliang/swinir +``` + +### Run a model + +**Run using model name.** replicate-cli will use the latest version. +``` +replicate-cli run jingyunliang/swinir [input] +``` +Input currently supports a fully qualified domain-name. + +**Run using version ID.** +``` +replicate-cli run jingyunliang/swinir +``` + +## Development +Pull requests welcome! replicate-cli is built using [Cobra](https://cobra.dev). + +--- + +**Copyright (c) 2022 Jamie Steven. Licensed Under Apache 2.0.** \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 0000000..2ed7d3e --- /dev/null +++ b/TODO @@ -0,0 +1,20 @@ +v0.1.0 +Code cleanup +Document code where needed +Publish to GitHub with license +"Release in GitHub" + +v0.2.0 +Support text models, like Stable Diffusion +Support models with unique inputs using via flags or arguments +Add local file support as image input +Save output to local file +Consider normalizing model output (always wrap in JSON?) + +v0.? +Tests? +Improved documentation? +Packagecloud binary support? + +Notes: +Suggest Replicate team return 404 for https://api.replicate.com/v1/predictions on non-existant version IDs \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..13d7cee --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "os" + + token "github.com/jamiesteven/replicate-cli/lib" + + "github.com/spf13/cobra" +) + +var TokenFlag string +var TokenKey string + +var rootCmd = &cobra.Command{ + Use: "replicate-cli [command]", + Short: "replicate-cli", + Long: `replicate-cli 💫 A command line interface for Replicate ❤️ +Version 0.1.0 by Jamie Steven (github.com/jamiesteven)`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + tokenResp, err := token.GetTokenKey(TokenFlag) + if err != nil { + return err + } + TokenKey = tokenResp + return nil + }, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +func init() { + rootCmd.PersistentFlags().StringVarP(&TokenFlag, "token", "t", "", "Replicate API token. Uses REPLICATE_TOKEN environment variable if not specified.") +} diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 0000000..312ca94 --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,95 @@ +package cmd + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/briandowns/spinner" + "github.com/go-resty/resty/v2" + "github.com/spf13/cobra" + "github.com/tidwall/gjson" +) + +var runCmd = &cobra.Command{ + Use: "run [version/model] [input]", + Short: "Run a model.", + Long: "Run a model. Provide a model version ID, or a model name, i.e. 'tencentarc/gfpgan'", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("No model and/or input specified. Run 'replicate-cli run [model] [input]'.\n") + } + + // Get latest version ID if args[0] is a model name, i.e. contains a slash + var versionId string + if strings.Contains(args[0], "/") { + url := "https://api.replicate.com/v1/models/" + args[0] + "/versions" + + client := resty.New() + resp, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Token "+TokenKey). + Get(url) + if err != nil { + return err + } else if resp.Status() == "401 Unauthorized" { + return errors.New("Unauthorized. Specify Replicate API token with --token or REPLICATE_TOKEN environment variable.\n") + } else if resp.Status() == "404 Not Found" { + return errors.New("Model not found.\n") + } + versionId = gjson.Get(resp.String(), "results.0.id").String() + } else { + versionId = args[0] + } + + client := resty.New() + resp, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Token "+TokenKey). + SetBody(`{"version":"` + versionId + `","input":{"image":"` + args[1] + `"}}`). + Post("https://api.replicate.com/v1/predictions") + if err != nil { + return err + } else if resp.Status() == "401 Unauthorized" { + return errors.New("Unauthorized. Specify Replicate API token with --token or REPLICATE_TOKEN environment variable.\n") + } else if resp.Status() == "404 Not Found" { + return errors.New("Model version not found.\n") + } else if resp.Status() == "400 Bad Request" { + return errors.New("Model version not found.\n") // TODO: Issue: Replicate API should replace this response as a 404 and leave 400 for malformed requests. + } + predictionUrl := gjson.Get(resp.String(), "urls.get").String() + predictionId := gjson.Get(resp.String(), "id").String() + fmt.Println("prediction url: https://replicate.com/p/" + predictionId) + + for { + resp, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Token "+TokenKey). + Get(predictionUrl) + if err != nil { + return err + } + status := gjson.Get(resp.String(), "status").String() + + s := spinner.New(spinner.CharSets[9], 100*time.Millisecond) + s.Prefix = status + ` ` + s.Start() + time.Sleep(3 * time.Second) + s.Stop() + + if status == "succeeded" { + output := gjson.Get(resp.String(), "output") + fmt.Println("succeeded.\n", output) + break + } else if status == "failed" { + return errors.New("Prediction failed.\n") + } + } + return nil + }, +} + +func init() { + rootCmd.AddCommand(runCmd) +} diff --git a/cmd/versions.go b/cmd/versions.go new file mode 100644 index 0000000..ece59d1 --- /dev/null +++ b/cmd/versions.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "errors" + "fmt" + + "github.com/go-resty/resty/v2" + "github.com/spf13/cobra" + "github.com/tidwall/gjson" +) + +var versionsCmd = &cobra.Command{ + Use: "versions [model]", + Short: "Get versions for a specific model.", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("No model specified. Run 'replicate-cli versions [model]'.\n") + } + + url := "https://api.replicate.com/v1/models/" + args[0] + "/versions" + + client := resty.New() + resp, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Token "+TokenKey). + Get(url) + if err != nil { + return err + } else if resp.Status() == "401 Unauthorized" { + return errors.New("Unauthorized. Specify Replicate API token with --token or REPLICATE_TOKEN environment variable.\n") + } else if resp.Status() == "404 Not Found" { + return errors.New("Model not found.\n") + } + + results := gjson.Get(resp.String(), "results").Array() + + for _, value := range results { + id := gjson.Get(value.String(), "id") + created_at := gjson.Get(value.String(), "created_at") + fmt.Println(id, created_at) + } + + return nil + }, +} + +func init() { + rootCmd.AddCommand(versionsCmd) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a42e449 --- /dev/null +++ b/go.mod @@ -0,0 +1,22 @@ +module github.com/jamiesteven/replicate-cli + +go 1.19 + +require ( + github.com/briandowns/spinner v1.19.0 + github.com/go-resty/resty/v2 v2.7.0 + github.com/spf13/cobra v1.5.0 + github.com/tidwall/gjson v1.14.3 +) + +require ( + github.com/fatih/color v1.13.0 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect + golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5263e8a --- /dev/null +++ b/go.sum @@ -0,0 +1,48 @@ +github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E= +github.com/briandowns/spinner v1.19.0/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= +github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho= +golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/lib/token.go b/lib/token.go new file mode 100644 index 0000000..c6e43bc --- /dev/null +++ b/lib/token.go @@ -0,0 +1,22 @@ +package token + +import ( + "errors" + "os" +) + +func GetTokenKey(tokenFlag string) (string, error) { + var tokenString string + + if tokenFlag != "" { + tokenString = tokenFlag + } else { + if os.Getenv("REPLICATE_TOKEN") != "" { + tokenString = os.Getenv("REPLICATE_TOKEN") + } else { + return "", errors.New("No token provided. Specify Replicate API token with --token or REPLICATE_TOKEN environment variable.\n") + } + } + + return tokenString, nil +} diff --git a/replicate.go b/replicate.go new file mode 100644 index 0000000..caa4d0d --- /dev/null +++ b/replicate.go @@ -0,0 +1,7 @@ +package main + +import "github.com/jamiesteven/replicate-cli/cmd" + +func main() { + cmd.Execute() +}