Documentation
¶
Overview ¶
Package hubcli implements `hub`, the command line client for the hub API.
Every command except the handful that manage configuration is generated from the API catalog, so the CLI gains a command the moment an rpc is added to a .proto - there is no table of endpoints to keep in step. That also makes the surface predictable enough for an AI agent to use without being told: rpc ListUser on UserService is `hub user list-user`, its request fields are its flags, and `hub api list` enumerates the lot as JSON.
Index ¶
- Constants
- Variables
- func CommandAliases(op apicatalog.Operation) []string
- func CommandName(op apicatalog.Operation) string
- func ConfigPath() (string, error)
- func Execute(opts Options) int
- func FlagName(field apicatalog.Field) string
- func GroupName(op apicatalog.Operation) string
- func NewRootCommand(opts Options) *cobra.Command
- func Paginated(op apicatalog.Operation) bool
- func Render(w io.Writer, format Format, value any) error
- func RenderReference(catalog *apicatalog.Catalog) string
- func SaveConfig(path string, cfg Config) error
- type APIError
- type CLI
- type Client
- type Config
- type Format
- type OIDC
- type Options
- type Profile
- type Request
- type Settings
- type Token
Constants ¶
const DefaultConfigDir = ".hub"
DefaultConfigDir is the directory holding the CLI's configuration, relative to the user's home directory.
Variables ¶
var Formats = []string{string(FormatJSON), string(FormatYAML), string(FormatTable)}
Formats lists the accepted --output values.
Functions ¶
func CommandAliases ¶
func CommandAliases(op apicatalog.Operation) []string
CommandAliases returns the shorthand for the plain CRUD rpcs - "list" for ListUser under `hub user` - and nothing for the rest.
func CommandName ¶
func CommandName(op apicatalog.Operation) string
CommandName is the subcommand for an rpc, the kebab-case of its name. It is deliberately mechanical: an agent that has read the rpc name in the proto or in `hub api list` can predict the command without consulting a mapping table.
func ConfigPath ¶
ConfigPath returns the location of the configuration file, honouring HUB_CONFIG so a caller can point the CLI at its own file.
func FlagName ¶
func FlagName(field apicatalog.Field) string
FlagName is the flag for a request field: "group_ids" becomes "group-ids".
func GroupName ¶
func GroupName(op apicatalog.Operation) string
GroupName is the command a service is grouped under: "user.v1.UserService" becomes "user", "system.group.v1.GroupService" becomes "group".
func NewRootCommand ¶
NewRootCommand builds the `hub` command tree.
func Paginated ¶
func Paginated(op apicatalog.Operation) bool
Paginated reports whether an operation is a list endpoint --all can follow: one that takes a limit and an offset.
func RenderReference ¶
func RenderReference(catalog *apicatalog.Catalog) string
RenderReference writes the Markdown API reference bundled with the agent skill. It is generated rather than written because a hand-kept reference is exactly the kind of document that quietly goes stale, and an agent following a stale reference produces confidently wrong calls.
func SaveConfig ¶
SaveConfig writes the configuration file, creating its directory. The file is owner-only because it stores bearer tokens and client secrets.
Types ¶
type APIError ¶
type APIError struct {
StatusCode int
Status string
Body json.RawMessage
}
APIError is a non-2xx response from the gateway.
type CLI ¶
type CLI struct {
// contains filtered or unexported fields
}
CLI holds the state shared by every command: the flags parsed off the root, and the settings and client derived from them once flag parsing is done.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client calls the hub REST gateway.
func (*Client) BuildRequest ¶
BuildRequest maps an operation and its argument values onto an HTTP request.
Where each value goes is decided by the operation's google.api.http rule, not by the caller: path fields are substituted into the URL, and the rest become either the JSON body or query parameters depending on whether the rpc declares a body. Values are keyed by protobuf or JSON field name.
func (*Client) Call ¶
func (c *Client) Call(ctx context.Context, op apicatalog.Operation, values map[string]any) (json.RawMessage, error)
Call is BuildRequest followed by Do.
type Config ¶
type Config struct {
// Current is the profile used when --profile is not given.
Current string `yaml:"current,omitempty"`
// Profiles holds every configured environment by name.
Profiles map[string]Profile `yaml:"profiles,omitempty"`
}
Config is the on-disk configuration file.
func LoadConfig ¶
LoadConfig reads the configuration file. A missing file is not an error: the CLI is fully usable from flags and environment variables alone, which is how it is normally run in CI and by agents.
type Format ¶
type Format string
Format is how a command renders its result.
const ( // FormatJSON is indented JSON. It is the default because the CLI's main // consumers - scripts and agents - parse what they read. FormatJSON Format = "json" // FormatYAML is YAML, for eyeballing large responses. FormatYAML Format = "yaml" // FormatTable renders a response's list field as columns, falling back to // JSON for anything that is not a list of objects. FormatTable Format = "table" )
func ParseFormat ¶
ParseFormat validates an --output value.
type OIDC ¶
type OIDC struct {
// Issuer is the realm URL, e.g. "http://localhost:8080/realms/hub".
Issuer string `yaml:"issuer,omitempty"`
// ClientID is the OIDC client the CLI authenticates as.
ClientID string `yaml:"client_id,omitempty"`
// ClientSecret is the client secret, required for confidential clients.
ClientSecret string `yaml:"client_secret,omitempty"`
}
OIDC holds the OpenID Connect client settings used to obtain a token.
type Options ¶
type Options struct {
// Catalog is the API surface to build commands from. Defaults to the
// globally registered descriptors.
Catalog *apicatalog.Catalog
// Version is reported by `hub version`.
Version string
}
Options configures a CLI instance.
type Profile ¶
type Profile struct {
// Endpoint is the base URL of the hub API, e.g. "http://localhost:9090".
Endpoint string `yaml:"endpoint,omitempty"`
// Token is a bearer token. `hub auth login` writes one here.
Token string `yaml:"token,omitempty"`
// RefreshToken renews Token without asking for credentials again. Keycloak
// access tokens last minutes, so without this the CLI would need a fresh
// `hub auth login` several times an hour.
RefreshToken string `yaml:"refresh_token,omitempty"`
// ExpiresAt is when Token stops being accepted. The zero time means
// unknown, in which case the CLI refreshes only after a call is refused.
ExpiresAt time.Time `yaml:"expires_at,omitempty"`
// OIDC describes where `hub auth login` gets a token from.
OIDC OIDC `yaml:"oidc,omitempty"`
}
Profile is one named set of connection settings.
type Request ¶
type Request struct {
Method string `json:"method"`
URL string `json:"url"`
Body json.RawMessage `json:"body,omitempty"`
}
Request is a fully resolved HTTP call, kept as data so --dry-run can print exactly what would be sent without sending it.
type Settings ¶
type Settings struct {
Endpoint string
Token string
RefreshToken string
ExpiresAt time.Time
OIDC OIDC
}
Settings is the connection configuration actually used for a command, after flags, environment variables and the config file have been combined.
type Token ¶
type Token struct {
AccessToken string `json:"access_token" yaml:"access_token"`
RefreshToken string `json:"refresh_token,omitempty" yaml:"refresh_token,omitempty"`
ExpiresIn int `json:"expires_in,omitempty" yaml:"expires_in,omitempty"`
TokenType string `json:"token_type,omitempty" yaml:"token_type,omitempty"`
// contains filtered or unexported fields
}
Token is the subset of an OAuth2 token response the CLI needs.