mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-18 22:13:16 +00:00
feat: grant first setup agent gateway operator access
This commit is contained in:
1 parent
95fd1f8f2d
commit
c320de5a87
22 files changed
+1300
-45
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nextlevelbuilder/goclaw/internal/config"
|
||||
@@ -29,6 +30,9 @@ var healthClient = &http.Client{Timeout: 3 * time.Second}
|
||||
|
||||
// resolveGatewayBaseURL reads host/port from config and returns http://host:port.
|
||||
func resolveGatewayBaseURL() string {
|
||||
if server := strings.TrimSpace(os.Getenv("GOCLAW_SERVER")); server != "" {
|
||||
return strings.TrimRight(server, "/")
|
||||
}
|
||||
cfg, err := config.Load(resolveConfigPath())
|
||||
if err != nil {
|
||||
return "http://127.0.0.1:18790"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package cmd
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestResolveGatewayBaseURLUsesGOCLAWServer(t *testing.T) {
|
||||
t.Setenv("GOCLAW_SERVER", "http://127.0.0.1:19999/")
|
||||
|
||||
got := resolveGatewayBaseURL()
|
||||
if got != "http://127.0.0.1:19999" {
|
||||
t.Fatalf("resolveGatewayBaseURL()=%q, want GOCLAW_SERVER without trailing slash", got)
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,11 @@ func wireHTTP(stores *store.Stores, defaultWorkspace, dataDir, bundledSkillsDir
|
||||
agentsH = httpapi.NewAgentsHandler(stores.Agents, stores.Providers, providerReg, stores.DB, stores.Tracing, defaultWorkspace, msgBus, summoner, isOwner)
|
||||
agentsH.SetImportStores(stores.Memory, stores.KnowledgeGraph)
|
||||
agentsH.SetDataDir(dataDir)
|
||||
if stores.SecureCLI != nil && stores.SecureCLIGrants != nil {
|
||||
if agentCreds, ok := stores.SecureCLI.(store.SecureCLIAgentCredentialStore); ok {
|
||||
agentsH.SetGatewayOperatorBootstrap(stores.SecureCLI, stores.SecureCLIGrants, agentCreds, gatewayAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if stores != nil && stores.Skills != nil {
|
||||
|
||||
+55
-11
@@ -16,6 +16,7 @@ func setupAgentStep() {
|
||||
return
|
||||
}
|
||||
|
||||
grantGatewayOperatorAccess := false
|
||||
if len(agents) > 0 {
|
||||
fmt.Printf(" Found %d existing agent(s):\n", len(agents))
|
||||
for _, a := range agents {
|
||||
@@ -30,12 +31,31 @@ func setupAgentStep() {
|
||||
} else {
|
||||
fmt.Println(" No agents yet. Let's create your first one.")
|
||||
fmt.Println()
|
||||
grant, err := promptConfirm("Grant this first agent local gateway operator access via the goclaw CLI?", false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
grantGatewayOperatorAccess = grant
|
||||
if grantGatewayOperatorAccess {
|
||||
fmt.Println(" The agent will get revocable SecureCLI access to run local gateway commands.")
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
createAgent()
|
||||
createAgent(grantGatewayOperatorAccess)
|
||||
}
|
||||
|
||||
func createAgent() {
|
||||
type setupAgentCreateResponse struct {
|
||||
httpAgent
|
||||
GatewayOperatorBootstrap *gatewayOperatorBootstrapResponse `json:"gateway_operator_bootstrap,omitempty"`
|
||||
}
|
||||
|
||||
type gatewayOperatorBootstrapResponse struct {
|
||||
Status string `json:"status"`
|
||||
Warning string `json:"warning,omitempty"`
|
||||
}
|
||||
|
||||
func createAgent(grantGatewayOperatorAccess bool) {
|
||||
agentKey, err := promptString("Agent key (slug)", "e.g. assistant, coder", "assistant")
|
||||
if err != nil {
|
||||
return
|
||||
@@ -80,19 +100,43 @@ func createAgent() {
|
||||
return
|
||||
}
|
||||
|
||||
body := map[string]any{
|
||||
"agent_key": agentKey,
|
||||
"display_name": displayName,
|
||||
"agent_type": agentType,
|
||||
"provider": findProviderType(providers, providerID),
|
||||
"model": model,
|
||||
}
|
||||
body := setupAgentCreatePayload(agentKey, displayName, agentType, findProviderType(providers, providerID), model, grantGatewayOperatorAccess)
|
||||
|
||||
_, err = gatewayHTTPPost("/v1/agents", body)
|
||||
result, err := gatewayHTTPPostTyped[setupAgentCreateResponse]("/v1/agents", body)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, " Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" Agent %q created (%s).\n\n", agentKey, model)
|
||||
fmt.Printf(" Agent %q created (%s).\n", agentKey, model)
|
||||
printGatewayOperatorBootstrapResult(result.GatewayOperatorBootstrap)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func setupAgentCreatePayload(agentKey, displayName, agentType, providerType, model string, grantGatewayOperatorAccess bool) map[string]any {
|
||||
body := map[string]any{
|
||||
"agent_key": agentKey,
|
||||
"display_name": displayName,
|
||||
"agent_type": agentType,
|
||||
"provider": providerType,
|
||||
"model": model,
|
||||
}
|
||||
if grantGatewayOperatorAccess {
|
||||
body["grant_gateway_operator_access"] = true
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func printGatewayOperatorBootstrapResult(result *gatewayOperatorBootstrapResponse) {
|
||||
if result == nil {
|
||||
return
|
||||
}
|
||||
switch result.Status {
|
||||
case "granted":
|
||||
fmt.Println(" Gateway operator access granted. Try: goclaw agent list")
|
||||
case "warning", "skipped":
|
||||
if result.Warning != "" {
|
||||
fmt.Printf(" Warning: %s\n", result.Warning)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cmd
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSetupAgentCreatePayloadIncludesGatewayOperatorConsentOnlyWhenTrue(t *testing.T) {
|
||||
withoutConsent := setupAgentCreatePayload("assistant", "Assistant", "predefined", "anthropic", "claude", false)
|
||||
if _, ok := withoutConsent["grant_gateway_operator_access"]; ok {
|
||||
t.Fatalf("grant_gateway_operator_access must be absent without explicit consent: %#v", withoutConsent)
|
||||
}
|
||||
|
||||
withConsent := setupAgentCreatePayload("assistant", "Assistant", "predefined", "anthropic", "claude", true)
|
||||
if got, ok := withConsent["grant_gateway_operator_access"].(bool); !ok || !got {
|
||||
t.Fatalf("grant_gateway_operator_access missing after consent: %#v", withConsent)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user