@tanagram/cli 0.4.0 → 0.4.2
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/main.go +0 -6
- 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/main.go
CHANGED
|
@@ -179,10 +179,7 @@ USAGE:
|
|
|
179
179
|
COMMANDS:
|
|
180
180
|
run Check git changes against policies (default)
|
|
181
181
|
login Authenticate with Tanagram using Stytch B2B
|
|
182
|
-
<<<<<<< HEAD
|
|
183
182
|
sync-policies Sync cloud policies from Tanagram
|
|
184
|
-
=======
|
|
185
|
-
>>>>>>> origin/main
|
|
186
183
|
snapshot Create a snapshot of current file state (used by PreToolUse hook)
|
|
187
184
|
sync Manually sync instruction files to cache
|
|
188
185
|
list Show all cached policies
|
|
@@ -195,10 +192,7 @@ EXAMPLES:
|
|
|
195
192
|
tanagram # Check changes (auto-syncs if files changed)
|
|
196
193
|
tanagram run # Same as above
|
|
197
194
|
tanagram login # Authenticate with Tanagram
|
|
198
|
-
<<<<<<< HEAD
|
|
199
195
|
tanagram sync-policies # Sync cloud policies from Tanagram
|
|
200
|
-
=======
|
|
201
|
-
>>>>>>> origin/main
|
|
202
196
|
tanagram snapshot # Create snapshot before making changes
|
|
203
197
|
tanagram sync # Manually sync local instruction files
|
|
204
198
|
tanagram list # View all cached policies
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanagram/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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
|
+
}
|