feat(cli): positional audit grammar (account positional, ls alias)

This commit is contained in:
2026-06-27 12:33:11 +01:00
parent 1e00f68a3d
commit dbefb68611
3 changed files with 33 additions and 8 deletions
+2 -2
View File
@@ -547,7 +547,7 @@ Every agent action (`list`, `get`, `search`, `ack`, `send`) — allowed or block
```bash
emcli audit list # most recent 50
emcli audit list --account gmail # filter to one account
emcli audit list gmail # filter to one account
emcli audit list --limit 200
```
@@ -626,7 +626,7 @@ emcli account remove N [--yes] # delete an account (--yes o
emcli whitelist <add|remove|list|enable|disable> N [addr…] --in|--out
emcli config list # list all settings and descriptions
emcli config set|get <key> [value] # e.g. audit_retention_days
emcli audit list [--account N] [--limit K]
emcli audit list [account] [--limit K]
emcli version
# Agent (requires EMCLI_KEY or EMCLI_ADMIN_KEY; one line of JSON each)
+15 -6
View File
@@ -339,21 +339,30 @@ func runConfig(args []string, role store.Role, out, errOut io.Writer) int {
}
}
// runAudit handles `audit list [--account <name>] [--limit N]`.
// runAudit handles `audit list [account] [--limit N]`.
func runAudit(args []string, role store.Role, out, errOut io.Writer) int {
if len(args) > 0 && helpRequested(args[0]) {
printCmdUsage(out, "audit")
return 0
}
if len(args) == 0 || args[0] != "list" {
fmt.Fprintln(errOut, "usage: emcli audit list [--account <name>] [--limit N]")
if len(args) == 0 || normalizeVerb(args[0]) != "list" {
fmt.Fprintln(errOut, "usage: emcli audit list [account] [--limit N]")
return 2
}
// Peel an optional positional account before flag parsing.
rest := args[1:]
var account string
if len(rest) > 0 && !strings.HasPrefix(rest[0], "-") {
account, rest = rest[0], rest[1:]
}
fs := flag.NewFlagSet("audit list", flag.ContinueOnError)
fs.SetOutput(errOut)
account := fs.String("account", "", "filter by account")
limit := fs.Int("limit", 50, "max rows")
if err := fs.Parse(args[1:]); err != nil {
if err := fs.Parse(rest); err != nil {
return 2
}
if fs.NArg() > 0 {
fmt.Fprintf(errOut, "unexpected argument %q\n", fs.Arg(0))
return 2
}
st, err := openStore(role)
@@ -362,7 +371,7 @@ func runAudit(args []string, role store.Role, out, errOut io.Writer) int {
return 1
}
defer st.Close()
if err := auditList(st, *account, *limit, out); err != nil {
if err := auditList(st, account, *limit, out); err != nil {
fmt.Fprintf(errOut, "audit list: %v\n", err)
return 1
}
+16
View File
@@ -224,6 +224,22 @@ func TestAuditListCoreRenders(t *testing.T) {
}
}
func TestAuditListPositionalAccount(t *testing.T) {
adminEnv(t)
// Positional account + `ls` alias must be accepted (empty log → exit 0).
if code, _, e := run(t, "audit", "ls", "someacct"); code != 0 {
t.Fatalf("audit ls <account> should succeed: code=%d err=%q", code, e)
}
// Extra positional is a usage error.
if code, _, _ := run(t, "audit", "list", "a", "b"); code != 2 {
t.Fatal("extra positional must be a usage error")
}
// The removed --account flag is now a usage error.
if code, _, _ := run(t, "audit", "list", "--account", "x"); code != 2 {
t.Fatal("removed --account flag should now be a usage error")
}
}
func TestAccountEditFromValidationRejectsMalformed(t *testing.T) {
adminEnv(t)
run(t, "account", "add", "valacc", "--imap-host", "imap.x.com", "--username", "u@x.com")