@tanagram/cli 0.4.0 → 0.4.1

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/api/client.go ADDED
@@ -0,0 +1,106 @@
1
+ package api
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "io"
7
+ "net/http"
8
+ "os"
9
+ "time"
10
+
11
+ "github.com/tanagram/cli/auth"
12
+ )
13
+
14
+ // TanagramAPIClient handles communication with the Tanagram API
15
+ type TanagramAPIClient struct {
16
+ baseURL string
17
+ jwt string
18
+ client *http.Client
19
+ }
20
+
21
+ // Repository represents a git repository
22
+ type Repository struct {
23
+ ID string `json:"id"`
24
+ Name string `json:"name"`
25
+ Owner string `json:"owner"`
26
+ IsPrivate bool `json:"is_private"`
27
+ }
28
+
29
+ // Policy represents a Tanagram policy
30
+ type Policy struct {
31
+ ID string `json:"id"`
32
+ Name string `json:"name"`
33
+ OrganizationID string `json:"organization_id"`
34
+ Substrate string `json:"substrate"` // "llm" or "tql"
35
+ DescriptionFromUser string `json:"description_from_user"`
36
+ DescriptionRewrittenByLLM string `json:"description_rewritten_by_llm"`
37
+ CustomMessage string `json:"custom_message"`
38
+ EnabledStatus string `json:"enabled_status"`
39
+ CreatedAt time.Time `json:"created_at"`
40
+ UpdatedAt time.Time `json:"updated_at"`
41
+ PolicyRepositories []Repository `json:"policy_repositories"`
42
+ ViolationsCount int `json:"violations_count"`
43
+ }
44
+
45
+ // PolicyListResponse is the response from GET /api/policies/
46
+ type PolicyListResponse struct {
47
+ Policies []Policy `json:"policies"`
48
+ Total int `json:"total"`
49
+ }
50
+
51
+ // getAPIBaseURL returns the API base URL
52
+ func getAPIBaseURL() string {
53
+ if url := os.Getenv("TANAGRAM_API_URL"); url != "" {
54
+ return url
55
+ }
56
+ return "https://api.tanagram.ai"
57
+ }
58
+
59
+ // NewAPIClient creates a new Tanagram API client
60
+ func NewAPIClient() (*TanagramAPIClient, error) {
61
+ jwt, err := auth.GetAccessToken()
62
+ if err != nil {
63
+ return nil, err
64
+ }
65
+
66
+ return &TanagramAPIClient{
67
+ baseURL: getAPIBaseURL(),
68
+ jwt: jwt,
69
+ client: &http.Client{
70
+ Timeout: 30 * time.Second,
71
+ },
72
+ }, nil
73
+ }
74
+
75
+ // GetPolicies fetches all policies from the API
76
+ func (c *TanagramAPIClient) GetPolicies() (*PolicyListResponse, error) {
77
+ req, err := http.NewRequest("GET", c.baseURL+"/api/policies/", nil)
78
+ if err != nil {
79
+ return nil, fmt.Errorf("failed to create request: %w", err)
80
+ }
81
+
82
+ req.Header.Set("Authorization", "Bearer "+c.jwt)
83
+ req.Header.Set("Content-Type", "application/json")
84
+
85
+ resp, err := c.client.Do(req)
86
+ if err != nil {
87
+ return nil, fmt.Errorf("failed to fetch policies: %w", err)
88
+ }
89
+ defer resp.Body.Close()
90
+
91
+ if resp.StatusCode == 401 {
92
+ return nil, fmt.Errorf("authentication failed - your session may have expired. Please run 'tanagram login' again")
93
+ }
94
+
95
+ if resp.StatusCode != 200 {
96
+ body, _ := io.ReadAll(resp.Body)
97
+ return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
98
+ }
99
+
100
+ var result PolicyListResponse
101
+ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
102
+ return nil, fmt.Errorf("failed to parse response: %w", err)
103
+ }
104
+
105
+ return &result, nil
106
+ }
package/auth/auth.go ADDED
@@ -0,0 +1,26 @@
1
+ package auth
2
+
3
+ import (
4
+ "fmt"
5
+
6
+ "github.com/zalando/go-keyring"
7
+ )
8
+
9
+ const (
10
+ serviceName = "tanagram-cli"
11
+ accessTokenKey = "access-token"
12
+ )
13
+
14
+ // GetAccessToken retrieves the stored access token from the keyring
15
+ func GetAccessToken() (string, error) {
16
+ token, err := keyring.Get(serviceName, accessTokenKey)
17
+ if err != nil {
18
+ return "", fmt.Errorf("not logged in - run 'tanagram login' first: %w", err)
19
+ }
20
+ return token, nil
21
+ }
22
+
23
+ // SetAccessToken stores the access token in the keyring
24
+ func SetAccessToken(token string) error {
25
+ return keyring.Set(serviceName, accessTokenKey, token)
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanagram/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Tanagram - Catch sloppy code before it ships",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -33,6 +33,8 @@
33
33
  },
34
34
  "files": [
35
35
  "bin/tanagram.js",
36
+ "api/",
37
+ "auth/",
36
38
  "checker/",
37
39
  "commands/",
38
40
  "config/",
@@ -45,6 +47,7 @@
45
47
  "snapshot/",
46
48
  "storage/",
47
49
  "tui/",
50
+ "utils/",
48
51
  "main.go",
49
52
  "go.mod",
50
53
  "go.sum",
@@ -0,0 +1,36 @@
1
+ package utils
2
+
3
+ import (
4
+ "os"
5
+ "sync"
6
+
7
+ "github.com/shirou/gopsutil/v3/process"
8
+ )
9
+
10
+ var (
11
+ parentProcessName string
12
+ parentProcessOnce sync.Once
13
+ )
14
+
15
+ // GetParentProcess returns the name of the parent process.
16
+ // The result is memoized after the first call to avoid repeated system calls.
17
+ func GetParentProcess() string {
18
+ parentProcessOnce.Do(func() {
19
+ parentProcessName = "unknown"
20
+
21
+ parentPID := int32(os.Getppid())
22
+ parent, err := process.NewProcess(parentPID)
23
+ if err != nil {
24
+ return
25
+ }
26
+
27
+ name, err := parent.Name()
28
+ if err != nil {
29
+ return
30
+ }
31
+
32
+ parentProcessName = name
33
+ })
34
+
35
+ return parentProcessName
36
+ }