-
Notifications
You must be signed in to change notification settings - Fork 31
feat: implement initial AxeClient for remote agent execution #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||||||||||||||||||||||
| package client | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||
| "bytes" | ||||||||||||||||||||||||||
| "encoding/json" | ||||||||||||||||||||||||||
| "net/http" | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // AxeClient handles communication with the Axe agent service. | ||||||||||||||||||||||||||
| type AxeClient struct { | ||||||||||||||||||||||||||
| BaseURL string | ||||||||||||||||||||||||||
| APIKey string | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| func (c *AxeClient) ExecuteAgent(agentID string, input map[string]interface{}) (map[string]interface{}, error) { | ||||||||||||||||||||||||||
| url := c.BaseURL + "/v1/agents/" + agentID + "/execute" | ||||||||||||||||||||||||||
| data, _ := json.Marshal(input) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| req, _ := http.NewRequest("POST", url, bytes.NewBuffer(data)) | ||||||||||||||||||||||||||
|
Comment on lines
+17
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's find and examine the api.go file
find . -name "api.go" -path "*/client/*" | head -5Repository: jrswab/axe Length of output: 73 🏁 Script executed: cat -n ./pkg/client/api.goRepository: jrswab/axe Length of output: 1067 Handle request-construction errors; right now they're silently dropped. Lines 17 and 19 discard Suggested fix import (
"bytes"
"encoding/json"
+ "fmt"
"net/http"
)
@@
- data, _ := json.Marshal(input)
+ data, err := json.Marshal(input)
+ if err != nil {
+ return nil, fmt.Errorf("encode execute payload as JSON: ensure input contains JSON-serializable values: %w", err)
+ }
- req, _ := http.NewRequest("POST", url, bytes.NewBuffer(data))
+ req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data))
+ if err != nil {
+ return nil, fmt.Errorf("build execute request: check BaseURL %q and agentID %q: %w", c.BaseURL, agentID, err)
+ }As per coding guidelines, "Errors should help the user fix the problem, not just describe it." 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| req.Header.Set("Authorization", "Bearer " + c.APIKey) | ||||||||||||||||||||||||||
| req.Header.Set("Content-Type", "application/json") | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| client := &http.Client{} | ||||||||||||||||||||||||||
| resp, err := client.Do(req) | ||||||||||||||||||||||||||
|
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's look at the actual file structure
find . -name "api.go" -type f | head -20Repository: jrswab/axe Length of output: 73 🏁 Script executed: # Check the pkg/client/api.go file around lines 23-24
if [ -f "pkg/client/api.go" ]; then
wc -l pkg/client/api.go
echo "---"
head -40 pkg/client/api.go
fiRepository: jrswab/axe Length of output: 861 🏁 Script executed: # Let's see the broader context and method signatures
if [ -f "pkg/client/api.go" ]; then
cat -n pkg/client/api.go | head -60
fiRepository: jrswab/axe Length of output: 1067 🏁 Script executed: # Search for AxeClient struct definition and usage
rg "type AxeClient" -A 5Repository: jrswab/axe Length of output: 333 🏁 Script executed: # Look for http.Client creation patterns in the codebase
rg "http\.Client\{\}" -B 2 -A 2 --type goRepository: jrswab/axe Length of output: 268 🏁 Script executed: # Check for any existing HTTPClient field or similar patterns
rg "HTTPClient|httpClient" --type go -B 2 -A 2Repository: jrswab/axe Length of output: 891 🏁 Script executed: # Look for tests related to this API client
fd "test.go|_test.go" | xargs rg -l "AxeClient" 2>/dev/null || echo "No tests found with AxeClient"Repository: jrswab/axe Length of output: 83 Inject the HTTP client and set a timeout (no per-call client creation). Line 23 creates a fresh This also aligns with the guideline: "No global state — pass dependencies explicitly." Suggested refactor import (
"bytes"
"encoding/json"
"net/http"
+ "time"
)
@@
type AxeClient struct {
BaseURL string
APIKey string
+ HTTPClient *http.Client
}
@@
- client := &http.Client{}
- resp, err := client.Do(req)
+ httpClient := c.HTTPClient
+ if httpClient == nil {
+ httpClient = &http.Client{Timeout: 30 * time.Second}
+ }
+ resp, err := httpClient.Do(req)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| defer resp.Body.Close() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| var result map[string]interface{} | ||||||||||||||||||||||||||
| json.NewDecoder(resp.Body).Decode(&result) | ||||||||||||||||||||||||||
| return result, nil | ||||||||||||||||||||||||||
|
Comment on lines
+24
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -name "api.go" -path "*/client/*" | head -20Repository: jrswab/axe Length of output: 73 🏁 Script executed: wc -l pkg/client/api.go 2>/dev/null || echo "File not found, searching..."Repository: jrswab/axe Length of output: 74 🏁 Script executed: if [ -f pkg/client/api.go ]; then
sed -n '20,40p' pkg/client/api.go
else
find . -type f -name "*.go" | grep -i api | head -10
fiRepository: jrswab/axe Length of output: 381 🏁 Script executed: cat -n pkg/client/api.goRepository: jrswab/axe Length of output: 1067 Add HTTP status checking and JSON decode error handling to prevent silent failures. Lines 24-32 silently return success for HTTP 4xx/5xx responses and ignore JSON decode errors—resulting in failed executions reported as successful. Additionally, the error at line 24 lacks context to help operators debug (network issue? Invalid API key? Wrong BaseURL?). Import the necessary packages and:
Suggested fix import (
"bytes"
"encoding/json"
"fmt"
+ "io"
"net/http"
+ "strings"
)
@@
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("execute agent %q request failed: verify BaseURL/network/API key: %w", agentID, err)
}
defer resp.Body.Close()
+
+ if resp.StatusCode < 200 || resp.StatusCode >= 300 {
+ body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
+ return nil, fmt.Errorf("execute agent %q failed with status %d: %s", agentID, resp.StatusCode, strings.TrimSpace(string(body)))
+ }
var result map[string]interface{}
- json.NewDecoder(resp.Body).Decode(&result)
+ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
+ return nil, fmt.Errorf("decode execute response for agent %q as JSON: %w", agentID, err)
+ }
return result, nil🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Verification inconclusive
Script executed:
Repository: jrswab/axe
Repository: jrswab/axe
Exit code: 0
stdout:
Script executed:
rg "BaseURL.*\+" pkg/Repository: jrswab/axe
Repository: jrswab/axe
Exit code: 0
stdout:
Escape
agentIDbefore building the endpoint path — but fix the variable shadowing in the suggested solution.Line 16 concatenates raw
agentIDinto the path. Special characters like/,?, or spaces will corrupt the URL structure. Useurl.PathEscape(agentID)to safely escape the ID, and normalize theBaseURLtrailing slash.However, the suggested fix has a critical bug: assigning to a variable named
urlshadows thenet/urlpackage import, causingurl.PathEscape()to fail. Use a different variable name likeendpointinstead:Corrected fix
📝 Committable suggestion
🤖 Prompt for AI Agents