cli/internal/creds/creds.go
Alex Dunmow 2f065e3ed4 feat: ninja CLI in its own repo (block/cli, WO-WZ-023)
The ninja developer CLI moves out of block/core into its own module
git.dev.alexdunmow.com/block/cli. Pins block/core@v0.18.2 for abi/v1, the
plugin.mod parser, and DESCRIBE guest builds; vendors its own orchestrator
registry client (internal/api, generated from a vendored plugin_registry.proto)
since it cannot import block/core/internal. Produces byte-identical v1
artifacts (verified: art-deco codeless SHA256-identical to the pre-move build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 22:39:32 +08:00

89 lines
2.2 KiB
Go

package creds
import (
"encoding/json"
"errors"
"os"
"path/filepath"
)
type Credentials struct {
DefaultHost string `json:"default_host"`
Hosts map[string]HostCreds `json:"hosts"`
}
type HostCreds struct {
Token string `json:"token"`
User string `json:"user,omitempty"`
DefaultScope string `json:"default_scope,omitempty"`
// ActiveAccountID is the orchestrator-side UUID of the account that
// account-scoped commands (notably `ninja plugins publish --private`)
// operate against. Set during `ninja login` (forced selection when the
// user belongs to more than one account) and changeable via
// `ninja account set`.
ActiveAccountID string `json:"active_account_id,omitempty"`
// ActiveAccountSlug mirrors ActiveAccountID in human-readable form for
// display in CLI output. The orchestrator-side slug is authoritative;
// the CLI refreshes it whenever it talks to the server.
ActiveAccountSlug string `json:"active_account_slug,omitempty"`
}
func filePath() (string, error) {
dir, err := os.UserConfigDir()
if err != nil {
return "", err
}
return filepath.Join(dir, "ninja", "credentials.json"), nil
}
func Load() (*Credentials, error) {
p, err := filePath()
if err != nil {
return nil, err
}
b, err := os.ReadFile(p)
if errors.Is(err, os.ErrNotExist) {
return &Credentials{Hosts: map[string]HostCreds{}}, nil
}
if err != nil {
return nil, err
}
c := &Credentials{Hosts: map[string]HostCreds{}}
if err := json.Unmarshal(b, c); err != nil {
return nil, err
}
return c, nil
}
func (c *Credentials) Save() error {
p, err := filePath()
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(p), 0o700); err != nil {
return err
}
b, err := json.MarshalIndent(c, "", " ")
if err != nil {
return err
}
return os.WriteFile(p, b, 0o600)
}
func (c *Credentials) Resolve(host string) (string, HostCreds, error) {
if host == "" {
host = c.DefaultHost
}
if host == "" {
host = "https://my.blockninjacms.com"
}
if t := os.Getenv("NINJA_TOKEN"); t != "" {
return host, HostCreds{Token: t}, nil
}
hc, ok := c.Hosts[host]
if !ok || hc.Token == "" {
return host, HostCreds{}, errors.New("not logged in; run `ninja login`")
}
return host, hc, nil
}