da9faa9e07
Borrows the lark-cli agent-affordance model (https://github.com/larksuite/cli/blob/main/AGENTS.md + skills/lark-shared/SKILL.md) so weknora is designed to be agent-friendly: error messages, output format, and flag design follow conventions agents can rely on. cli/AGENTS.md (operational reference for LLM agents invoking weknora): Public document covering envelope schema, exit-code protocol (0/1/2/10/130), stdout/stderr separation, and behavioral rules. Sensitive commands (\`context use\`, \`kb delete\`, \`doc delete\`, \`init\`) gain "AI agents:" paragraphs in their cobra Long descriptions so guidance shows in --help. format.Envelope schema additions: Risk per-operation classification (read / write / high-risk-write + action description), populated by write commands on both success and failure paths. Notice system advisories (CLI update available, server-CLI version skew); type defined, emit sites land in v0.3. DryRun marker for envelopes returned from --dry-run preview paths. RiskLevel constants realigned to lark's taxonomy: read / write / high-risk-write (was: read / mutating / destructive — not yet wired by any command). cmdutil.Error gains OperationRisk; PrintErrorEnvelope auto-attaches it to envelope.Risk so destructive failure paths surface uniformly. Exit-10 confirmation protocol: New ErrorCode \`input.confirmation_required\` mapped to exit code 10 in cmdutil.ExitCode. ConfirmDestructive now returns this code (with OperationRisk attached) when stdout is non-TTY or --json was set, with -y/--yes absent. Previous behavior — silent proceed in non-TTY — was unsafe: scripts and agents could delete resources with no explicit approval. Three test cases re-pinned around the new contract. This is a wire-contract change for any caller who relied on silent proceed; v0.0/v0.1 had no destructive commands, so the blast radius is contained to v0.2 itself. --dry-run global flag: cmd write paths (kb create/delete, doc upload/delete, api POST/PUT/PATCH/ DELETE) check cmdutil.IsDryRun(cmd) and skip the SDK call, emitting an envelope with dry_run=true plus a Risk classification. Read commands ignore --dry-run by design (no side effect to preview). Human-mode prints \`[dry-run] would <action>\` to stdout. Command discovery: agents introspect via the existing \`--help\` surface (consistent with gh / kubectl / aws / gcloud / terraform — none of them ship a CLI-tree self-description command). An earlier draft added a \`weknora schema\` reflection command; dropped after a mainstream survey found it has no stable analog (lark-cli's schema describes Lark API methods, not its own CLI tree). Tests: 27 cli packages pass at this commit. Added two new tests covering envelope.risk and envelope._notice serialization.
58 lines
2.1 KiB
Go
58 lines
2.1 KiB
Go
package cmdutil
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/Tencent/WeKnora/cli/internal/iostreams"
|
|
"github.com/Tencent/WeKnora/cli/internal/prompt"
|
|
)
|
|
|
|
// ConfirmDestructive guards a destructive operation (delete, force-overwrite)
|
|
// behind explicit user approval. Behavior matrix:
|
|
//
|
|
// yes=true → proceed (explicit user opt-in via -y/--yes)
|
|
// non-TTY OR jsonOut → return CodeInputConfirmationRequired (exit 10);
|
|
// no UI to prompt, agent/CI must re-invoke with -y
|
|
// after the human explicitly approves
|
|
// TTY + interactive → prompt; user-yes proceeds, user-no returns
|
|
// CodeUserAborted ("Aborted." to stderr)
|
|
// prompter error → returns CodeInputMissingFlag (rare; stdin closed
|
|
// mid-prompt)
|
|
//
|
|
// The non-TTY branch is the lark-cli skill protocol: high-risk writes
|
|
// always require explicit confirmation in scripted contexts, never silent
|
|
// proceed. See cli/AGENTS.md "Exit codes" and
|
|
// https://github.com/larksuite/cli/blob/main/skills/lark-shared/SKILL.md.
|
|
//
|
|
// `yes` should be sourced from the persistent global -y/--yes flag.
|
|
//
|
|
// On exit-10 path, the returned *Error carries OperationRisk so the envelope
|
|
// printer attaches `risk: {level: "high-risk-write", action: ...}`.
|
|
func ConfirmDestructive(p prompt.Prompter, yes, jsonOut bool, what, id string) error {
|
|
if yes {
|
|
return nil
|
|
}
|
|
risk := &OperationRisk{Level: "high-risk-write", Action: fmt.Sprintf("delete %s %s", what, id)}
|
|
if !iostreams.IO.IsStdoutTTY() || jsonOut {
|
|
e := NewError(
|
|
CodeInputConfirmationRequired,
|
|
fmt.Sprintf("delete %s %s requires explicit confirmation: re-run with -y/--yes", what, id),
|
|
)
|
|
var typed *Error
|
|
if errors.As(e, &typed) {
|
|
typed.OperationRisk = risk
|
|
}
|
|
return e
|
|
}
|
|
ok, err := p.Confirm(fmt.Sprintf("Delete %s %s? This cannot be undone.", what, id), false)
|
|
if err != nil {
|
|
return Wrapf(CodeInputMissingFlag, err, "confirm delete")
|
|
}
|
|
if !ok {
|
|
fmt.Fprintln(iostreams.IO.Err, "Aborted.")
|
|
return NewError(CodeUserAborted, "delete aborted")
|
|
}
|
|
return nil
|
|
}
|