@tanagram/cli 0.4.19 → 0.4.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/config.go +21 -10
- package/commands/config_test.go +10 -9
- package/commands/list.go +14 -1
- package/commands/login.go +57 -1
- package/commands/login_test.go +132 -0
- package/commands/run.go +194 -156
- package/commands/snapshot.go +11 -1
- package/commands/snapshot_test.go +2 -1
- package/commands/sync.go +65 -29
- package/commands/sync_policies.go +14 -1
- package/dist/npm/darwin-arm64/tanagram +0 -0
- package/dist/npm/darwin-x64/tanagram +0 -0
- package/dist/npm/linux-arm64/tanagram +0 -0
- package/dist/npm/linux-x64/tanagram +0 -0
- package/dist/npm/tanagram_0.4.20_darwin_amd64.tar.gz +0 -0
- package/dist/npm/tanagram_0.4.20_darwin_arm64.tar.gz +0 -0
- package/dist/npm/tanagram_0.4.20_linux_amd64.tar.gz +0 -0
- package/dist/npm/tanagram_0.4.20_linux_arm64.tar.gz +0 -0
- package/dist/npm/tanagram_0.4.20_windows_amd64.zip +0 -0
- package/dist/npm/win32-x64/tanagram.exe +0 -0
- package/go.mod +6 -4
- package/go.sum +16 -2
- package/main.go +94 -27
- package/package.json +1 -1
- package/utils/sentry.go +44 -0
- package/dist/npm/tanagram_0.4.19_darwin_amd64.tar.gz +0 -0
- package/dist/npm/tanagram_0.4.19_darwin_arm64.tar.gz +0 -0
- package/dist/npm/tanagram_0.4.19_linux_amd64.tar.gz +0 -0
- package/dist/npm/tanagram_0.4.19_linux_arm64.tar.gz +0 -0
- package/dist/npm/tanagram_0.4.19_windows_amd64.zip +0 -0
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
package commands
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"context"
|
|
4
5
|
"fmt"
|
|
5
6
|
|
|
7
|
+
"github.com/getsentry/sentry-go"
|
|
6
8
|
"github.com/tanagram/cli/api"
|
|
7
9
|
"github.com/tanagram/cli/storage"
|
|
8
10
|
)
|
|
9
11
|
|
|
10
12
|
// SyncPolicies fetches policies from Tanagram API and saves them locally
|
|
11
|
-
func SyncPolicies() error {
|
|
13
|
+
func SyncPolicies(ctx context.Context) error {
|
|
14
|
+
span := sentry.StartSpan(ctx, "command.sync_policies")
|
|
15
|
+
defer span.Finish()
|
|
16
|
+
|
|
12
17
|
// Find git root
|
|
18
|
+
findGitRootSpan := sentry.StartSpan(span.Context(), "storage.find_git_root")
|
|
13
19
|
gitRoot, err := storage.FindGitRoot()
|
|
20
|
+
findGitRootSpan.Finish()
|
|
14
21
|
if err != nil {
|
|
15
22
|
return fmt.Errorf("not in a git repository: %w", err)
|
|
16
23
|
}
|
|
@@ -18,14 +25,18 @@ func SyncPolicies() error {
|
|
|
18
25
|
fmt.Println("Syncing policies from Tanagram...")
|
|
19
26
|
|
|
20
27
|
// Create API client
|
|
28
|
+
createClientSpan := sentry.StartSpan(span.Context(), "api.new_client")
|
|
21
29
|
client, err := api.NewAPIClient()
|
|
30
|
+
createClientSpan.Finish()
|
|
22
31
|
if err != nil {
|
|
23
32
|
return err
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
// Fetch policies from API
|
|
27
36
|
fmt.Println("Fetching policies from API...")
|
|
37
|
+
fetchPoliciesSpan := sentry.StartSpan(span.Context(), "api.get_policies")
|
|
28
38
|
response, err := client.GetPolicies()
|
|
39
|
+
fetchPoliciesSpan.Finish()
|
|
29
40
|
if err != nil {
|
|
30
41
|
return err
|
|
31
42
|
}
|
|
@@ -49,6 +60,7 @@ func SyncPolicies() error {
|
|
|
49
60
|
fmt.Printf("Policies apply to %d repositories\n", len(repoMap))
|
|
50
61
|
|
|
51
62
|
// Save policies for each repository
|
|
63
|
+
savePoliciesSpan := sentry.StartSpan(span.Context(), "storage.save_policies")
|
|
52
64
|
cloudStorage := storage.NewCloudPolicyStorage(gitRoot)
|
|
53
65
|
savedCount := 0
|
|
54
66
|
|
|
@@ -66,6 +78,7 @@ func SyncPolicies() error {
|
|
|
66
78
|
savedCount++
|
|
67
79
|
fmt.Printf(" ✓ Saved %d policies for %s\n", len(policies), repoKey)
|
|
68
80
|
}
|
|
81
|
+
savePoliciesSpan.Finish()
|
|
69
82
|
|
|
70
83
|
// Save metadata
|
|
71
84
|
orgID := ""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/go.mod
CHANGED
|
@@ -6,8 +6,14 @@ require (
|
|
|
6
6
|
github.com/anthropics/anthropic-sdk-go v1.17.0
|
|
7
7
|
github.com/charmbracelet/bubbletea v1.3.10
|
|
8
8
|
github.com/charmbracelet/lipgloss v1.1.0
|
|
9
|
+
github.com/getsentry/sentry-go v0.40.0
|
|
10
|
+
github.com/getsentry/sentry-go/slog v0.40.0
|
|
11
|
+
github.com/golang-jwt/jwt/v5 v5.3.0
|
|
12
|
+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
|
9
13
|
github.com/posthog/posthog-go v1.6.12
|
|
10
14
|
github.com/shirou/gopsutil/v3 v3.24.5
|
|
15
|
+
github.com/zalando/go-keyring v0.2.6
|
|
16
|
+
golang.org/x/term v0.37.0
|
|
11
17
|
)
|
|
12
18
|
|
|
13
19
|
require (
|
|
@@ -19,7 +25,6 @@ require (
|
|
|
19
25
|
github.com/charmbracelet/x/term v0.2.1 // indirect
|
|
20
26
|
github.com/danieljoos/wincred v1.2.2 // indirect
|
|
21
27
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
|
22
|
-
github.com/getsentry/sentry-go v0.40.0 // indirect
|
|
23
28
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
|
24
29
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
|
25
30
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
|
@@ -31,7 +36,6 @@ require (
|
|
|
31
36
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
|
32
37
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
|
33
38
|
github.com/muesli/termenv v0.16.0 // indirect
|
|
34
|
-
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
|
35
39
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
|
36
40
|
github.com/rivo/uniseg v0.4.7 // indirect
|
|
37
41
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
|
@@ -43,8 +47,6 @@ require (
|
|
|
43
47
|
github.com/tklauser/numcpus v0.6.1 // indirect
|
|
44
48
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
|
45
49
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
|
46
|
-
github.com/zalando/go-keyring v0.2.6 // indirect
|
|
47
50
|
golang.org/x/sys v0.38.0 // indirect
|
|
48
|
-
golang.org/x/term v0.37.0 // indirect
|
|
49
51
|
golang.org/x/text v0.27.0 // indirect
|
|
50
52
|
)
|
package/go.sum
CHANGED
|
@@ -24,13 +24,21 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6
|
|
|
24
24
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
|
25
25
|
github.com/getsentry/sentry-go v0.40.0 h1:VTJMN9zbTvqDqPwheRVLcp0qcUcM+8eFivvGocAaSbo=
|
|
26
26
|
github.com/getsentry/sentry-go v0.40.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=
|
|
27
|
+
github.com/getsentry/sentry-go/slog v0.40.0 h1:uR2EPL9w6uHw3XB983IAqzqM9mP+fjJpNY9kfob3/Z8=
|
|
28
|
+
github.com/getsentry/sentry-go/slog v0.40.0/go.mod h1:ArRaP+0rsbnJGyvZwYDo/vDQT/YBbOQeOlO+DGW+F9s=
|
|
29
|
+
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
|
30
|
+
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
|
27
31
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
|
28
32
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
|
29
33
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
|
30
34
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
|
35
|
+
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
|
36
|
+
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
|
31
37
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
32
38
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
33
39
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
40
|
+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
|
41
|
+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
|
34
42
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
|
35
43
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
|
36
44
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
|
@@ -49,8 +57,12 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
|
|
|
49
57
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
|
50
58
|
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
|
51
59
|
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
|
60
|
+
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
|
61
|
+
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
|
52
62
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
|
53
63
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
|
64
|
+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
65
|
+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
54
66
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
55
67
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
56
68
|
github.com/posthog/posthog-go v1.6.12 h1:rsOBL/YdMfLJtOVjKJLgdzYmvaL3aIW6IVbAteSe+aI=
|
|
@@ -66,6 +78,8 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt
|
|
|
66
78
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
|
67
79
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
|
68
80
|
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
|
81
|
+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
|
82
|
+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
69
83
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
|
70
84
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
71
85
|
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
@@ -88,6 +102,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
|
|
|
88
102
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
|
89
103
|
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
|
|
90
104
|
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
|
|
105
|
+
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
|
106
|
+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
|
91
107
|
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
|
92
108
|
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
|
93
109
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
@@ -97,8 +113,6 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
|
97
113
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
98
114
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
99
115
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
100
|
-
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
|
101
|
-
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|
102
116
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
|
103
117
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|
104
118
|
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
package/main.go
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package main
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"context"
|
|
4
5
|
"encoding/json"
|
|
5
6
|
"flag"
|
|
6
7
|
"fmt"
|
|
@@ -12,6 +13,7 @@ import (
|
|
|
12
13
|
"time"
|
|
13
14
|
|
|
14
15
|
"github.com/getsentry/sentry-go"
|
|
16
|
+
sentryslog "github.com/getsentry/sentry-go/slog"
|
|
15
17
|
"github.com/tanagram/cli/commands"
|
|
16
18
|
"github.com/tanagram/cli/metrics"
|
|
17
19
|
"github.com/tanagram/cli/tui"
|
|
@@ -33,15 +35,20 @@ func main() {
|
|
|
33
35
|
metrics.Init()
|
|
34
36
|
|
|
35
37
|
if err := sentry.Init(sentry.ClientOptions{
|
|
36
|
-
Dsn:
|
|
37
|
-
Release:
|
|
38
|
-
Environment:
|
|
39
|
-
EnableTracing:
|
|
38
|
+
Dsn: "https://a967718dd129e143907fe01b4e80cad2@o4509017064472576.ingest.us.sentry.io/4510649104007168",
|
|
39
|
+
Release: "tanagram-cli@" + Version,
|
|
40
|
+
Environment: getEnvironment(),
|
|
41
|
+
EnableTracing: true,
|
|
42
|
+
// TOOD: Configure sample rate based on environment (1.0 for dev; 0.1 for prod)
|
|
43
|
+
// Depends on TAN-1968
|
|
40
44
|
TracesSampleRate: 0.1,
|
|
45
|
+
EnableLogs: true,
|
|
41
46
|
}); err != nil {
|
|
42
47
|
slog.Warn("Sentry initialization failed", "error", err)
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
ctx := sentry.SetHubOnContext(context.Background(), sentry.CurrentHub())
|
|
51
|
+
|
|
45
52
|
exitCode := 0
|
|
46
53
|
defer func() {
|
|
47
54
|
if r := recover(); r != nil {
|
|
@@ -70,6 +77,12 @@ func main() {
|
|
|
70
77
|
"subcommand": subcommand,
|
|
71
78
|
})
|
|
72
79
|
|
|
80
|
+
tx := sentry.StartTransaction(ctx, fmt.Sprintf("cli.%s", subcommand),
|
|
81
|
+
sentry.WithOpName("command"),
|
|
82
|
+
)
|
|
83
|
+
defer tx.Finish()
|
|
84
|
+
ctx = tx.Context()
|
|
85
|
+
|
|
73
86
|
var logOutput io.Writer = os.Stderr
|
|
74
87
|
if utils.GetParentProcess() == "claude" {
|
|
75
88
|
// We use "exit-code 2" behavior for claude: https://code.claude.com/docs/en/hooks#simple:-exit-code
|
|
@@ -94,11 +107,16 @@ func main() {
|
|
|
94
107
|
logOutput = logFile
|
|
95
108
|
}
|
|
96
109
|
isTTY := term.IsTerminal(int(os.Stdout.Fd()))
|
|
97
|
-
logger := newLogger(*flagLogLevel, *flagLogFormat, logOutput, isTTY)
|
|
110
|
+
logger := newLogger(ctx, *flagLogLevel, *flagLogFormat, logOutput, isTTY)
|
|
98
111
|
slog.SetDefault(logger)
|
|
99
112
|
|
|
113
|
+
utils.SetGlobalTags(map[string]string{
|
|
114
|
+
"parent_process": utils.GetParentProcess(),
|
|
115
|
+
"is_tty": fmt.Sprintf("%t", isTTY),
|
|
116
|
+
})
|
|
117
|
+
|
|
100
118
|
slog.Info("Running CLI with args",
|
|
101
|
-
"args", os.Args[1:],
|
|
119
|
+
"args", strings.Join(os.Args[1:], " "),
|
|
102
120
|
)
|
|
103
121
|
|
|
104
122
|
// THIS IS A HUGE HACK
|
|
@@ -110,7 +128,7 @@ func main() {
|
|
|
110
128
|
// TODO: handle 0 or multiple workspace_roots
|
|
111
129
|
if utils.GetParentProcess() == "cursor" {
|
|
112
130
|
input, err := io.ReadAll(os.Stdin)
|
|
113
|
-
if err == nil {
|
|
131
|
+
if err == nil && len(input) > 0 {
|
|
114
132
|
var payload struct {
|
|
115
133
|
WorkspaceRoots []string `json:"workspace_roots"`
|
|
116
134
|
}
|
|
@@ -131,39 +149,39 @@ func main() {
|
|
|
131
149
|
"command": "run",
|
|
132
150
|
})
|
|
133
151
|
// Auto-setup hooks on first run
|
|
134
|
-
if err := commands.EnsureHooksConfigured(); err != nil {
|
|
152
|
+
if err := commands.EnsureHooksConfigured(ctx); err != nil {
|
|
135
153
|
slog.Error("Failed to configure hooks", "error", err)
|
|
136
154
|
exitCode = 1
|
|
137
155
|
return
|
|
138
156
|
}
|
|
139
|
-
err = commands.Run()
|
|
157
|
+
err = commands.Run(ctx)
|
|
140
158
|
case "snapshot":
|
|
141
159
|
metrics.Track("cli.command.execute", map[string]interface{}{
|
|
142
160
|
"command": "snapshot",
|
|
143
161
|
})
|
|
144
|
-
err = commands.Snapshot()
|
|
162
|
+
err = commands.Snapshot(ctx)
|
|
145
163
|
case "sync":
|
|
146
164
|
metrics.Track("cli.command.execute", map[string]interface{}{
|
|
147
165
|
"command": "sync",
|
|
148
166
|
})
|
|
149
167
|
// Auto-setup hooks on first run
|
|
150
|
-
if err := commands.EnsureHooksConfigured(); err != nil {
|
|
168
|
+
if err := commands.EnsureHooksConfigured(ctx); err != nil {
|
|
151
169
|
slog.Error("Failed to configure hooks", "error", err)
|
|
152
170
|
exitCode = 1
|
|
153
171
|
return
|
|
154
172
|
}
|
|
155
|
-
err = commands.Sync()
|
|
173
|
+
err = commands.Sync(ctx)
|
|
156
174
|
case "list":
|
|
157
175
|
metrics.Track("cli.command.execute", map[string]interface{}{
|
|
158
176
|
"command": "list",
|
|
159
177
|
})
|
|
160
178
|
// Auto-setup hooks on first run
|
|
161
|
-
if err := commands.EnsureHooksConfigured(); err != nil {
|
|
179
|
+
if err := commands.EnsureHooksConfigured(ctx); err != nil {
|
|
162
180
|
slog.Error("Failed to configure hooks", "error", err)
|
|
163
181
|
exitCode = 1
|
|
164
182
|
return
|
|
165
183
|
}
|
|
166
|
-
err = commands.List()
|
|
184
|
+
err = commands.List(ctx)
|
|
167
185
|
case "config":
|
|
168
186
|
// Handle config subcommands
|
|
169
187
|
if len(os.Args) < 3 {
|
|
@@ -186,7 +204,7 @@ func main() {
|
|
|
186
204
|
err = pathErr
|
|
187
205
|
break
|
|
188
206
|
}
|
|
189
|
-
err = commands.ConfigClaude(settingsPath)
|
|
207
|
+
err = commands.ConfigClaude(ctx, settingsPath)
|
|
190
208
|
case "cursor":
|
|
191
209
|
metrics.Track("cli.command.execute", map[string]interface{}{
|
|
192
210
|
"command": "config.cursor",
|
|
@@ -196,12 +214,12 @@ func main() {
|
|
|
196
214
|
err = pathErr
|
|
197
215
|
break
|
|
198
216
|
}
|
|
199
|
-
err = commands.ConfigCursor(hooksPath)
|
|
217
|
+
err = commands.ConfigCursor(ctx, hooksPath)
|
|
200
218
|
case "list":
|
|
201
219
|
metrics.Track("cli.command.execute", map[string]interface{}{
|
|
202
220
|
"command": "config.list",
|
|
203
221
|
})
|
|
204
|
-
err = commands.ConfigList()
|
|
222
|
+
err = commands.ConfigList(ctx)
|
|
205
223
|
default:
|
|
206
224
|
fmt.Fprintf(os.Stderr, "Unknown config subcommand: %s\n", subCmd)
|
|
207
225
|
exitCode = 1
|
|
@@ -232,12 +250,12 @@ func main() {
|
|
|
232
250
|
metrics.Track("cli.command.execute", map[string]interface{}{
|
|
233
251
|
"command": "login",
|
|
234
252
|
})
|
|
235
|
-
err = commands.Login()
|
|
253
|
+
err = commands.Login(ctx)
|
|
236
254
|
case "sync-policies":
|
|
237
255
|
metrics.Track("cli.command.execute", map[string]interface{}{
|
|
238
256
|
"command": "sync-policies",
|
|
239
257
|
})
|
|
240
|
-
err = commands.SyncPolicies()
|
|
258
|
+
err = commands.SyncPolicies(ctx)
|
|
241
259
|
case "version", "-v", "--version":
|
|
242
260
|
fmt.Println(Version)
|
|
243
261
|
return
|
|
@@ -252,9 +270,8 @@ func main() {
|
|
|
252
270
|
}
|
|
253
271
|
|
|
254
272
|
if err != nil {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
sentry.CaptureException(err)
|
|
273
|
+
utils.CaptureError(err, map[string]string{
|
|
274
|
+
"command": subcommand,
|
|
258
275
|
})
|
|
259
276
|
metrics.Track("cli.command.error", map[string]interface{}{
|
|
260
277
|
"command": subcommand,
|
|
@@ -328,7 +345,7 @@ HOOK WORKFLOW:
|
|
|
328
345
|
fmt.Print(help)
|
|
329
346
|
}
|
|
330
347
|
|
|
331
|
-
func newLogger(levelStr, format string, output io.Writer, isTTY bool) *slog.Logger {
|
|
348
|
+
func newLogger(ctx context.Context, levelStr, format string, output io.Writer, isTTY bool) *slog.Logger {
|
|
332
349
|
var lvl slog.Level
|
|
333
350
|
switch strings.ToLower(levelStr) {
|
|
334
351
|
case "debug":
|
|
@@ -347,10 +364,10 @@ func newLogger(levelStr, format string, output io.Writer, isTTY bool) *slog.Logg
|
|
|
347
364
|
Level: lvl,
|
|
348
365
|
}
|
|
349
366
|
|
|
350
|
-
var
|
|
367
|
+
var baseHandler slog.Handler
|
|
351
368
|
switch strings.ToLower(format) {
|
|
352
369
|
case "json":
|
|
353
|
-
|
|
370
|
+
baseHandler = slog.NewJSONHandler(output, opts)
|
|
354
371
|
default:
|
|
355
372
|
if isTTY {
|
|
356
373
|
opts.ReplaceAttr = func(groups []string, a slog.Attr) slog.Attr {
|
|
@@ -360,10 +377,60 @@ func newLogger(levelStr, format string, output io.Writer, isTTY bool) *slog.Logg
|
|
|
360
377
|
return a
|
|
361
378
|
}
|
|
362
379
|
}
|
|
363
|
-
|
|
380
|
+
baseHandler = slog.NewTextHandler(output, opts)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
sentryHandler := sentryslog.Option{
|
|
384
|
+
EventLevel: []slog.Level{slog.LevelError, sentryslog.LevelFatal},
|
|
385
|
+
// LogLevel defaults to `[]slog.Level{slog.LevelDebug, slog.LevelInfo, slog.LevelWarn, slog.LevelError, LevelFatal}`, which seems reasonable
|
|
386
|
+
AddSource: true,
|
|
387
|
+
}.NewSentryHandler(ctx)
|
|
388
|
+
|
|
389
|
+
return slog.New(newMultiHandler(baseHandler, sentryHandler))
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
type multiHandler struct {
|
|
393
|
+
handlers []slog.Handler
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
func newMultiHandler(handlers ...slog.Handler) *multiHandler {
|
|
397
|
+
return &multiHandler{handlers: handlers}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
func (h *multiHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
|
401
|
+
for _, handler := range h.handlers {
|
|
402
|
+
if handler.Enabled(ctx, level) {
|
|
403
|
+
return true
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return false
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
func (h *multiHandler) Handle(ctx context.Context, r slog.Record) error {
|
|
410
|
+
for _, handler := range h.handlers {
|
|
411
|
+
if handler.Enabled(ctx, r.Level) {
|
|
412
|
+
if err := handler.Handle(ctx, r); err != nil {
|
|
413
|
+
return err
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return nil
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
func (h *multiHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
|
421
|
+
handlers := make([]slog.Handler, len(h.handlers))
|
|
422
|
+
for i, handler := range h.handlers {
|
|
423
|
+
handlers[i] = handler.WithAttrs(attrs)
|
|
364
424
|
}
|
|
425
|
+
return &multiHandler{handlers: handlers}
|
|
426
|
+
}
|
|
365
427
|
|
|
366
|
-
|
|
428
|
+
func (h *multiHandler) WithGroup(name string) slog.Handler {
|
|
429
|
+
handlers := make([]slog.Handler, len(h.handlers))
|
|
430
|
+
for i, handler := range h.handlers {
|
|
431
|
+
handlers[i] = handler.WithGroup(name)
|
|
432
|
+
}
|
|
433
|
+
return &multiHandler{handlers: handlers}
|
|
367
434
|
}
|
|
368
435
|
|
|
369
436
|
func createLogFile(logFilePath string) (string, error) {
|
package/package.json
CHANGED
package/utils/sentry.go
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package utils
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"github.com/getsentry/sentry-go"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
// CaptureError captures an error with additional context to Sentry
|
|
8
|
+
func CaptureError(err error, ctx map[string]string) {
|
|
9
|
+
sentry.WithScope(func(scope *sentry.Scope) {
|
|
10
|
+
for k, v := range ctx {
|
|
11
|
+
scope.SetTag(k, v)
|
|
12
|
+
}
|
|
13
|
+
sentry.CaptureException(err)
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// AddBreadcrumb adds a breadcrumb to the current Sentry scope
|
|
18
|
+
func AddBreadcrumb(category, message string, level sentry.Level, data map[string]interface{}) {
|
|
19
|
+
sentry.AddBreadcrumb(&sentry.Breadcrumb{
|
|
20
|
+
Category: category,
|
|
21
|
+
Message: message,
|
|
22
|
+
Level: level,
|
|
23
|
+
Data: data,
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// SetUserContext sets the user context for Sentry
|
|
28
|
+
func SetUserContext(userID, email string) {
|
|
29
|
+
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
|
30
|
+
scope.SetUser(sentry.User{
|
|
31
|
+
ID: userID,
|
|
32
|
+
Email: email,
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// SetGlobalTags sets global tags for all Sentry events
|
|
38
|
+
func SetGlobalTags(tags map[string]string) {
|
|
39
|
+
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
|
40
|
+
for k, v := range tags {
|
|
41
|
+
scope.SetTag(k, v)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|