cmsctl: persist a scoped API key for script auth

Add `auth api-key {set|clear}` and prefer a stored key over the device
token, so scripts authenticate with a permanent minted API key
(X-API-Key) instead of re-running the 12h device flow. Explicit flags
and env still win over persisted credentials. Also treat a zero-byte
config file as defaults (previously every command failed to parse), and
correct AGENTS.md, which claimed device tokens auto-refresh.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
mats committed 2026-08-29 14:52:50 +02:00
1 parent cb7967e419
commit a5d00c9d7e
3 files changed
+111 -6

No files matched your search

+11 -1
View File
@@ -1,6 +1,7 @@
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@@ -23,6 +24,9 @@ type Config struct {
AccessToken string `json:"access_token,omitempty"`
TokenType string `json:"token_type,omitempty"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
// APIKey is a minted scoped API key (AUTH_APIKEYS_FILE). Prefer over a
// device access token for scripts: it has no expiry. Sent as X-API-Key.
APIKey string `json:"api_key,omitempty"`
}
func defaultConfig() Config {
@@ -64,6 +68,11 @@ func loadConfig(path string) (Config, error) {
if err != nil {
return Config{}, fmt.Errorf("read config %s: %w", path, err)
}
// A zero-byte or whitespace-only file (e.g. created by `touch` or a
// botched write) is not a corrupt config; fall back to defaults.
if len(bytes.TrimSpace(body)) == 0 {
return cfg, nil
}
if err := json.Unmarshal(body, &cfg); err != nil {
return Config{}, fmt.Errorf("parse config %s: %w", path, err)
}
@@ -118,10 +127,11 @@ func saveConfig(path string, cfg Config) error {
return nil
}
func clearToken(cfg *Config) {
func clearCredentials(cfg *Config) {
cfg.AccessToken = ""
cfg.TokenType = "Bearer"
cfg.ExpiresAt = time.Time{}
cfg.APIKey = ""
}
func effectiveBaseURL(cfg Config) string {