@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 +106 -0
- package/auth/auth.go +26 -0
- package/package.json +4 -1
- package/utils/process.go +36 -0
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.
|
|
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",
|
package/utils/process.go
ADDED
|
@@ -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
|
+
}
|