- commit
- 3e46541
- parent
- 33ea8c5
- author
- Eric Bower
- date
- 2026-02-24 10:18:44 -0500 EST
refactor(ssh): replace wish with pssh
+4,
-0
1@@ -4,6 +4,10 @@ Use spec: https://common-changelog.org/
2
3 ## Staged
4
5+### Changed
6+
7+- Removed charm's `wish` with pico's `pssh`
8+
9 ## v2026-02-24
10
11 ### Changed
M
cfg.go
+1,
-0
1@@ -22,6 +22,7 @@ type GitCfg struct {
2 Host string `koanf:"host"`
3 SshPort string `koanf:"ssh_port"`
4 WebPort string `koanf:"web_port"`
5+ PromPort string `koanf:"prom_port"`
6 AdminsStr []string `koanf:"admins"`
7 Admins []ssh.PublicKey `koanf:"admins_pk"`
8 CreateRepo string `koanf:"create_repo"`
M
cli.go
+33,
-35
1@@ -8,8 +8,7 @@ import (
2 "strings"
3 "text/tabwriter"
4
5- "github.com/charmbracelet/ssh"
6- "github.com/charmbracelet/wish"
7+ "github.com/picosh/pico/pkg/pssh"
8 "github.com/urfave/cli/v2"
9 )
10
11@@ -45,22 +44,22 @@ func getPatchsetFromOpt(patchsets []*Patchset, optPatchsetID string) (*Patchset,
12 return nil, fmt.Errorf("cannot find patchset: %s", optPatchsetID)
13 }
14
15-func printPatches(sesh ssh.Session, patches []*Patch) {
16+func printPatches(sesh *pssh.SSHServerConnSession, patches []*Patch) {
17 if len(patches) == 1 {
18- wish.Println(sesh, patches[0].RawText)
19+ sesh.Println(patches[0].RawText)
20 return
21 }
22
23 opatches := patches
24 for idx, patch := range opatches {
25- wish.Println(sesh, patch.RawText)
26+ sesh.Println(patch.RawText)
27 if idx < len(patches)-1 {
28- wish.Printf(sesh, "\n\n\n")
29+ sesh.Printf("\n\n\n")
30 }
31 }
32 }
33
34-func prSummary(be *Backend, pr GitPatchRequest, sesh ssh.Session, prID int64) error {
35+func prSummary(be *Backend, pr GitPatchRequest, sesh *pssh.SSHServerConnSession, prID int64) error {
36 request, err := pr.GetPatchRequestByID(prID)
37 if err != nil {
38 return err
39@@ -76,9 +75,9 @@ func prSummary(be *Backend, pr GitPatchRequest, sesh ssh.Session, prID int64) er
40 return err
41 }
42
43- wish.Printf(sesh, "Info\n====\n")
44- wish.Printf(sesh, "URL: https://%s/prs/%d\n", be.Cfg.Url, prID)
45- wish.Printf(sesh, "Repo: %s\n\n", be.CreateRepoNs(repoUser.Name, repo.Name))
46+ sesh.Printf("Info\n====\n")
47+ sesh.Printf("URL: https://%s/prs/%d\n", be.Cfg.Url, prID)
48+ sesh.Printf("Repo: %s\n\n", be.CreateRepoNs(repoUser.Name, repo.Name))
49
50 writer := NewTabWriter(sesh)
51 _, _ = fmt.Fprintln(writer, "ID\tName\tStatus\tDate")
52@@ -94,7 +93,7 @@ func prSummary(be *Backend, pr GitPatchRequest, sesh ssh.Session, prID int64) er
53 return err
54 }
55
56- wish.Printf(sesh, "\nPatchsets\n====\n")
57+ sesh.Printf("\nPatchsets\n====\n")
58
59 writerSet := NewTabWriter(sesh)
60 _, _ = fmt.Fprintln(writerSet, "ID\tType\tUser\tDate")
61@@ -130,7 +129,7 @@ func prSummary(be *Backend, pr GitPatchRequest, sesh ssh.Session, prID int64) er
62 return err
63 }
64
65- wish.Printf(sesh, "\nPatches from latest patchset\n====\n")
66+ sesh.Printf("\nPatches from latest patchset\n====\n")
67
68 opatches := patches
69 w := NewTabWriter(sesh)
70@@ -152,7 +151,7 @@ func prSummary(be *Backend, pr GitPatchRequest, sesh ssh.Session, prID int64) er
71 return nil
72 }
73
74-func printPatchsetFromID(sesh ssh.Session, pr GitPatchRequest, psID int64) error {
75+func printPatchsetFromID(sesh *pssh.SSHServerConnSession, pr GitPatchRequest, psID int64) error {
76 patches, err := pr.GetPatchesByPatchsetID(psID)
77 if err != nil {
78 return err
79@@ -161,7 +160,7 @@ func printPatchsetFromID(sesh ssh.Session, pr GitPatchRequest, psID int64) error
80 return nil
81 }
82
83-func printPatchsetFromPrID(sesh ssh.Session, pr GitPatchRequest, prID int64) error {
84+func printPatchsetFromPrID(sesh *pssh.SSHServerConnSession, pr GitPatchRequest, prID int64) error {
85 patchsets, err := pr.GetPatchsetsByPrID(prID)
86 if err != nil {
87 return err
88@@ -176,7 +175,7 @@ func printPatchsetFromPrID(sesh ssh.Session, pr GitPatchRequest, prID int64) err
89 return nil
90 }
91
92-func NewCli(sesh ssh.Session, be *Backend, pr GitPatchRequest) *cli.App {
93+func NewCli(sesh *pssh.SSHServerConnSession, be *Backend, pr GitPatchRequest) *cli.App {
94 desc := fmt.Sprintf(`git-pr (v%s): A pastebin supercharged for git collaboration.
95
96 Here's how it works:
97@@ -209,12 +208,12 @@ To get started, submit a new patch request:
98 ErrWriter: sesh,
99 ExitErrHandler: func(cCtx *cli.Context, err error) {
100 if err != nil {
101- wish.Fatalln(sesh, fmt.Errorf("err: %w", err))
102+ sesh.Fatal(fmt.Errorf("err: %w", err))
103 }
104 },
105 OnUsageError: func(cCtx *cli.Context, err error, isSubcommand bool) error {
106 if err != nil {
107- wish.Fatalln(sesh, fmt.Errorf("err: %w", err))
108+ sesh.Fatal(fmt.Errorf("err: %w", err))
109 }
110 return nil
111 },
112@@ -305,7 +304,7 @@ To get started, submit a new patch request:
113 if err != nil {
114 return err
115 }
116- wish.Printf(sesh, "User created successfully!\nUser: %s\nPubkey: %s\n", user.Name, pubkey)
117+ sesh.Printf("User created successfully!\nUser: %s\nPubkey: %s\n", user.Name, pubkey)
118 return nil
119 },
120 },
121@@ -350,7 +349,7 @@ To get started, submit a new patch request:
122 if err != nil {
123 return err
124 }
125- wish.Printf(sesh, "successfully removed patchset: %d\n", patchsetID)
126+ sesh.Printf("successfully removed patchset: %d\n", patchsetID)
127 return nil
128 },
129 },
130@@ -389,7 +388,7 @@ To get started, submit a new patch request:
131 }
132 }
133
134- wish.Printf(sesh, "repo created: %s/%s", user.Name, repo.Name)
135+ sesh.Printf("repo created: %s/%s", user.Name, repo.Name)
136 return nil
137 },
138 },
139@@ -591,8 +590,7 @@ To get started, submit a new patch request:
140 if err != nil {
141 return err
142 }
143- wish.Println(
144- sesh,
145+ sesh.Println(
146 "PR submitted! Use the ID for interacting with this PR.",
147 )
148
149@@ -641,7 +639,7 @@ To get started, submit a new patch request:
150 for _, prIDStr := range prIDs {
151 prID, err := strToInt(prIDStr)
152 if err != nil {
153- wish.Errorln(sesh, err)
154+ sesh.Errorln(err)
155 continue
156 }
157
158@@ -673,12 +671,12 @@ To get started, submit a new patch request:
159 if err != nil {
160 return err
161 }
162- wish.Printf(sesh, "Accepted PR %s (#%d)\n", prq.Name, prq.ID)
163+ sesh.Printf("Accepted PR %s (#%d)\n", prq.Name, prq.ID)
164 err = prSummary(be, pr, sesh, prID)
165 if err != nil {
166 errs = errors.Join(errs, err)
167 }
168- wish.Printf(sesh, "\n\n")
169+ sesh.Printf("\n\n")
170 }
171
172 return errs
173@@ -708,7 +706,7 @@ To get started, submit a new patch request:
174 for _, prIDStr := range prIDs {
175 prID, err := strToInt(prIDStr)
176 if err != nil {
177- wish.Errorln(sesh, err)
178+ sesh.Errorln(err)
179 continue
180 }
181
182@@ -745,12 +743,12 @@ To get started, submit a new patch request:
183 if err != nil {
184 return err
185 }
186- wish.Printf(sesh, "Closed PR %s (#%d)\n", prq.Name, prq.ID)
187+ sesh.Printf("Closed PR %s (#%d)\n", prq.Name, prq.ID)
188 err = prSummary(be, pr, sesh, prID)
189 if err != nil {
190 errs = errors.Join(errs, err)
191 }
192- wish.Printf(sesh, "\n\n")
193+ sesh.Printf("\n\n")
194 }
195 return errs
196 },
197@@ -808,7 +806,7 @@ To get started, submit a new patch request:
198
199 err = pr.UpdatePatchRequestStatus(prID, user.ID, StatusOpen, cCtx.String("comment"))
200 if err == nil {
201- wish.Printf(sesh, "Reopened PR %s (#%d)\n", prq.Name, prq.ID)
202+ sesh.Printf("Reopened PR %s (#%d)\n", prq.Name, prq.ID)
203 }
204 return prSummary(be, pr, sesh, prID)
205 },
206@@ -860,7 +858,7 @@ To get started, submit a new patch request:
207 title,
208 )
209 if err == nil {
210- wish.Printf(sesh, "New title: %s (%d)\n", title, prq.ID)
211+ sesh.Printf("New title: %s (%d)\n", title, prq.ID)
212 }
213
214 return err
215@@ -930,14 +928,14 @@ To get started, submit a new patch request:
216 op := OpNormal
217 nextStatus := StatusOpen
218 if isReview {
219- wish.Println(sesh, "Marking patchset as a review")
220+ sesh.Println("Marking patchset as a review")
221 op = OpReview
222 } else if isAccept {
223- wish.Println(sesh, "Marking PR as accepted")
224+ sesh.Println("Marking PR as accepted")
225 nextStatus = StatusAccepted
226 op = OpAccept
227 } else if isClose {
228- wish.Println(sesh, "Marking PR as closed")
229+ sesh.Println("Marking PR as closed")
230 nextStatus = StatusClosed
231 op = OpClose
232 }
233@@ -948,7 +946,7 @@ To get started, submit a new patch request:
234 }
235
236 if len(patches) == 0 {
237- wish.Println(sesh, "Patches submitted! However none were saved, probably because they already exist in the system")
238+ sesh.Println("Patches submitted! However none were saved, probably because they already exist in the system")
239 return nil
240 }
241
242@@ -959,7 +957,7 @@ To get started, submit a new patch request:
243 }
244 }
245
246- wish.Println(sesh, "Patches submitted!")
247+ sesh.Println("Patches submitted!")
248 return prSummary(be, pr, sesh, prID)
249 },
250 },
+19,
-16
1@@ -9,7 +9,6 @@ import (
2 "os"
3 "os/signal"
4 "syscall"
5- "time"
6
7 git "github.com/picosh/git-pr"
8 )
9@@ -26,15 +25,6 @@ func main() {
10 git.LoadConfigFile(*fpath, logger)
11 cfg := git.NewGitCfg(logger)
12
13- // SSH Server
14- ssh := git.GitSshServer(cfg)
15- cfg.Logger.Info("starting SSH server", "host", cfg.Host, "port", cfg.SshPort)
16- go func() {
17- if err := ssh.ListenAndServe(); err != nil {
18- cfg.Logger.Error("serve error", "err", err)
19- }
20- }()
21-
22 // Web Server
23 addr := fmt.Sprintf("%s:%s", cfg.Host, cfg.WebPort)
24 web := git.GitWebServer(cfg)
25@@ -45,13 +35,26 @@ func main() {
26 }
27 }()
28
29+ ctx, cancel := context.WithCancel(context.Background())
30+ defer cancel()
31+ // SSH Server
32+ ssh := git.GitSshServer(ctx, cfg)
33+
34 done := make(chan os.Signal, 1)
35 signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
36- <-done
37- cfg.Logger.Info("stopping SSH server")
38- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
39- defer func() { cancel() }()
40- if err := ssh.Shutdown(ctx); err != nil {
41- cfg.Logger.Error("shutdown", "err", err)
42+ logger.Info("starting SSH server", "addr", ssh.Config.ListenAddr)
43+ go func() {
44+ if err := ssh.ListenAndServe(); err != nil {
45+ logger.Error("serve", "err", err.Error())
46+ os.Exit(1)
47+ }
48+ }()
49+
50+ exit := func() {
51+ logger.Info("stopping ssh server")
52+ cancel()
53 }
54+
55+ <-done
56+ exit()
57 }
+4,
-1
1@@ -1,6 +1,7 @@
2 package main
3
4 import (
5+ "context"
6 "flag"
7 "fmt"
8 "log/slog"
9@@ -38,7 +39,9 @@ func main() {
10 git.LoadConfigFile(cfgPath, logger)
11 cfg := git.NewGitCfg(logger)
12
13- s := git.GitSshServer(cfg)
14+ ctx, cancel := context.WithCancel(context.Background())
15+ defer cancel()
16+ s := git.GitSshServer(ctx, cfg)
17 go func() {
18 _ = s.ListenAndServe()
19 }()
+6,
-6
1@@ -24,7 +24,9 @@ func testSingleTenantE2E(t *testing.T) {
2 _ = os.RemoveAll(dataDir)
3 }()
4 suite := setupTest(dataDir, cfgSingleTenantTmpl)
5- s := GitSshServer(suite.cfg)
6+ ctx, cancel := context.WithCancel(context.Background())
7+ defer cancel()
8+ s := GitSshServer(ctx, suite.cfg)
9 go func() {
10 _ = s.ListenAndServe()
11 }()
12@@ -48,8 +50,6 @@ func testSingleTenantE2E(t *testing.T) {
13 actual, err := suite.userKey.Cmd(nil, "pr ls")
14 bail(err)
15 snaps.MatchSnapshot(t, actual)
16-
17- _ = s.Shutdown(context.Background())
18 }
19
20 func testMultiTenantE2E(t *testing.T) {
21@@ -59,7 +59,9 @@ func testMultiTenantE2E(t *testing.T) {
22 _ = os.RemoveAll(dataDir)
23 }()
24 suite := setupTest(dataDir, cfgMultiTenantTmpl)
25- s := GitSshServer(suite.cfg)
26+ ctx, cancel := context.WithCancel(context.Background())
27+ defer cancel()
28+ s := GitSshServer(ctx, suite.cfg)
29 go func() {
30 _ = s.ListenAndServe()
31 }()
32@@ -135,8 +137,6 @@ func testMultiTenantE2E(t *testing.T) {
33 actual, err = suite.userKey.Cmd(nil, "logs --repo admin/ai")
34 bail(err)
35 snaps.MatchSnapshot(t, actual)
36-
37- _ = s.Shutdown(context.Background())
38 }
39
40 type TestSuite struct {
M
go.mod
+33,
-49
1@@ -1,87 +1,71 @@
2 module github.com/picosh/git-pr
3
4-go 1.22
5+go 1.25
6
7 require (
8- github.com/alecthomas/chroma/v2 v2.13.0
9+ github.com/alecthomas/chroma/v2 v2.23.1
10 github.com/bluekeyes/go-gitdiff v0.8.0
11 github.com/charmbracelet/ssh v0.0.0-20240301204039-e79ff702f5b3
12- github.com/charmbracelet/wish v1.3.2
13- github.com/gkampitakis/go-snaps v0.5.7
14- github.com/gorilla/feeds v1.1.2
15- github.com/jmoiron/sqlx v1.3.5
16+ github.com/gkampitakis/go-snaps v0.5.15
17+ github.com/gorilla/feeds v1.2.0
18+ github.com/jmoiron/sqlx v1.4.0
19 github.com/knadh/koanf/parsers/toml v0.1.0
20 github.com/knadh/koanf/providers/env v0.1.0
21 github.com/knadh/koanf/providers/file v1.0.0
22 github.com/knadh/koanf/v2 v2.1.1
23 github.com/oddg/hungarian-algorithm v0.0.0-20170809162819-9567cbc363de
24+ github.com/picosh/pico v1.13.2-0.20260225015203-1f6cc61d65a3
25 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
26 github.com/urfave/cli/v2 v2.27.2
27- golang.org/x/crypto v0.21.0
28- modernc.org/sqlite v1.27.0
29+ golang.org/x/crypto v0.47.0
30+ modernc.org/sqlite v1.44.3
31 )
32
33 require (
34 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
35- github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
36- github.com/charmbracelet/bubbletea v0.25.0 // indirect
37- github.com/charmbracelet/keygen v0.5.0 // indirect
38- github.com/charmbracelet/lipgloss v0.10.0 // indirect
39- github.com/charmbracelet/log v0.3.1 // indirect
40+ github.com/antoniomika/syncmap v1.0.0 // indirect
41+ github.com/beorn7/perks v1.0.1 // indirect
42+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
43 github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 // indirect
44 github.com/charmbracelet/x/exp/term v0.0.0-20240229115032-4b79243a3516 // indirect
45- github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
46- github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
47+ github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
48 github.com/creack/pty v1.1.21 // indirect
49- github.com/dlclark/regexp2 v1.11.0 // indirect
50+ github.com/dlclark/regexp2 v1.11.5 // indirect
51 github.com/dustin/go-humanize v1.0.1 // indirect
52 github.com/fsnotify/fsnotify v1.7.0 // indirect
53- github.com/gkampitakis/ciinfo v0.3.0 // indirect
54+ github.com/gkampitakis/ciinfo v0.3.2 // indirect
55 github.com/gkampitakis/go-diff v1.3.2 // indirect
56- github.com/go-logfmt/logfmt v0.6.0 // indirect
57+ github.com/go-andiamo/splitter v1.2.5 // indirect
58 github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
59- github.com/google/uuid v1.4.0 // indirect
60- github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
61+ github.com/goccy/go-yaml v1.18.0 // indirect
62+ github.com/google/uuid v1.6.0 // indirect
63 github.com/knadh/koanf/maps v0.1.1 // indirect
64 github.com/kr/pretty v0.3.1 // indirect
65 github.com/kr/text v0.2.0 // indirect
66- github.com/lib/pq v1.10.9 // indirect
67- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
68 github.com/maruel/natural v1.1.1 // indirect
69 github.com/mattn/go-isatty v0.0.20 // indirect
70- github.com/mattn/go-localereader v0.0.1 // indirect
71- github.com/mattn/go-runewidth v0.0.15 // indirect
72 github.com/mitchellh/copystructure v1.2.0 // indirect
73 github.com/mitchellh/reflectwalk v1.0.2 // indirect
74- github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
75- github.com/muesli/cancelreader v0.2.2 // indirect
76- github.com/muesli/reflow v0.3.0 // indirect
77- github.com/muesli/termenv v0.15.2 // indirect
78+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
79+ github.com/ncruces/go-strftime v1.0.0 // indirect
80 github.com/pelletier/go-toml v1.9.5 // indirect
81- github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
82+ github.com/prometheus/client_golang v1.23.2 // indirect
83+ github.com/prometheus/client_model v0.6.2 // indirect
84+ github.com/prometheus/common v0.67.5 // indirect
85+ github.com/prometheus/procfs v0.19.2 // indirect
86 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
87- github.com/rivo/uniseg v0.4.7 // indirect
88- github.com/rogpeppe/go-internal v1.12.0 // indirect
89+ github.com/rogpeppe/go-internal v1.14.1 // indirect
90 github.com/russross/blackfriday/v2 v2.1.0 // indirect
91- github.com/tidwall/gjson v1.17.0 // indirect
92- github.com/tidwall/match v1.1.1 // indirect
93+ github.com/tidwall/gjson v1.18.0 // indirect
94+ github.com/tidwall/match v1.2.0 // indirect
95 github.com/tidwall/pretty v1.2.1 // indirect
96 github.com/tidwall/sjson v1.2.5 // indirect
97 github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
98- golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
99- golang.org/x/mod v0.14.0 // indirect
100- golang.org/x/sync v0.6.0 // indirect
101- golang.org/x/sys v0.21.0 // indirect
102- golang.org/x/term v0.18.0 // indirect
103- golang.org/x/text v0.14.0 // indirect
104- golang.org/x/tools v0.15.0 // indirect
105- lukechampine.com/uint128 v1.2.0 // indirect
106- modernc.org/cc/v3 v3.40.0 // indirect
107- modernc.org/ccgo/v3 v3.16.13 // indirect
108- modernc.org/libc v1.29.0 // indirect
109- modernc.org/mathutil v1.6.0 // indirect
110- modernc.org/memory v1.7.2 // indirect
111- modernc.org/opt v0.1.3 // indirect
112- modernc.org/strutil v1.1.3 // indirect
113- modernc.org/token v1.0.1 // indirect
114+ go.yaml.in/yaml/v2 v2.4.3 // indirect
115+ golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
116+ golang.org/x/sys v0.40.0 // indirect
117+ google.golang.org/protobuf v1.36.11 // indirect
118+ modernc.org/libc v1.67.7 // indirect
119+ modernc.org/mathutil v1.7.1 // indirect
120+ modernc.org/memory v1.11.0 // indirect
121 )
M
go.sum
+185,
-119
1@@ -1,73 +1,104 @@
2-github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
3-github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
4-github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
5-github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
6-github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
7-github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
8+codeberg.org/emersion/go-scfg v0.1.0 h1:6dnGU0ZI4gX+O5rMjwhoaySItzHG710eXL5TIQKl+uM=
9+codeberg.org/emersion/go-scfg v0.1.0/go.mod h1:0nooW1ufBB4SlJEdTtiVN9Or+bnNM1icOkQ6Tbrq6O0=
10+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
11+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
12+git.sr.ht/~delthas/senpai v0.4.1 h1:5CABpwJVzIBQaXtOngxG9TQDcxlaUqmGsXxlW+XcLuM=
13+git.sr.ht/~delthas/senpai v0.4.1/go.mod h1:mzdu4o3wANA6cYzRnrz3w+uGPHA2z3j02JDrr/M3Myc=
14+git.sr.ht/~rockorager/vaxis v0.15.1-0.20251218121515-cdf898cf10c7 h1:Hik9uYRclWV+mt5PnGc41aeGX6sh8MGzWcBnJt9EzpY=
15+git.sr.ht/~rockorager/vaxis v0.15.1-0.20251218121515-cdf898cf10c7/go.mod h1:ptCAzb19xAhqhfAmCsN9Xnxdx01vlXkwZcuMYPnyqzE=
16+github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
17+github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
18+github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
19+github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
20+github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
21+github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
22 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
23 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
24-github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
25-github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
26+github.com/antoniomika/syncmap v1.0.0 h1:iFSfbQFQOvHZILFZF+hqWosO0no+W9+uF4y2VEyMKWU=
27+github.com/antoniomika/syncmap v1.0.0/go.mod h1:fK2829foEYnO4riNfyUn0SHQZt4ue3DStYjGU+sJj38=
28+github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
29+github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
30+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
31+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
32+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
33+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
34 github.com/bluekeyes/go-gitdiff v0.8.0 h1:Nn1wfw3/XeKoc3lWk+2bEXGUHIx36kj80FM1gVcBk+o=
35 github.com/bluekeyes/go-gitdiff v0.8.0/go.mod h1:WWAk1Mc6EgWarCrPFO+xeYlujPu98VuLW3Tu+B/85AE=
36-github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
37-github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
38-github.com/charmbracelet/keygen v0.5.0 h1:XY0fsoYiCSM9axkrU+2ziE6u6YjJulo/b9Dghnw6MZc=
39-github.com/charmbracelet/keygen v0.5.0/go.mod h1:DfvCgLHxZ9rJxdK0DGw3C/LkV4SgdGbnliHcObV3L+8=
40-github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
41-github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
42-github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw=
43-github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
44+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
45+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
46 github.com/charmbracelet/ssh v0.0.0-20240301204039-e79ff702f5b3 h1:BI6Vno579jK/NKUwrvboHtMfF2On6kh6mU1cguf5+vQ=
47 github.com/charmbracelet/ssh v0.0.0-20240301204039-e79ff702f5b3/go.mod h1:wUZ0VTrLI5ixIbYOSRHrqrZnfj8EXgLZOOvQYAQ2f18=
48-github.com/charmbracelet/wish v1.3.2 h1:9+32OZnfebIw59Mcx0Yhsj6uke727bJVGJb6WolxsxQ=
49-github.com/charmbracelet/wish v1.3.2/go.mod h1:aulqcv2nEoW14yC3tlkrmIbVN7qDjeH+pzIO239VGTA=
50 github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 h1:3RXpZWGWTOeVXCTv0Dnzxdv/MhNUkBfEcbaTY0zrTQI=
51 github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
52 github.com/charmbracelet/x/exp/term v0.0.0-20240229115032-4b79243a3516 h1:wL/PiybPudKHv/LDgAUqS9eoPQr3pOAmzShMPG99cXA=
53 github.com/charmbracelet/x/exp/term v0.0.0-20240229115032-4b79243a3516/go.mod h1:ntNL6rRIDmBHKUmo6ZKt344wCTcrPsSrfVj72qT8A5U=
54-github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
55-github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
56-github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
57-github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
58+github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
59+github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
60+github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
61+github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
62+github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
63+github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
64+github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
65+github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
66 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
67 github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
68 github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
69 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
70-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
71 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
72-github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
73-github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
74+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
75+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
76+github.com/delthas/go-libnp v0.2.0 h1:OVll7Z9CER0rYpgiglXN1QvuNtkyY5X9uzLqm+xDstA=
77+github.com/delthas/go-libnp v0.2.0/go.mod h1:LHUapIgbv1vBcZJU4cJRPkyslKhKe7GNRF38yUbl9QA=
78+github.com/delthas/go-localeinfo v0.2.0 h1:0K5F5GQ5p4ealZjuBs4eubHLOIL21/YWSxs557hHZ+M=
79+github.com/delthas/go-localeinfo v0.2.0/go.mod h1:sG54BxlyQgIskYURLrg7mvhoGBe0Qq12DNtYRALwNa4=
80+github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
81+github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
82+github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
83+github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
84 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
85 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
86+github.com/forPelevin/gomoji v1.4.1 h1:7U+Bl8o6RV/dOQz7coQFWj/jX6Ram6/cWFOuFDEPEUo=
87+github.com/forPelevin/gomoji v1.4.1/go.mod h1:mM6GtmCgpoQP2usDArc6GjbXrti5+FffolyQfGgPboQ=
88 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
89 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
90-github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8=
91-github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
92+github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
93+github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
94 github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
95 github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
96-github.com/gkampitakis/go-snaps v0.5.7 h1:uVGjHR4t4pPHU944udMx7VKHpwepZXmvDMF+yDmI0rg=
97-github.com/gkampitakis/go-snaps v0.5.7/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
98-github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
99-github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
100-github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
101-github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
102+github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
103+github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
104+github.com/go-andiamo/splitter v1.2.5 h1:P3NovWMY2V14TJJSolXBvlOmGSZo3Uz+LtTl2bsV/eY=
105+github.com/go-andiamo/splitter v1.2.5/go.mod h1:8WHU24t9hcMKU5FXDQb1hysSEC/GPuivIp0uKY1J8gw=
106+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
107+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
108+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
109+github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
110+github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
111 github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c=
112 github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
113-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
114-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
115-github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
116-github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
117-github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
118-github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
119-github.com/gorilla/feeds v1.1.2 h1:pxzZ5PD3RJdhFH2FsJJ4x6PqMqbgFk1+Vez4XWBW8Iw=
120-github.com/gorilla/feeds v1.1.2/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
121+github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
122+github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
123+github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
124+github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
125+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
126+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
127+github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno=
128+github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
129+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
130+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
131+github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
132+github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
133+github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
134+github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
135+github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
136+github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
137+github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
138 github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
139 github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
140-github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
141-github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
142-github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
143-github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
144+github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
145+github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
146+github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
147+github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
148 github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
149 github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
150 github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=
151@@ -85,65 +116,74 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
152 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
153 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
154 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
155-github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
156-github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
157+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
158+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
159 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
160-github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
161-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
162+github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI=
163+github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
164 github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
165 github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
166 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
167 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
168-github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
169-github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
170-github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
171-github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
172-github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
173-github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
174-github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
175-github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
176+github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
177+github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
178+github.com/mattn/go-sixel v0.0.8 h1:H0bBGQVOJoSvzvtTgCInxvg1IZiNlTcIIIx8A6uvjpQ=
179+github.com/mattn/go-sixel v0.0.8/go.mod h1:wbDSbrwpykVI1qEHyjZYsDgaJTwpVg9wSwmmh2slnBw=
180+github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
181+github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
182+github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
183+github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
184 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
185 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
186 github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
187 github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
188-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
189-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
190-github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
191-github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
192-github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
193-github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
194-github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
195-github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
196+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
197+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
198+github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
199+github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
200 github.com/oddg/hungarian-algorithm v0.0.0-20170809162819-9567cbc363de h1:kuqx+ZOU3HjRVyMuT43K4xzTCqq+Ag1TCf8wtbYmqrw=
201 github.com/oddg/hungarian-algorithm v0.0.0-20170809162819-9567cbc363de/go.mod h1:dv3Q0yoeN8DwXGhZiv8Vi6/rr9mPtf4ylV60eLTGjUo=
202 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
203 github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
204+github.com/picosh/pico v1.13.2-0.20260225015203-1f6cc61d65a3 h1:L315467SH4O1IlTmd/pjjvsD7IX9vnsY4pQw9WKnf1M=
205+github.com/picosh/pico v1.13.2-0.20260225015203-1f6cc61d65a3/go.mod h1:1Xml9JbPlipITzar/eu8/kcrfgXqXvx/XxL/xBj0xE8=
206+github.com/picosh/utils v0.0.0-20260125160622-5c3a9e231ec6 h1:9KfCtfcx7vrSyGU1K9whdE1crll9Aq+nAZ6c0FzuzvE=
207+github.com/picosh/utils v0.0.0-20260125160622-5c3a9e231ec6/go.mod h1:HogYEyJ43IGXrOa3D/kjM1pkzNAyh+pejRyv8Eo//pk=
208 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
209 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
210 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
211 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
212+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
213+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
214+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
215+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
216+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
217+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
218+github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
219+github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
220 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
221 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
222-github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
223-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
224 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
225 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
226 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
227-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
228-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
229+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
230+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
231 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
232 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
233 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
234 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
235+github.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y=
236+github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds=
237 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
238 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
239-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
240-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
241+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
242+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
243 github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
244-github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
245-github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
246-github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
247+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
248+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
249 github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
250+github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
251+github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
252 github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
253 github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
254 github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
255@@ -153,55 +193,81 @@ github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
256 github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
257 github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
258 github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
259-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
260-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
261-golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
262-golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
263-golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
264-golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
265-golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
266-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
267-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
268+github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
269+github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
270+github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
271+github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
272+github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
273+github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
274+go.abhg.dev/goldmark/anchor v0.2.0 h1:RQZTodRc6VHSUoQYKFlyH0pokbhk1klwUuGgDmjGp2E=
275+go.abhg.dev/goldmark/anchor v0.2.0/go.mod h1:Ym74zBV+QBKxK9ITOty680N9FT8otgGYvtYXroJUWms=
276+go.abhg.dev/goldmark/hashtag v0.4.0 h1:jkYcsIRUpog/rhWMnIOjj14qj8p5p4um+IzPbPUxFww=
277+go.abhg.dev/goldmark/hashtag v0.4.0/go.mod h1:5DQwSo2dEHKtsfXOC1+Po7WgVwR5IoGjgeVIerddz9g=
278+go.abhg.dev/goldmark/toc v0.12.0 h1:kiEBBIOB7jEzNpXmGdiL2L/zGSELKw/p3mosm2+RSuo=
279+go.abhg.dev/goldmark/toc v0.12.0/go.mod h1:kskbM5l9y8wOFEFfyEe9wnwhWeykvmHB6xEPCVrZIvg=
280+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
281+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
282+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
283+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
284+golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
285+golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
286+golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
287+golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
288+golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
289+golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
290+golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
291+golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
292+golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
293+golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
294+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
295+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
296 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
297-golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
298-golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
299-golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
300-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
301-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
302-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
303-golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
304-golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
305+golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
306+golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
307+golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
308+golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
309+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
310+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
311+golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
312+golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
313+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
314+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
315 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
316 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
317+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
318+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
319 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
320+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
321 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
322 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
323 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
324-lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
325-lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
326-modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
327-modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
328-modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
329-modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
330-modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
331-modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
332-modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
333-modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
334-modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs=
335-modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ=
336-modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
337-modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
338-modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
339-modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
340-modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
341-modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
342-modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8=
343-modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
344-modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
345-modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
346-modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
347-modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
348-modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
349-modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
350-modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
351-modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
352+modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
353+modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
354+modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
355+modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
356+modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
357+modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
358+modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
359+modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
360+modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
361+modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
362+modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
363+modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
364+modernc.org/libc v1.67.7 h1:H+gYQw2PyidyxwxQsGTwQw6+6H+xUk+plvOKW7+d3TI=
365+modernc.org/libc v1.67.7/go.mod h1:UjCSJFl2sYbJbReVQeVpq/MgzlbmDM4cRHIYFelnaDk=
366+modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
367+modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
368+modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
369+modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
370+modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
371+modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
372+modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
373+modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
374+modernc.org/sqlite v1.44.3 h1:+39JvV/HWMcYslAwRxHb8067w+2zowvFOUrOWIy9PjY=
375+modernc.org/sqlite v1.44.3/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
376+modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
377+modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
378+modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
379+modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
380+mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
381+mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=
M
mdw.go
+10,
-8
1@@ -3,23 +3,25 @@ package git
2 import (
3 "fmt"
4
5- "github.com/charmbracelet/ssh"
6- "github.com/charmbracelet/wish"
7+ "github.com/picosh/pico/pkg/pssh"
8 )
9
10-func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware {
11- return func(next ssh.Handler) ssh.Handler {
12- return func(sesh ssh.Session) {
13+func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) pssh.SSHServerMiddleware {
14+ return func(next pssh.SSHServerHandler) pssh.SSHServerHandler {
15+ return func(sesh *pssh.SSHServerConnSession) error {
16 args := sesh.Command()
17 cli := NewCli(sesh, be, pr)
18 margs := append([]string{"git"}, args...)
19+ be.Logger.Info("ssh args", "args", args)
20 err := cli.Run(margs)
21 if err != nil {
22 be.Logger.Error("error when running cli", "err", err)
23- wish.Fatalln(sesh, fmt.Errorf("err: %w", err))
24- next(sesh)
25- return
26+ sesh.Fatal(fmt.Errorf("err: %w", err))
27+ _ = next(sesh)
28+ return err
29 }
30+
31+ return nil
32 }
33 }
34 }
M
ssh.go
+34,
-21
1@@ -1,17 +1,24 @@
2 package git
3
4 import (
5+ "context"
6 "fmt"
7+ "os"
8 "path/filepath"
9
10- "github.com/charmbracelet/ssh"
11- "github.com/charmbracelet/wish"
12+ "github.com/picosh/pico/pkg/pssh"
13+ "golang.org/x/crypto/ssh"
14 )
15
16-func authHandler(pr *PrCmd) func(ctx ssh.Context, key ssh.PublicKey) bool {
17- return func(ctx ssh.Context, key ssh.PublicKey) bool {
18+func authHandler(pr *PrCmd) func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
19+ return func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
20 pubkey := pr.Backend.Pubkey(key)
21- userName := ctx.User()
22+ userName := conn.User()
23+ perms := &ssh.Permissions{
24+ Extensions: map[string]string{
25+ "pubkey": pubkey,
26+ },
27+ }
28 err := pr.IsBanned(pubkey, userName)
29 if err != nil {
30 pr.Backend.Logger.Info(
31@@ -20,13 +27,13 @@ func authHandler(pr *PrCmd) func(ctx ssh.Context, key ssh.PublicKey) bool {
32 "username", userName,
33 "pubkey", pubkey,
34 )
35- return false
36+ return perms, err
37 }
38- return true
39+ return perms, nil
40 }
41 }
42
43-func GitSshServer(cfg *GitCfg) *ssh.Server {
44+func GitSshServer(ctx context.Context, cfg *GitCfg) *pssh.SSHServer {
45 dbpath := filepath.Join(cfg.DataDir, "pr.db?_fk=on")
46 dbh, err := SqliteOpen("file:"+dbpath, cfg.Logger)
47 if err != nil {
48@@ -38,25 +45,31 @@ func GitSshServer(cfg *GitCfg) *ssh.Server {
49 Logger: cfg.Logger,
50 Cfg: cfg,
51 }
52+
53 prCmd := &PrCmd{
54 Backend: be,
55 }
56
57- s, err := wish.NewServer(
58- wish.WithAddress(
59- fmt.Sprintf("%s:%s", cfg.Host, cfg.SshPort),
60- ),
61- wish.WithHostKeyPath(
62- filepath.Join(cfg.DataDir, "term_info_ed25519"),
63- ),
64- wish.WithPublicKeyAuth(authHandler(prCmd)),
65- wish.WithMiddleware(
66+ server, err := pssh.NewSSHServerWithConfig(
67+ ctx,
68+ cfg.Logger,
69+ "git-pr",
70+ cfg.Host,
71+ cfg.SshPort,
72+ cfg.PromPort,
73+ filepath.Join(cfg.DataDir, "term_info_ed25519"),
74+ authHandler(prCmd),
75+ []pssh.SSHServerMiddleware{
76 GitPatchRequestMiddleware(be, prCmd),
77- ),
78+ },
79+ []pssh.SSHServerMiddleware{},
80+ nil,
81 )
82+
83 if err != nil {
84- cfg.Logger.Error("could not create server", "err", err)
85- return nil
86+ cfg.Logger.Error("failed to create ssh server", "err", err)
87+ os.Exit(1)
88 }
89- return s
90+
91+ return server
92 }