guide: read-only guided manual-rotation layer (links + worklist + TUI)
Advances the guided-rotation goal without rotating anything. - internal/links: offline change-password URL generation — curated per-service table + RFC 8615 /.well-known/change-password + parent-domain fallback; derives a web host from a credential (git/netrc "user @ host", docker registry, aws) - internal/worklist: secrets-free rotation worklist (source, identity, auto/manual, link) + Markdown renderer - internal/tui: Bubble Tea walk-through (one credential per screen, open link, mark done/skip, progress) — READ-ONLY, enters/stores no secret - cmd: `incredigo worklist` (offline, stdout/--out) and `incredigo guide` (TUI) - tests: link derivation, worklist build/markdown + no-secret-column, TUI navigation/done/quit/view Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
// Package worklist turns discovered credentials into a rotation worklist: per
|
||||
// credential, whether it can be auto-rotated (a driver exists) or is manual, and
|
||||
// the page to rotate it on. It contains NO secrets — only non-secret metadata and
|
||||
// links — so a worklist is safe to write to disk or print.
|
||||
package worklist
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"incredigo/internal/discover"
|
||||
"incredigo/internal/links"
|
||||
"incredigo/internal/rotate"
|
||||
)
|
||||
|
||||
// Item is one credential's rotation entry.
|
||||
type Item struct {
|
||||
Index int
|
||||
Credential discover.Credential
|
||||
Driver string // "" when no rotation driver handles it (manual)
|
||||
Link links.Link
|
||||
}
|
||||
|
||||
// Manual reports whether this credential has no automated rotation path today.
|
||||
func (it Item) Manual() bool { return it.Driver == "" }
|
||||
|
||||
// Build classifies credentials (via the rotate registry) and attaches links.
|
||||
// With no drivers registered every Item is Manual.
|
||||
func Build(creds []discover.Credential) []Item {
|
||||
plans := rotate.PlanAll(creds)
|
||||
items := make([]Item, len(plans))
|
||||
for i, p := range plans {
|
||||
items[i] = Item{
|
||||
Index: i + 1,
|
||||
Credential: p.Credential,
|
||||
Driver: p.Driver,
|
||||
Link: links.LinkFor(p.Credential),
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Markdown renders a worklist as a Markdown document (no secrets).
|
||||
func Markdown(items []Item) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("# incredigo rotation worklist\n\n")
|
||||
fmt.Fprintf(&b, "%d credential(s). No secrets are included in this file.\n\n", len(items))
|
||||
b.WriteString("| # | source | identity | rotation | change-password link |\n")
|
||||
b.WriteString("|---|--------|----------|----------|----------------------|\n")
|
||||
for _, it := range items {
|
||||
rot := "manual"
|
||||
if !it.Manual() {
|
||||
rot = "auto: " + it.Driver
|
||||
}
|
||||
url := it.Link.URL
|
||||
if url == "" {
|
||||
url = "— (no web page)"
|
||||
}
|
||||
fmt.Fprintf(&b, "| %d | %s | %s | %s | %s |\n",
|
||||
it.Index, it.Credential.Source, it.Credential.Identity, rot, url)
|
||||
}
|
||||
b.WriteString("\nGenerated by incredigo. Links are derived offline ")
|
||||
b.WriteString("(curated table or RFC 8615 /.well-known/change-password).\n")
|
||||
return b.String()
|
||||
}
|
||||
Reference in New Issue
Block a user