diff --git a/.gitignore b/.gitignore index 9abefd28..57443f01 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ go.work* **coverage.txt src/*.yaml .vscode/ +src/cli diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 824e9b26..6ff3872e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,104 +1,182 @@ -# Contributing +# Contributing to opslevel-cli -1. [About this document](#about-this-document) -1. [Getting the code](#getting-the-code) -1. [Local development](#local-development) -1. [Submitting a Pull Request](#submitting-a-pull-request) +👋 Welcome, and thank you for your interest in contributing to `opslevel-cli`! This guide will help you ramp up, propose changes, develop locally, and contribute code effectively. -## About this document +--- -This document is a guide intended for folks interested in contributing to `opslevel-cli`. Below, we document the process by which members of the community should create issues and submit pull requests (PRs) in this repository. This guide assumes you are using macOS and are comfortable with the command line. +## Table of Contents -If you're new to Golang development or contributing to open-source software, we encourage you to read this document from start to finish. +1. [About the CLI](#about-the-cli) +2. [Getting Started](#getting-started) +3. [Development Workflow](#development-workflow) +4. [Proposing and Submitting Changes](#proposing-and-submitting-changes) +5. [Command Architecture & Style](#command-architecture--style) +6. [Testing & Tooling](#testing--tooling) +7. [Release Process](#release-process) -## Proposing a change +--- -This project is what it is today because community members like you have opened issues, provided feedback, and contributed to the knowledge loop for the entire community. Whether you are a seasoned open source contributor or a first-time committer, we welcome and encourage you to contribute code, documentation, ideas, or problem statements to this project. +## About the CLI -### Defining the problem +The `opslevel-cli` is a command-line interface for interacting with the [OpsLevel](https://www.opslevel.com) API. It helps engineers automate, inspect, and manage their service catalog, ownership, checks, and more. -If you have an idea for a new feature or if you've discovered a bug, the first step is to open an issue. Please check the list of [open issues](https://github.com/OpsLevel/cli/issues) before creating a new one. If you find a relevant issue, please add a comment to the open issue instead of creating a new one. +### Architecture -> **Note:** All community-contributed Pull Requests _must_ be associated with an open issue. If you submit a Pull Request that does not pertain to an open issue, you will be asked to create an issue describing the problem before the Pull Request can be reviewed. +**[Cobra](https://github.com/spf13/cobra)** is the library we use to define our CLI. Commands are defined as Go structs with handlers, descriptions, and flags. +- **[Viper](https://github.com/spf13/viper)** handles flag parsing, environment variables, and configuration files. Examples of our usage of it are [here](https://github.com/OpsLevel/cli/blob/main/src/cmd/root.go#L44) and [here](https://github.com/OpsLevel/cli/blob/main/src/cmd/policy.go#L246) +- Modular command files live under `/cmd`, grouped by functionality (e.g., services, checks, etc.). +- Commands are registered to `rootCmd` via `init()` functions. +- 80% of our functionality is provided by opslevel-go and the purpose of this CLI is just to marshal data between the user and opslevel-go in a UX friendly way. +- Most commands follow the standard CRUD pattern `opslevel create ...`, `opslevel get ...`, `opslevel list ...`, `opslevel update ...`, `opslevel delete ...`, etc. +- We have an `opslevel beta` subcommand for experimental commands that are subject to change. -### Submitting a change +--- -If an issue is appropriately well scoped and describes a beneficial change to the codebase, then anyone may submit a Pull Request to implement the functionality described in the issue. See the sections below on how to do this. +## Getting Started -The maintainers will add a `good first issue` label if an issue is suitable for a first-time contributor. This label often means that the required code change is small or a net-new addition that does not impact existing functionality. You can see the list of currently open issues on the [Contribute](https://github.com/OpsLevel/cli/contribute) page. +### Clone the Repo -## Getting the code +If you're an external contributor fork the repo, then: -### Installing git - -You will need `git` in order to download and modify the source code. On macOS, the best way to download git is to just install [Xcode](https://developer.apple.com/support/xcode/). +```bash +git clone https://github.com/YOUR_USERNAME/cli.git +cd cli +git checkout -b my-feature +``` -### External contributors +If you're part of the OpsLevel org: -If you are not a member of the `OpsLevel` GitHub organization, you can contribute by forking the repository. For a detailed overview on forking, check out the [GitHub docs on forking](https://help.github.com/en/articles/fork-a-repo). In short, you will need to: +```bash +git clone git@github.com:OpsLevel/cli.git +cd cli +git checkout -b my-feature +``` -1. fork the repository -2. clone your fork locally -3. check out a new branch for your proposed changes -4. push changes to your fork -5. open a pull request from your forked repository +> You might need to be given permissions to the repo, please reach out to team platform. -### OpsLevel contributors +### Prerequisites -If you are a member of the `OpsLevel` GitHub organization, you will have push access to the repo. Rather than forking to make your changes, just clone the repository, check out a new branch, and push directly to that branch. +- [Task](https://taskfile.dev) +- An [OpsLevel API Token](https://app.opslevel.com/api_tokens) -## Local Development +You can use `task setup` to run an idempotent one-time setup. -### Installation +Set your API token: -First make sure you have working [golang development environment](https://learn.gopherguides.com/courses/preparing-your-environment-for-go-development) setup. +```sh +export OPSLEVEL_API_TOKEN=your_token_here +``` -You will also need an [OpsLevel API Token](https://app.opslevel.com/api_tokens) from your account to successfully make API calls against. Once you have the API token it is best to put it in your terminal's environment +If you need to target a different environment, set the `OPSLEVEL_API_URL` environment variable: ```sh - export OPSLEVEL_API_TOKEN=XXXXXXXXX +export OPSLEVEL_API_URL=https://self-hosted.opslevel.dev/ ``` -### Local Development Testing +--- + +## Development Workflow -You can run your local code as if it was a prebuilt CLI using: +### Run the CLI Locally ```sh -go run main.go -h +cd ./src +go run main.go --help ``` -This way you can iterate on the CLI code quickly to test out your new functionality. +This is the easiest way to test your changes live. -#### Local Development with an `opslevel-go` Feature Branch +### Using a commit from an `opslevel-go` Branch -To test local code against a feature branch in the `opslevel-go` repository, run: +Sometimes you will need to make CLI changes in tandem with changes to [`opslevel-go`](https://github.com/OpsLevel/opslevel-go). +To test changes from a local branch of [`opslevel-go`](https://github.com/OpsLevel/opslevel-go): ```sh -# initializes opslevel-go submodule then sets up src/go.work +# Set up workspace using task task workspace -# git checkouts my-feature-branch in the src/submodules/opslevel-go directory +# Switch to your feature branch in the submodule git -C ./src/submodules/opslevel-go checkout --track origin/my-feature-branch ``` -Code imported from `github.com/opslevel/opslevel-go` will now be sourced from the -local `my-feature-branch`. +All CLI calls will now use your local `opslevel-go` code checked out into the submodule at `./src/submodules/opslevel-go`. +This way you can effectively work on both the CLI and `opslevel-go` in parallel if needed. + +## Testing & Tooling + +- Use `task test` to run tests locally +- Use `task lint` to check for code quality issues locally +- Use `task fix` to fix formatting, linting, go.mod, and update submodule all in one go + +Our CI pipeline will run `task ci` which can also be run locally to debug any issue that might only arise in CI. + +--- + +## Proposing and Submitting Changes + +### 1. Open an Issue -### Changie (Change log generation) +If you're fixing a bug or adding a feature, please [open an issue](https://github.com/OpsLevel/cli/issues) first. This helps us track discussions and ensures your work aligns with project goals. -Before submitting the pull request, you need add a change entry via Changie so that your contribution changes can be tracked for our next release. +> **Note:** All PRs must be tied to a GitHub issue. -To install Changie, follow the directions [here](https://changie.dev/guide/installation/). +Look for issues marked `good first issue` if you're new. + +### 2. Submit a Pull Request + +- Push your changes to your fork or feature branch +- Run `changie new` to create a changelog entry +- Open a PR to `main` or the relevant feature branch +- Our GitHub Actions pipeline will run tests and lint checks +- A maintainer will review your PR and request changes if needed + +Once approved and merged, your change will be included in the next release. + +--- + +## Command Architecture & Style + +- Each CLI command lives in its own file under `/cmd` +- Commands should: + - Register themselves via `init()` + - Use `RunE` instead of `Run` for error handling +- Flags and environment variables should be registered with Viper for consistency +- Prefer readable, minimalistic command logic — delegate heavy logic to opslevel-go unless it's not possible + +### Minimal Command Example + +The following shows the minimum amount of code needed to create a command. There are a plethora of commands already registered to the root command, so this is just an example of how to create a new command. +Please take a look at the existing commands to get a feel for how they work and whats possible. + +```go +var exampleCmd = &cobra.Command{ + Use: "example", + Short: "Hello World Command", + Long: "Hello World Command to show how an example command works", + RunE: func(cmd *cobra.Command, args []string) error { + log.Info().Msg("Hello World!") + return nil + }, +} + +func init() { + rootCmd.AddCommand(exampleCmd) +} +``` -Next, to create a new change entry, in the root of the repository run: `changie new` +--- -Follow the prompts to create your change entry - remember this is what will show up in the changelog. Changie registers the change in a .yaml file, and that file must be included in your pull request before we can release. +## Release Process -## Submitting a Pull Request +- All customer facing changes must have a [Changie](https://changie.dev) changelog entry + - Run: `changie new` + - Follow prompts to categorize your change + - This generates a YAML file in `.changes/` that must be committed with your PR -OpsLevel provides a CI environment to test changes through GitHub Actions. For example, if you submit a pull request to the repo, GitHub will trigger automated code checks and tests upon approval from an OpsLevel maintainer. +- CI/CD (GitHub Actions) runs lint and tests automatically on pull requests and the main branch +- Maintainers will merge once approved +- Your contribution will be included in the next versioned release (triggered by a maintainer) -A maintainer will review your PR. They may suggest code revision for style or clarity, or request that you add unit or integration test(s). These are good things! We believe that, with a little bit of help, anyone can contribute high-quality code. -- First time contributors should be aware that code checks + unit tests require a maintainer to approve. +--- -Once all tests are passing and your PR has been approved, a maintainer will merge your changes into the active development branch. And that's it! It will be available in the next release that is cut. Happy developing :tada: +Happy hacking 🎉 and thank you for helping improve the `opslevel-cli`! diff --git a/src/cmd/beta.go b/src/cmd/beta.go new file mode 100644 index 00000000..268dec3a --- /dev/null +++ b/src/cmd/beta.go @@ -0,0 +1,13 @@ +package cmd + +import "github.com/spf13/cobra" + +var betaCmd = &cobra.Command{ + Use: "beta", + Short: "Beta commands that are subject to removal", + Long: "Beta commands that are subject to removal", +} + +func init() { + rootCmd.AddCommand(betaCmd) +} diff --git a/src/cmd/mcp.go b/src/cmd/mcp.go new file mode 100644 index 00000000..6df47bdb --- /dev/null +++ b/src/cmd/mcp.go @@ -0,0 +1,181 @@ +package cmd + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + "github.com/opslevel/cli/common" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" +) + +type NullArguments struct{} + +type LightweightComponent struct { + Id string `json:"id"` + Name string `json:"name"` + Owner string `json:"owner"` + URL string `json:"url"` +} + +var mcpCmd = &cobra.Command{ + Use: "mcp", + Short: "MCP Server", + Long: "MCP Server", + + RunE: func(cmd *cobra.Command, args []string) error { + done := make(chan struct{}) + + s := server.NewMCPServer( + "OpsLevel", + "1.0.0", + ) + + client := common.NewGraphClient(fmt.Sprintf("mcp-%s", version)) + + // Register Teams + s.AddTool( + mcp.NewTool("teams", + mcp.WithDescription("Get all the team names, identifiers and metadata for the opslevel account. Teams are owners of other objects in opslevel. Only use this if you need to search all teams.")), + func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + resp, err := client.ListTeams(nil) + if err != nil { + return nil, err + } + data, err := json.Marshal(resp.Nodes) + if err != nil { + return nil, err + } + return mcp.NewToolResultText(string(data)), nil + }) + + // Register Users + s.AddTool( + mcp.NewTool("users", mcp.WithDescription("Get all the user names, e-mail addresses and metadata for the opslevel account. Users are the people in opslevel. Only use this if you need to search all users.")), + func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + resp, err := client.ListUsers(nil) + if err != nil { + return nil, err + } + data, err := json.Marshal(resp.Nodes) + if err != nil { + return nil, err + } + return mcp.NewToolResultText(string(data)), nil + }) + + // Register Actions + s.AddTool( + mcp.NewTool("actions", mcp.WithDescription("Get all the information about actions the user can run in the opslevel account")), + func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + resp, err := client.ListTriggerDefinitions(nil) + if err != nil { + return nil, err + } + data, err := json.Marshal(resp.Nodes) + if err != nil { + return nil, err + } + return mcp.NewToolResultText(string(data)), nil + }) + + // Register Filters + s.AddTool( + mcp.NewTool("filters", mcp.WithDescription("Get all the rubric filter names and which predicates they have for the opslevel account")), + func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + resp, err := client.ListFilters(nil) + if err != nil { + return nil, err + } + data, err := json.Marshal(resp.Nodes) + if err != nil { + return nil, err + } + return mcp.NewToolResultText(string(data)), nil + }) + + // Register Components + s.AddTool( + mcp.NewTool("components", mcp.WithDescription("Get all the components in the opslevel account. Components are objects in opslevel that represent things like apis, libraries, services, frontends, backends, etc.")), + func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + resp, err := client.ListServices(nil) + if err != nil { + return nil, err + } + var components []LightweightComponent + for _, node := range resp.Nodes { + components = append(components, LightweightComponent{ + Id: string(node.Id), + Name: node.Name, + Owner: node.Owner.Alias, + URL: node.HtmlURL, + }) + } + data, err := json.Marshal(components) + if err != nil { + return nil, err + } + return mcp.NewToolResultText(string(data)), nil + }) + + // Register Infra + s.AddTool( + mcp.NewTool("infrastructure", mcp.WithDescription("Get all the infrastructure in the opslevel account. Infrastructure are objects in opslevel that represent cloud provider resources like vpc, databases, caches, networks, vms, etc.")), + func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + resp, err := client.ListInfrastructure(nil) + if err != nil { + return nil, err + } + data, err := json.Marshal(resp.Nodes) + if err != nil { + return nil, err + } + return mcp.NewToolResultText(string(data)), nil + }) + + // Register Domains + s.AddTool( + mcp.NewTool("domains", mcp.WithDescription("Get all the domains in the opslevel account. Domains are objects in opslevel that represent a top-level abstraction used to organize and categorize software systems.")), + func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + resp, err := client.ListDomains(nil) + if err != nil { + return nil, err + } + data, err := json.Marshal(resp.Nodes) + if err != nil { + return nil, err + } + return mcp.NewToolResultText(string(data)), nil + }) + + // Register Systems + s.AddTool( + mcp.NewTool("systems", mcp.WithDescription("Get all the systems in the opslevel account. Systems are objects in opslevel that represent a grouping of services or components that act together to serve a business function or process.")), + func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + resp, err := client.ListSystems(nil) + if err != nil { + return nil, err + } + data, err := json.Marshal(resp.Nodes) + if err != nil { + return nil, err + } + return mcp.NewToolResultText(string(data)), nil + }) + + log.Info().Msg("Starting MCP server...") + if err := server.ServeStdio(s); err != nil { + panic(err) + } + <-done + + return nil + }, +} + +func init() { + betaCmd.AddCommand(mcpCmd) +} diff --git a/src/go.mod b/src/go.mod index 732620a7..e18b7bb4 100644 --- a/src/go.mod +++ b/src/go.mod @@ -11,6 +11,7 @@ require ( github.com/gosimple/slug v1.15.0 github.com/itchyny/gojq v0.12.17 github.com/manifoldco/promptui v0.9.0 + github.com/mark3labs/mcp-go v0.21.0 github.com/mitchellh/mapstructure v1.5.0 github.com/open-policy-agent/opa v0.67.1 github.com/opslevel/opslevel-go/v2024 v2024.12.24 @@ -47,7 +48,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.25.0 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect @@ -87,15 +88,16 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/sdk v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.36.0 // indirect - golang.org/x/net v0.37.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect google.golang.org/protobuf v1.36.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/src/go.sum b/src/go.sum index cf908795..5462af5f 100644 --- a/src/go.sum +++ b/src/go.sum @@ -103,8 +103,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8= -github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= @@ -170,6 +170,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/mark3labs/mcp-go v0.21.0 h1:oyEtiXg8PnrVEFis9b1AwbiUWF2dTbyBP5yLo7SruXE= +github.com/mark3labs/mcp-go v0.21.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -264,6 +266,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= @@ -285,17 +289,17 @@ go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -307,14 +311,14 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +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.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/src/run.sh b/src/run.sh new file mode 100755 index 00000000..85f4b3b9 --- /dev/null +++ b/src/run.sh @@ -0,0 +1,8 @@ +#! /bin/sh + +# Stub script to call `go run` but with the right working directory so that go's +# module system is happy, when called by systems like Claude Desktop that don't +# permit setting the working directory. Primarily useful for development. + +cd "$(dirname "$0")" +go run main.go "$@"