feat(cli): agent-readable account list (reduced JSON view)

account list now routes to the agent role; an agent (EMCLI_KEY only) gets a
JSON envelope of name/from/can_send, while the admin keeps the full text
table. account add/edit/remove stay admin-only.

Also emit the agent path's missing-key/open failure as a JSON Failure
envelope (per spec), and update the stale run_test case that asserted the
old admin-only behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-23 21:37:37 +01:00
parent 64ff32ab29
commit 2140d9e173
6 changed files with 151 additions and 18 deletions
+11 -4
View File
@@ -27,9 +27,16 @@ func realMailer(acc store.Account) (Mailer, error) {
// commandRole maps a command to the privilege it requires. Admin commands
// mutate configuration or expose oversight data; everything else is agent.
func commandRole(cmd string) store.Role {
switch cmd {
case "account", "whitelist", "config", "audit":
func commandRole(args []string) store.Role {
switch args[0] {
case "account":
// account list is a read-only discovery view available to agents;
// add/edit/remove mutate config and require admin.
if len(args) >= 2 && args[1] == "list" {
return store.RoleAgent
}
return store.RoleAdmin
case "whitelist", "config", "audit":
return store.RoleAdmin
default: // list, get, search, ack, send, doctor
return store.RoleAgent
@@ -135,7 +142,7 @@ func Run(args []string, out, errOut io.Writer) int {
return 0
}
cmd, rest := args[0], args[1:]
role := commandRole(cmd)
role := commandRole(args)
switch cmd {
case "list", "get", "search", "ack":
return runAgent(cmd, rest, role, out, errOut)