返回 DeepSeek-Reasonix
trust.go
根目录 / internal / cli / trust.go
1 package cli
2
3 import (
4 "bufio"
5 "fmt"
6 "io"
7 "os"
8 "strings"
9
10 "reasonix/internal/boot"
11 "reasonix/internal/config"
12 "reasonix/internal/hook"
13 )
14
15 type trustOptions struct {
16 dir string
17 yes bool
18 revoke bool
19 }
20
21 func trustCommand(args []string) int {
22 return runTrust(args, bufio.NewScanner(os.Stdin), os.Stdout, isInteractive())
23 }
24
25 // runTrust lists the programs a workspace's own files name and records the
26 // person's approval of them, as they stand now, under their Reasonix home.
27 func runTrust(args []string, in *bufio.Scanner, out io.Writer, interactive bool) int {
28 opts, err := parseTrustOptions(args)
29 if err != nil {
30 fmt.Fprintln(out, err)
31 return 2
32 }
33 root := boot.ResolveWorkspaceRoot(opts.dir)
34 store := config.NewProjectProgramStore(config.ReasonixHomeDir())
35 if opts.revoke {
36 if err := store.Revoke(root); err != nil {
37 fmt.Fprintln(out, "revoke:", err)
38 return 1
39 }
40 fmt.Fprintf(out, "Revoked every program approval for %s.\n", root)
41 return 0
42 }
43 pending, err := pendingProjectPrograms(root)
44 if err != nil {
45 fmt.Fprintln(out, "load:", err)
46 return 1
47 }
48 if len(pending) == 0 {
49 fmt.Fprintf(out, "Nothing in %s is waiting for approval.\n", root)
50 return 0
51 }
52 fmt.Fprintf(out, "%s names programs Reasonix would run on your machine:\n", root)
53 for _, p := range pending {
54 fmt.Fprintf(out, " [%s] %s\n %s\n declaration: %s\n", p.Kind, p.Name, p.Detail, p.Declaration)
55 for _, f := range p.Files {
56 fmt.Fprintf(out, " file (content checked): %s\n", f)
57 }
58 }
59 if !opts.yes {
60 if !interactive {
61 fmt.Fprintln(out, "Nothing approved. Review them, then run `reasonix trust --yes` here to approve.")
62 return 1
63 }
64 if answer := ask(in, out, "Approve them as they are now?", "y/N"); !strings.EqualFold(strings.TrimSpace(answer), "y") {
65 fmt.Fprintln(out, "Nothing approved.")
66 return 1
67 }
68 }
69 if err := store.Approve(root, pending...); err != nil {
70 fmt.Fprintln(out, "approve:", err)
71 return 1
72 }
73 fmt.Fprintln(out, "Approved. Any change to them needs approval again.")
74 return 0
75 }
76
77 func pendingProjectPrograms(root string) ([]config.ProjectProgram, error) {
78 cfg, err := config.LoadForRootReadOnly(root)
79 if err != nil {
80 return nil, err
81 }
82 pending := cfg.PendingProjectPrograms()
83 if p, ok := hook.PendingProjectHooks(hook.LoadOptions{ProjectRoot: root}); ok {
84 pending = append(pending, p)
85 }
86 return pending, nil
87 }
88
89 func parseTrustOptions(args []string) (trustOptions, error) {
90 var opts trustOptions
91 for i := 0; i < len(args); i++ {
92 switch arg := args[i]; {
93 case arg == "--yes" || arg == "-y":
94 opts.yes = true
95 case arg == "--revoke":
96 opts.revoke = true
97 case arg == "--dir" && i+1 < len(args):
98 i++
99 opts.dir = args[i]
100 case strings.HasPrefix(arg, "--dir="):
101 opts.dir = strings.TrimPrefix(arg, "--dir=")
102 default:
103 return opts, fmt.Errorf("usage: reasonix trust [--dir PATH] [--yes|--revoke] (unknown argument %q)", arg)
104 }
105 }
106 if opts.yes && opts.revoke {
107 return opts, fmt.Errorf("usage: reasonix trust [--dir PATH] [--yes|--revoke] (choose one)")
108 }
109 return opts, nil
110 }
111
111 lines GO