Skip to content

Conversation

@masnwilliams
Copy link
Contributor

@masnwilliams masnwilliams commented Jan 5, 2026

Note

Introduces end-to-end agent authentication and credential management in the CLI, alongside a dependency migration to the new SDK module.

  • New agents auth commands: create, invoke, get, delete, list, plus invocation subcommands invocation get and invocation submit; supports hosted UI login (auto-opens browser), live polling, SSO/button/field submission, and interactive selection flows
  • New credentials commands: create, get, list, update, delete, totp-code for storing login data and retrieving current TOTP codes
  • Root wiring: registers agents and credentials command groups
  • SDK migration: switches imports from github.com/onkernel/kernel-go-sdk to github.com/kernel/kernel-go-sdk and bumps dependency to v0.25.0 (updates across cmd/*, pkg/*, go.mod, go.sum)

Written by Cursor Bugbot for commit dc09ad8. This will update automatically on new commits. Configure here.

type AgentAuthCmd struct {
auth AgentAuthService
invocations AgentAuthInvocationService
browsers BrowsersService
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field is declared but never used anywhere in the file. Consider removing it to avoid confusion.

Suggested change
browsers BrowsersService
type AgentAuthCmd struct {
auth AgentAuthService
invocations AgentAuthInvocationService
}

} else {
pterm.Info.Println("(Opened in browser)")
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The polling loop doesn't check for context cancellation. If a user presses Ctrl+C, the loop will continue until the next API call fails. Consider adding a context check:

Suggested change
}
for time.Since(startTime) < maxWaitTime {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
state, err := a.invocations.Get(ctx, invocation.InvocationID)

if agent.CredentialName != "" {
pterm.Println(fmt.Sprintf(" Credential: %s", agent.CredentialName))
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning nil on failure states like expired/canceled/failed means callers won't know an error occurred. For CLI usage this might be intentional, but consider returning an error so the exit code reflects the failure (e.g., return fmt.Errorf("invocation failed: %s", state.ErrorMessage)).

@masnwilliams masnwilliams marked this pull request as draft January 6, 2026 00:30
masnwilliams and others added 2 commits January 7, 2026 13:49
- Update SDK to v0.25.0 with new import path github.com/kernel/kernel-go-sdk
- Add agents auth list command with filtering
- Add agents auth invocation get/submit commands
- Add full credentials CLI (create, get, list, update, delete, totp-code)
- Add interactive mode (-i) for agent auth create with profile/credential selectors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@masnwilliams masnwilliams marked this pull request as ready for review January 8, 2026 00:20
Comment on lines +200 to +223
case kernel.AgentAuthInvocationResponseStatusExpired:
pterm.Println()
pterm.Error.Println("Invocation expired")
return nil

case kernel.AgentAuthInvocationResponseStatusCanceled:
pterm.Println()
pterm.Error.Println("Invocation was canceled")
return nil

case kernel.AgentAuthInvocationResponseStatusFailed:
pterm.Println()
pterm.Error.Println("Invocation failed")
if state.ErrorMessage != "" {
pterm.Error.Printf(" Error: %s\n", state.ErrorMessage)
}
return nil
}

time.Sleep(pollInterval)
}

pterm.Error.Println("Polling timed out after 5 minutes")
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning nil here still exits 0 even on expired/canceled/failed/timeout. If callers/scripts rely on exit status, returning an error makes this easier to handle.

Suggested change
case kernel.AgentAuthInvocationResponseStatusExpired:
pterm.Println()
pterm.Error.Println("Invocation expired")
return nil
case kernel.AgentAuthInvocationResponseStatusCanceled:
pterm.Println()
pterm.Error.Println("Invocation was canceled")
return nil
case kernel.AgentAuthInvocationResponseStatusFailed:
pterm.Println()
pterm.Error.Println("Invocation failed")
if state.ErrorMessage != "" {
pterm.Error.Printf(" Error: %s\n", state.ErrorMessage)
}
return nil
}
time.Sleep(pollInterval)
}
pterm.Error.Println("Polling timed out after 5 minutes")
return nil
case kernel.AgentAuthInvocationResponseStatusExpired:
pterm.Println()
pterm.Error.Println("Invocation expired")
return fmt.Errorf("invocation expired")
case kernel.AgentAuthInvocationResponseStatusCanceled:
pterm.Println()
pterm.Error.Println("Invocation was canceled")
return fmt.Errorf("invocation was canceled")
case kernel.AgentAuthInvocationResponseStatusFailed:
pterm.Println()
pterm.Error.Println("Invocation failed")
if state.ErrorMessage != "" {
pterm.Error.Printf(" Error: %s\n", state.ErrorMessage)
return fmt.Errorf("invocation failed: %s", state.ErrorMessage)
}
return fmt.Errorf("invocation failed")
}
time.Sleep(pollInterval)
}
pterm.Error.Println("Polling timed out after 5 minutes")
return fmt.Errorf("polling timed out after %s", maxWaitTime)

Comment on lines +71 to +74
if cred.TotpCode != "" {
rows = append(rows, []string{"TOTP Code", cred.TotpCode})
rows = append(rows, []string{"TOTP Expires", cred.TotpCodeExpiresAt.Format(time.RFC3339)})
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor footgun: printing a fresh TOTP code during create (and similarly in update) is easy to leak via terminal scrollback/log capture. I’d avoid printing codes except via the explicit totp-code command.

Suggested change
if cred.TotpCode != "" {
rows = append(rows, []string{"TOTP Code", cred.TotpCode})
rows = append(rows, []string{"TOTP Expires", cred.TotpCodeExpiresAt.Format(time.RFC3339)})
}
// Avoid printing TOTP codes here; use `credentials totp-code` when needed.

masnwilliams and others added 2 commits January 7, 2026 18:26
Shows how to invoke the auth agent after creation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Users can now run `kernel agents auth invoke -i` to select an auth
agent from a list instead of providing the ID directly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
options := []string{}
agentMap := make(map[string]string) // display -> id
for _, agent := range page.Items {
display := fmt.Sprintf("%s - %s (%s)", agent.ID[:8], agent.Domain, agent.ProfileName)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ID slice panics if agent ID shorter than 8 chars

Medium Severity

The expression agent.ID[:8] will cause a panic if agent.ID is shorter than 8 characters. While IDs are typically UUIDs (36 characters), this could panic if the API returns an empty string, a malformed ID, or if the ID format changes. The code iterates over page.Items and slices each ID without a length check.

Fix in Cursor Fix in Web

masnwilliams and others added 2 commits January 7, 2026 18:33
The hosted URL was causing 404 errors. Now we fetch the live view URL
from the first poll after creating the invocation and use that instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants