Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions cmd/duty.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func resolveNameOrID[T any](arg string, items []T, key func(T) (name, id string)
// id, which addresses its nested duty resources. GLOBAL agents (the
// praxis duty agent) are fetched via include_global=true.
func resolveAgentID(out io.Writer, active credentials.Active, agentArg string) string {
agents, err := agentcatalog.FetchIncludingGlobal(active.Profile.URL, active.Profile.Token)
agents, err := agentcatalog.FetchIncludingGlobal(active.Profile.URL, active.Profile.Auth())
if err != nil {
return reportResolveErr(out, active.Name, err)
}
Expand All @@ -111,7 +111,7 @@ func resolveAgentID(out io.Writer, active credentials.Active, agentArg string) s
// under the resolved agent. Same name→id-then-passthrough policy as
// resolveAgentID.
func resolveScheduleID(out io.Writer, active credentials.Active, agentID, dutyArg string) string {
schedules, err := duties.ListSchedules(active.Profile.URL, active.Profile.Token, agentID, "")
schedules, err := duties.ListSchedules(active.Profile.URL, active.Profile.Auth(), agentID, "")
if err != nil {
return reportResolveErr(out, active.Name, err)
}
Expand Down Expand Up @@ -141,7 +141,7 @@ var dutyListCmd = &cobra.Command{
active := activeOrAuthExit(out)
agentID := resolveAgentID(out, active, dutyAgent)

schedules, err := duties.ListSchedules(active.Profile.URL, active.Profile.Token, agentID, "")
schedules, err := duties.ListSchedules(active.Profile.URL, active.Profile.Auth(), agentID, "")
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand Down Expand Up @@ -176,7 +176,7 @@ var dutyRunsCmd = &cobra.Command{
scheduleID = resolveScheduleID(out, active, agentID, dutyRunsDuty)
}

runs, err := duties.ListRuns(active.Profile.URL, active.Profile.Token, agentID, scheduleID, dutyRunsLimit)
runs, err := duties.ListRuns(active.Profile.URL, active.Profile.Auth(), agentID, scheduleID, dutyRunsLimit)
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand All @@ -203,7 +203,7 @@ var dutyRunCmd = &cobra.Command{
active := activeOrAuthExit(out)
agentID := resolveAgentID(out, active, dutyAgent)

run, err := duties.GetRun(active.Profile.URL, active.Profile.Token, agentID, args[0])
run, err := duties.GetRun(active.Profile.URL, active.Profile.Auth(), agentID, args[0])
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand All @@ -227,7 +227,7 @@ var dutyReportCmd = &cobra.Command{
active := activeOrAuthExit(out)
agentID := resolveAgentID(out, active, dutyAgent)

run, err := duties.GetRun(active.Profile.URL, active.Profile.Token, agentID, args[0])
run, err := duties.GetRun(active.Profile.URL, active.Profile.Auth(), agentID, args[0])
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand All @@ -239,7 +239,7 @@ var dutyReportCmd = &cobra.Command{
os.Exit(exitcode.Error)
}

body, mime, err := duties.FetchArtifactContent(active.Profile.URL, active.Profile.Token, *run.ReportArtifactID)
body, mime, err := duties.FetchArtifactContent(active.Profile.URL, active.Profile.Auth(), *run.ReportArtifactID)
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand Down Expand Up @@ -279,7 +279,7 @@ var dutyFindingsCmd = &cobra.Command{
agentID := resolveAgentID(out, active, dutyAgent)
scheduleID := resolveScheduleID(out, active, agentID, args[0])

findings, err := duties.ListFindings(active.Profile.URL, active.Profile.Token, agentID, scheduleID, dutyFindingsStatus, dutyFindingsLimit)
findings, err := duties.ListFindings(active.Profile.URL, active.Profile.Auth(), agentID, scheduleID, dutyFindingsStatus, dutyFindingsLimit)
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand Down
26 changes: 13 additions & 13 deletions cmd/duty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func resetDutyFlags() {
func stubAgentResolution(t *testing.T, praxisID string) func() {
t.Helper()
orig := agentcatalog.FetchIncludingGlobal
agentcatalog.FetchIncludingGlobal = func(baseURL, token string) ([]agentcatalog.Agent, error) {
agentcatalog.FetchIncludingGlobal = func(baseURL string, token map[string]string) ([]agentcatalog.Agent, error) {
return []agentcatalog.Agent{{ID: praxisID, Name: "praxis", Scope: "global", IsActive: true}}, nil
}
return func() { agentcatalog.FetchIncludingGlobal = orig }
Expand All @@ -37,7 +37,7 @@ func stubAgentResolution(t *testing.T, praxisID string) func() {
func stubScheduleResolution(t *testing.T, name, id string) func() {
t.Helper()
orig := duties.ListSchedules
duties.ListSchedules = func(baseURL, token, agentID, tag string) ([]duties.Schedule, error) {
duties.ListSchedules = func(baseURL string, token map[string]string, agentID, tag string) ([]duties.Schedule, error) {
return []duties.Schedule{{ID: id, AgentID: agentID, Name: name, DisplayName: "Prod Watch", Status: "active", Enabled: true}}, nil
}
return func() { duties.ListSchedules = orig }
Expand Down Expand Up @@ -65,9 +65,9 @@ func TestDutyList_ResolvesAgentAndEmitsJSON(t *testing.T) {
defer restoreAgent()

orig := duties.ListSchedules
duties.ListSchedules = func(baseURL, token, agentID, tag string) ([]duties.Schedule, error) {
if baseURL != "https://x.test" || token != "sk_test_T" {
t.Errorf("auth threading: url=%q token=%q", baseURL, token)
duties.ListSchedules = func(baseURL string, auth map[string]string, agentID, tag string) ([]duties.Schedule, error) {
if baseURL != "https://x.test" || auth["Authorization"] != "Bearer sk_test_T" {
t.Errorf("auth threading: url=%q auth=%v", baseURL, auth)
}
if agentID != "agt_praxis" {
t.Errorf("agentID = %q; want agt_praxis (resolved from default --agent praxis)", agentID)
Expand Down Expand Up @@ -99,7 +99,7 @@ func TestDutyList_EmptyEmitsArray(t *testing.T) {
defer stubAgentResolution(t, "agt_praxis")()

orig := duties.ListSchedules
duties.ListSchedules = func(baseURL, token, agentID, tag string) ([]duties.Schedule, error) {
duties.ListSchedules = func(baseURL string, token map[string]string, agentID, tag string) ([]duties.Schedule, error) {
return nil, nil
}
defer func() { duties.ListSchedules = orig }()
Expand Down Expand Up @@ -132,7 +132,7 @@ func TestDutyRuns_ResolvesDutyNameToScheduleID(t *testing.T) {
defer stubScheduleResolution(t, "prod-watch", "sch1")()

orig := duties.ListRuns
duties.ListRuns = func(baseURL, token, agentID, scheduleID string, limit int) ([]duties.Run, error) {
duties.ListRuns = func(baseURL string, token map[string]string, agentID, scheduleID string, limit int) ([]duties.Run, error) {
if agentID != "agt_praxis" || scheduleID != "sch1" {
t.Errorf("resolution wrong: agent=%q schedule=%q", agentID, scheduleID)
}
Expand Down Expand Up @@ -168,7 +168,7 @@ func TestDutyRun_EmitsRunDetail(t *testing.T) {
defer stubAgentResolution(t, "agt_praxis")()

orig := duties.GetRun
duties.GetRun = func(baseURL, token, agentID, runID string) (*duties.Run, error) {
duties.GetRun = func(baseURL string, token map[string]string, agentID, runID string) (*duties.Run, error) {
if runID != "run9" {
t.Errorf("runID = %q", runID)
}
Expand Down Expand Up @@ -201,14 +201,14 @@ func TestDutyReport_FetchesArtifactContent(t *testing.T) {
defer stubAgentResolution(t, "agt_praxis")()

origRun := duties.GetRun
duties.GetRun = func(baseURL, token, agentID, runID string) (*duties.Run, error) {
duties.GetRun = func(baseURL string, token map[string]string, agentID, runID string) (*duties.Run, error) {
art := "art9"
return &duties.Run{ID: runID, ReportArtifactID: &art}, nil
}
defer func() { duties.GetRun = origRun }()

origArt := duties.FetchArtifactContent
duties.FetchArtifactContent = func(baseURL, token, artifactID string) ([]byte, string, error) {
duties.FetchArtifactContent = func(baseURL string, token map[string]string, artifactID string) ([]byte, string, error) {
if artifactID != "art9" {
t.Errorf("artifactID = %q; want art9 (from run.report_artifact_id)", artifactID)
}
Expand Down Expand Up @@ -244,7 +244,7 @@ func TestDutyFindings_EmitsJSON(t *testing.T) {
defer stubScheduleResolution(t, "prod-watch", "sch1")()

orig := duties.ListFindings
duties.ListFindings = func(baseURL, token, agentID, scheduleID, status string, limit int) ([]duties.Finding, error) {
duties.ListFindings = func(baseURL string, token map[string]string, agentID, scheduleID, status string, limit int) ([]duties.Finding, error) {
if scheduleID != "sch1" || status != "open" {
t.Errorf("schedule=%q status=%q", scheduleID, status)
}
Expand Down Expand Up @@ -343,7 +343,7 @@ func TestResolveAgentID_NameIDAndPassthrough(t *testing.T) {
defer resetDutyFlags()

orig := agentcatalog.FetchIncludingGlobal
agentcatalog.FetchIncludingGlobal = func(baseURL, token string) ([]agentcatalog.Agent, error) {
agentcatalog.FetchIncludingGlobal = func(baseURL string, token map[string]string) ([]agentcatalog.Agent, error) {
return []agentcatalog.Agent{
{ID: "agt_praxis", Name: "praxis", IsActive: true},
{ID: "agt_org", Name: "org-bot", IsActive: true},
Expand Down Expand Up @@ -374,7 +374,7 @@ func TestResolveScheduleID_NameIDAndPassthrough(t *testing.T) {
defer resetDutyFlags()

orig := duties.ListSchedules
duties.ListSchedules = func(baseURL, token, agentID, tag string) ([]duties.Schedule, error) {
duties.ListSchedules = func(baseURL string, token map[string]string, agentID, tag string) ([]duties.Schedule, error) {
return []duties.Schedule{
{ID: "sch1", Name: "prod-watch"},
{ID: "sch2", Name: "cost-audit"},
Expand Down
16 changes: 8 additions & 8 deletions cmd/git_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ are no-ops because the token is ephemeral — nothing is persisted on the laptop
},
}

// resolveGateway returns the active profile's gateway URL + token.
func resolveGateway() (string, string, error) {
// resolveGateway returns the active profile's gateway URL + auth headers.
func resolveGateway() (string, map[string]string, error) {
active, err := credentials.ResolveActive("")
if err != nil {
return "", "", err
return "", nil, err
}
if !active.Loaded || active.Profile.Token == "" {
return "", "", fmt.Errorf("no credentials for profile %q — run `praxis login`", active.Name)
return "", nil, fmt.Errorf("no credentials for profile %q — run `praxis login`", active.Name)
}
return active.Profile.URL, active.Profile.Token, nil
return active.Profile.URL, active.Profile.Auth(), nil
}

// isGitHubHost reports whether a brokered GitHub token may be handed to host.
Expand All @@ -74,7 +74,7 @@ func isGitHubHost(host string) bool {
}

// runGitCredential handles one credential-helper invocation.
func runGitCredential(out io.Writer, in io.Reader, op string, gw func() (string, string, error)) error {
func runGitCredential(out io.Writer, in io.Reader, op string, gw func() (string, map[string]string, error)) error {
switch op {
case "get":
// handled below
Expand All @@ -99,11 +99,11 @@ func runGitCredential(out io.Writer, in io.Reader, op string, gw func() (string,
"path": attrs["path"],
})

baseURL, token, err := gw()
baseURL, auth, err := gw()
if err != nil {
return err
}
raw, status, err := callMCP(baseURL, token, "vcs_cli", "mint_repo_credential", body, 30*time.Second)
raw, status, err := callMCP(baseURL, auth, "vcs_cli", "mint_repo_credential", body, 30*time.Second)
if err != nil {
return fmt.Errorf("gateway call failed: %w", err)
}
Expand Down
26 changes: 16 additions & 10 deletions cmd/git_credential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import (
"time"
)

// bearer builds the Auth() header map a Bearer-mode profile produces.
// Shared across cmd tests that thread an auth header map through a seam.
func bearer(tok string) map[string]string {
return map[string]string{"Authorization": "Bearer " + tok}
}

func TestGitCredentialGet_EmitsUsernamePassword(t *testing.T) {
orig := callMCP
defer func() { callMCP = orig }()
// mint_repo_credential returns an MCP envelope whose text is JSON.
callMCP = func(baseURL, token, mcp, fn string, body []byte, timeout time.Duration) ([]byte, int, error) {
callMCP = func(baseURL string, auth map[string]string, mcp, fn string, body []byte, timeout time.Duration) ([]byte, int, error) {
if mcp != "vcs_cli" || fn != "mint_repo_credential" {
t.Fatalf("unexpected call %s/%s", mcp, fn)
}
Expand All @@ -22,7 +28,7 @@ func TestGitCredentialGet_EmitsUsernamePassword(t *testing.T) {
in := strings.NewReader("protocol=https\nhost=github.com\npath=owner/x\n\n")
var out bytes.Buffer
err := runGitCredential(&out, in, "get",
func() (string, string, error) { return "https://gw", "tok", nil })
func() (string, map[string]string, error) { return "https://gw", bearer("tok"), nil })
if err != nil {
t.Fatalf("err: %v", err)
}
Expand All @@ -37,7 +43,7 @@ func TestGitCredentialStoreErase_NoOp(t *testing.T) {
var out bytes.Buffer
in := strings.NewReader("protocol=https\nhost=github.com\n\n")
if err := runGitCredential(&out, in, op,
func() (string, string, error) { return "https://gw", "tok", nil }); err != nil {
func() (string, map[string]string, error) { return "https://gw", bearer("tok"), nil }); err != nil {
t.Fatalf("%s should be no-op, got %v", op, err)
}
if out.Len() != 0 {
Expand All @@ -50,15 +56,15 @@ func TestGitCredentialGet_ParsesHostAndPath(t *testing.T) {
orig := callMCP
defer func() { callMCP = orig }()
var sentBody []byte
callMCP = func(baseURL, token, mcp, fn string, body []byte, timeout time.Duration) ([]byte, int, error) {
callMCP = func(baseURL string, auth map[string]string, mcp, fn string, body []byte, timeout time.Duration) ([]byte, int, error) {
sentBody = body
env := `{"content":[{"type":"text","text":"{\"username\":\"x-access-token\",\"password\":\"ghs_abc\"}"}]}`
return []byte(env), 200, nil
}
in := strings.NewReader("protocol=https\nhost=github.com\npath=owner/x\n\n")
var out bytes.Buffer
if err := runGitCredential(&out, in, "get",
func() (string, string, error) { return "https://gw", "tok", nil }); err != nil {
func() (string, map[string]string, error) { return "https://gw", bearer("tok"), nil }); err != nil {
t.Fatalf("err: %v", err)
}
if !strings.Contains(string(sentBody), `"host":"github.com"`) ||
Expand All @@ -80,15 +86,15 @@ func assertSilentFallThrough(t *testing.T, protocol, host string) {
orig := callMCP
defer func() { callMCP = orig }()
called := false
callMCP = func(baseURL, token, mcp, fn string, body []byte, timeout time.Duration) ([]byte, int, error) {
callMCP = func(baseURL string, auth map[string]string, mcp, fn string, body []byte, timeout time.Duration) ([]byte, int, error) {
called = true
return nil, 200, nil
}

var out bytes.Buffer
in := strings.NewReader("protocol=" + protocol + "\nhost=" + host + "\n\n")
if err := runGitCredential(&out, in, "get",
func() (string, string, error) { return "https://gw", "tok", nil }); err != nil {
func() (string, map[string]string, error) { return "https://gw", bearer("tok"), nil }); err != nil {
t.Fatalf("expected silent fall-through, got err %v", err)
}
if called {
Expand Down Expand Up @@ -117,14 +123,14 @@ func TestGitCredentialGet_AllowsGitHubHosts(t *testing.T) {
t.Run(host, func(t *testing.T) {
orig := callMCP
defer func() { callMCP = orig }()
callMCP = func(baseURL, token, mcp, fn string, body []byte, timeout time.Duration) ([]byte, int, error) {
callMCP = func(baseURL string, auth map[string]string, mcp, fn string, body []byte, timeout time.Duration) ([]byte, int, error) {
env := `{"content":[{"type":"text","text":"{\"username\":\"x-access-token\",\"password\":\"ghs_abc\"}"}]}`
return []byte(env), 200, nil
}
var out bytes.Buffer
in := strings.NewReader("protocol=https\nhost=" + host + "\n\n")
if err := runGitCredential(&out, in, "get",
func() (string, string, error) { return "https://gw", "tok", nil }); err != nil {
func() (string, map[string]string, error) { return "https://gw", bearer("tok"), nil }); err != nil {
t.Fatalf("err: %v", err)
}
if !strings.Contains(out.String(), "password=ghs_abc") {
Expand All @@ -138,7 +144,7 @@ func TestGitCredential_RejectsUnknownOperation(t *testing.T) {
var out bytes.Buffer
in := strings.NewReader("protocol=https\nhost=github.com\n\n")
err := runGitCredential(&out, in, "gte",
func() (string, string, error) { return "https://gw", "tok", nil })
func() (string, map[string]string, error) { return "https://gw", bearer("tok"), nil })
if err == nil {
t.Fatal("unknown operation must return an error, not silently succeed")
}
Expand Down
16 changes: 8 additions & 8 deletions cmd/ig.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func syncOne(active credentials.Active, catalog string) (upToDate bool, err erro
local, _ := readSyncState(dir)

body, etag, notModified, err := igcatalog.DownloadBundle(
active.Profile.URL, active.Profile.Token, catalog, local.Digest)
active.Profile.URL, active.Profile.Auth(), catalog, local.Digest)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -419,7 +419,7 @@ func statusOne(active credentials.Active, catalog string) (state, serverVersion,
return "", "", "", err
}
local, synced := readSyncState(catalogDir(home, catalog))
c, err := igcatalog.GetCatalog(active.Profile.URL, active.Profile.Token, catalog)
c, err := igcatalog.GetCatalog(active.Profile.URL, active.Profile.Auth(), catalog)
if err != nil {
return "", "", "", err
}
Expand Down Expand Up @@ -518,7 +518,7 @@ var igListCmd = &cobra.Command{
asJSON := render.UseJSON(igJSON, false, out)
active := activeOrAuthExitProfile(out, igProfile)

cats, err := igcatalog.ListCatalogs(active.Profile.URL, active.Profile.Token)
cats, err := igcatalog.ListCatalogs(active.Profile.URL, active.Profile.Auth())
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand Down Expand Up @@ -557,7 +557,7 @@ live tree.`,

targets := args
if igSyncAll {
cats, err := igcatalog.ListCatalogs(active.Profile.URL, active.Profile.Token)
cats, err := igcatalog.ListCatalogs(active.Profile.URL, active.Profile.Auth())
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand Down Expand Up @@ -687,7 +687,7 @@ var igPublishCmd = &cobra.Command{
active := activeOrAuthExitProfile(out, igProfile)

gz := gzipBytes(raw)
if err := igcatalog.PublishMember(active.Profile.URL, active.Profile.Token,
if err := igcatalog.PublishMember(active.Profile.URL, active.Profile.Auth(),
igPublishCatalog, igPublishMember, gz, git, sha); err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand Down Expand Up @@ -736,7 +736,7 @@ var igClaimsCmd = &cobra.Command{
}
active := activeOrAuthExitProfile(out, igProfile)

names, err := igcatalog.Claims(active.Profile.URL, active.Profile.Token, igClaimsGit)
names, err := igcatalog.Claims(active.Profile.URL, active.Profile.Auth(), igClaimsGit)
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand Down Expand Up @@ -790,7 +790,7 @@ var igManifestPushCmd = &cobra.Command{
PushedAt: nowFn().UTC().Format(time.RFC3339),
GitSHA: gitSHA,
}
if err := igcatalog.ManifestPush(active.Profile.URL, active.Profile.Token, igManifestCatalog, m); err != nil {
if err := igcatalog.ManifestPush(active.Profile.URL, active.Profile.Auth(), igManifestCatalog, m); err != nil {
return reportHTTPErr(out, active.Name, err)
}
result := map[string]string{
Expand All @@ -813,7 +813,7 @@ var igManifestPullCmd = &cobra.Command{
out := cmd.OutOrStdout()
active := activeOrAuthExitProfile(out, igProfile)

m, err := igcatalog.ManifestPull(active.Profile.URL, active.Profile.Token, args[0])
m, err := igcatalog.ManifestPull(active.Profile.URL, active.Profile.Auth(), args[0])
if err != nil {
return reportHTTPErr(out, active.Name, err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/ig_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func serverClaims(canonURL string) ([]string, error) {
}
ch := make(chan res, 1)
go func() {
n, e := igcatalog.Claims(act.Profile.URL, act.Profile.Token, canonURL)
n, e := igcatalog.Claims(act.Profile.URL, act.Profile.Auth(), canonURL)
ch <- res{n, e}
}()
select {
Expand Down
Loading