Skip to content

Commit afd1bfe

Browse files
rgarciaCursor Agentcursoragentmasnwilliamskernel-internal[bot]
authored
CLI: Update SDK and add auth connections commands (#102)
## Summary - Updates SDK to v0.33.0 (commit 4719594652b863858cb7492ca78adbb850a10552) - **Breaking change fix**: Removes `kernel agents auth` command group since the `AgentAuth` API was removed from the SDK - Fixes `kernel auth connections` to use correct SDK field names after API refactoring - Adds `--proxy-id` and `--proxy-name` flags to `kernel auth connections login` - Previously added `kernel auth connections` command group for the managed auth API: - `create`: Create managed auth for profile/domain combination - `get`: Get managed auth by ID - `list`: List managed auths - `delete`: Delete managed auth - `login`: Start login flow - `submit`: Submit field values to login flow - `follow`: Follow login flow events via SSE - Previously added `credential-providers list-items` command ## Test plan - [x] CLI builds successfully (`go build ./...`) - [x] All tests pass (`go test ./...`) - [x] `kernel auth connections --help` shows all subcommands - [x] `kernel auth connections create --help` shows all flags ## SDK Version `github.com/kernel/kernel-go-sdk@4719594652b863858cb7492ca78adbb850a10552` (v0.33.0) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Medium risk due to the SDK upgrade plus removal/rewiring of CLI command groups and flags, which can break existing scripts and alter API request parameters (auth connections, browsers listing, invocation behavior). No direct credential-handling logic changes beyond new plumbing to the updated SDK APIs. > > **Overview** > **SDK bump to `kernel-go-sdk@v0.33.0` and CLI surface changes.** Removes the entire `kernel agents` command group (including the automated `agents auth run` flow) and stops registering it in `root.go` to match the SDK’s removed `AgentAuth` API. > > **Adds/updates managed auth workflows via `kernel auth connections`.** Introduces `connections create/get/list/delete/login/submit/follow` using the new managed-auth (`AuthConnection*`) SDK types, including new `--proxy-id/--proxy-name` on `login`, support for credential references by name/provider/path/auto, and SSE-based `follow`. > > **Other CLI enhancements.** `kernel browsers list` gains a new `--status` filter (`active|deleted|all`) with `--include-deleted` deprecated; `kernel invoke` adds `--async-timeout` and a new `invoke browsers <invocation_id>` to list browsers created by an invocation; and `credential-providers` adds `list-items <id>` backed by a new `ListItems` SDK call. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit f014527. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor Agent <cursor-agent@kernel.sh> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: mason <mason@onkernel.com> Co-authored-by: kernel-internal[bot] <260533166+kernel-internal[bot]@users.noreply.github.com>
1 parent 6bf0331 commit afd1bfe

File tree

9 files changed

+932
-1384
lines changed

9 files changed

+932
-1384
lines changed

cmd/agents.go

Lines changed: 0 additions & 1360 deletions
This file was deleted.

cmd/auth_connections.go

Lines changed: 741 additions & 0 deletions
Large diffs are not rendered by default.

cmd/browsers.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ func getAvailableViewports() []string {
124124
"1920x1080@25",
125125
"1920x1200@25",
126126
"1440x900@25",
127+
"1280x800@60",
127128
"1024x768@60",
128129
"1200x800@60",
129-
"1280x800@60",
130130
}
131131
}
132132

@@ -222,6 +222,7 @@ type BrowsersCmd struct {
222222
type BrowsersListInput struct {
223223
Output string
224224
IncludeDeleted bool
225+
Status string
225226
Limit int
226227
Offset int
227228
}
@@ -232,7 +233,19 @@ func (b BrowsersCmd) List(ctx context.Context, in BrowsersListInput) error {
232233
}
233234

234235
params := kernel.BrowserListParams{}
235-
if in.IncludeDeleted {
236+
// Use new Status parameter if provided, otherwise fall back to deprecated IncludeDeleted
237+
if in.Status != "" {
238+
switch in.Status {
239+
case "active":
240+
params.Status = kernel.BrowserListParamsStatusActive
241+
case "deleted":
242+
params.Status = kernel.BrowserListParamsStatusDeleted
243+
case "all":
244+
params.Status = kernel.BrowserListParamsStatusAll
245+
default:
246+
return fmt.Errorf("invalid --status value: %s (must be 'active', 'deleted', or 'all')", in.Status)
247+
}
248+
} else if in.IncludeDeleted {
236249
params.IncludeDeleted = kernel.Opt(true)
237250
}
238251
if in.Limit > 0 {
@@ -263,7 +276,8 @@ func (b BrowsersCmd) List(ctx context.Context, in BrowsersListInput) error {
263276

264277
// Prepare table data
265278
headers := []string{"Browser ID", "Created At", "Persistent ID", "Profile", "CDP WS URL", "Live View URL"}
266-
if in.IncludeDeleted {
279+
showDeletedAt := in.IncludeDeleted || in.Status == "deleted" || in.Status == "all"
280+
if showDeletedAt {
267281
headers = append(headers, "Deleted At")
268282
}
269283
tableData := pterm.TableData{headers}
@@ -290,7 +304,7 @@ func (b BrowsersCmd) List(ctx context.Context, in BrowsersListInput) error {
290304
truncateURL(browser.BrowserLiveViewURL, 50),
291305
}
292306

293-
if in.IncludeDeleted {
307+
if showDeletedAt {
294308
deletedAt := "-"
295309
if !browser.DeletedAt.IsZero() {
296310
deletedAt = util.FormatLocal(browser.DeletedAt)
@@ -2053,7 +2067,8 @@ Note: Profiles can only be loaded into sessions that don't already have a profil
20532067
func init() {
20542068
// list flags
20552069
browsersListCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
2056-
browsersListCmd.Flags().Bool("include-deleted", false, "Include soft-deleted browser sessions in the results")
2070+
browsersListCmd.Flags().Bool("include-deleted", false, "DEPRECATED: Use --status instead. Include soft-deleted browser sessions in the results")
2071+
browsersListCmd.Flags().String("status", "", "Filter by status: 'active' (default), 'deleted', or 'all'")
20572072
browsersListCmd.Flags().Int("limit", 0, "Maximum number of results to return (default 20, max 100)")
20582073
browsersListCmd.Flags().Int("offset", 0, "Number of results to skip (for pagination)")
20592074

@@ -2322,11 +2337,13 @@ func runBrowsersList(cmd *cobra.Command, args []string) error {
23222337
b := BrowsersCmd{browsers: &svc}
23232338
out, _ := cmd.Flags().GetString("output")
23242339
includeDeleted, _ := cmd.Flags().GetBool("include-deleted")
2340+
status, _ := cmd.Flags().GetString("status")
23252341
limit, _ := cmd.Flags().GetInt("limit")
23262342
offset, _ := cmd.Flags().GetInt("offset")
23272343
return b.List(cmd.Context(), BrowsersListInput{
23282344
Output: out,
23292345
IncludeDeleted: includeDeleted,
2346+
Status: status,
23302347
Limit: limit,
23312348
Offset: offset,
23322349
})

cmd/browsers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1151,8 +1151,8 @@ func TestGetAvailableViewports_ReturnsExpectedOptions(t *testing.T) {
11511151
assert.Contains(t, viewports, "1920x1080@25")
11521152
assert.Contains(t, viewports, "1920x1200@25")
11531153
assert.Contains(t, viewports, "1440x900@25")
1154-
assert.Contains(t, viewports, "1200x800@60")
11551154
assert.Contains(t, viewports, "1280x800@60")
1155+
assert.Contains(t, viewports, "1200x800@60")
11561156
assert.Contains(t, viewports, "1024x768@60")
11571157
}
11581158

cmd/credential_providers.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type CredentialProvidersService interface {
2020
List(ctx context.Context, opts ...option.RequestOption) (res *[]kernel.CredentialProvider, err error)
2121
Delete(ctx context.Context, id string, opts ...option.RequestOption) (err error)
2222
Test(ctx context.Context, id string, opts ...option.RequestOption) (res *kernel.CredentialProviderTestResult, err error)
23+
ListItems(ctx context.Context, id string, opts ...option.RequestOption) (res *kernel.CredentialProviderListItemsResponse, err error)
2324
}
2425

2526
// CredentialProvidersCmd handles credential provider operations independent of cobra.
@@ -62,6 +63,11 @@ type CredentialProvidersTestInput struct {
6263
Output string
6364
}
6465

66+
type CredentialProvidersListItemsInput struct {
67+
ID string
68+
Output string
69+
}
70+
6571
func (c CredentialProvidersCmd) List(ctx context.Context, in CredentialProvidersListInput) error {
6672
if in.Output != "" && in.Output != "json" {
6773
return fmt.Errorf("unsupported --output value: use 'json'")
@@ -281,6 +287,51 @@ func (c CredentialProvidersCmd) Test(ctx context.Context, in CredentialProviders
281287
return nil
282288
}
283289

290+
func (c CredentialProvidersCmd) ListItems(ctx context.Context, in CredentialProvidersListItemsInput) error {
291+
if in.Output != "" && in.Output != "json" {
292+
return fmt.Errorf("unsupported --output value: use 'json'")
293+
}
294+
295+
if in.Output != "json" {
296+
pterm.Info.Printf("Listing items for credential provider '%s'...\n", in.ID)
297+
}
298+
299+
result, err := c.providers.ListItems(ctx, in.ID)
300+
if err != nil {
301+
return util.CleanedUpSdkError{Err: err}
302+
}
303+
304+
if in.Output == "json" {
305+
if len(result.Items) == 0 {
306+
fmt.Println("[]")
307+
return nil
308+
}
309+
return util.PrintPrettyJSONSlice(result.Items)
310+
}
311+
312+
if len(result.Items) == 0 {
313+
pterm.Info.Println("No items found")
314+
return nil
315+
}
316+
317+
tableData := pterm.TableData{{"Path", "Title", "Vault", "URLs"}}
318+
for _, item := range result.Items {
319+
urls := ""
320+
if len(item.URLs) > 0 {
321+
urls = strings.Join(item.URLs, ", ")
322+
}
323+
tableData = append(tableData, []string{
324+
item.Path,
325+
item.Title,
326+
item.VaultName,
327+
urls,
328+
})
329+
}
330+
331+
PrintTableNoPad(tableData, true)
332+
return nil
333+
}
334+
284335
// --- Cobra wiring ---
285336

286337
var credentialProvidersCmd = &cobra.Command{
@@ -345,13 +396,22 @@ var credentialProvidersTestCmd = &cobra.Command{
345396
RunE: runCredentialProvidersTest,
346397
}
347398

399+
var credentialProvidersListItemsCmd = &cobra.Command{
400+
Use: "list-items <id>",
401+
Short: "List items from a credential provider",
402+
Long: `List all credential items available from the specified external credential provider.`,
403+
Args: cobra.ExactArgs(1),
404+
RunE: runCredentialProvidersListItems,
405+
}
406+
348407
func init() {
349408
credentialProvidersCmd.AddCommand(credentialProvidersListCmd)
350409
credentialProvidersCmd.AddCommand(credentialProvidersGetCmd)
351410
credentialProvidersCmd.AddCommand(credentialProvidersCreateCmd)
352411
credentialProvidersCmd.AddCommand(credentialProvidersUpdateCmd)
353412
credentialProvidersCmd.AddCommand(credentialProvidersDeleteCmd)
354413
credentialProvidersCmd.AddCommand(credentialProvidersTestCmd)
414+
credentialProvidersCmd.AddCommand(credentialProvidersListItemsCmd)
355415

356416
// List flags
357417
credentialProvidersListCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
@@ -379,6 +439,9 @@ func init() {
379439

380440
// Test flags
381441
credentialProvidersTestCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
442+
443+
// ListItems flags
444+
credentialProvidersListItemsCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
382445
}
383446

384447
func runCredentialProvidersList(cmd *cobra.Command, args []string) error {
@@ -464,3 +527,15 @@ func runCredentialProvidersTest(cmd *cobra.Command, args []string) error {
464527
Output: output,
465528
})
466529
}
530+
531+
func runCredentialProvidersListItems(cmd *cobra.Command, args []string) error {
532+
client := getKernelClient(cmd)
533+
output, _ := cmd.Flags().GetString("output")
534+
535+
svc := client.CredentialProviders
536+
c := CredentialProvidersCmd{providers: &svc}
537+
return c.ListItems(cmd.Context(), CredentialProvidersListItemsInput{
538+
ID: args[0],
539+
Output: output,
540+
})
541+
}

cmd/invoke.go

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,20 @@ var invocationHistoryCmd = &cobra.Command{
3535
RunE: runInvocationHistory,
3636
}
3737

38+
var invocationBrowsersCmd = &cobra.Command{
39+
Use: "browsers <invocation_id>",
40+
Short: "List browser sessions for an invocation",
41+
Long: "List all active browser sessions created within a specific invocation.",
42+
Args: cobra.ExactArgs(1),
43+
RunE: runInvocationBrowsers,
44+
}
45+
3846
func init() {
3947
invokeCmd.Flags().StringP("version", "v", "latest", "Specify a version of the app to invoke (optional, defaults to 'latest')")
4048
invokeCmd.Flags().StringP("payload", "p", "", "JSON payload for the invocation (optional)")
4149
invokeCmd.Flags().StringP("payload-file", "f", "", "Path to a JSON file containing the payload (use '-' for stdin)")
4250
invokeCmd.Flags().BoolP("sync", "s", false, "Invoke synchronously (default false). A synchronous invocation will open a long-lived HTTP POST to the Kernel API to wait for the invocation to complete. This will time out after 60 seconds, so only use this option if you expect your invocation to complete in less than 60 seconds. The default is to invoke asynchronously, in which case the CLI will open an SSE connection to the Kernel API after submitting the invocation and wait for the invocation to complete.")
51+
invokeCmd.Flags().Int64("async-timeout", 0, "Timeout in seconds for async invocations (min 10, max 3600). Only applies when async mode is used.")
4352
invokeCmd.Flags().StringP("output", "o", "", "Output format: json for JSONL streaming output")
4453
invokeCmd.MarkFlagsMutuallyExclusive("payload", "payload-file")
4554

@@ -48,6 +57,9 @@ func init() {
4857
invocationHistoryCmd.Flags().String("version", "", "Filter by invocation version")
4958
invocationHistoryCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
5059
invokeCmd.AddCommand(invocationHistoryCmd)
60+
61+
invocationBrowsersCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
62+
invokeCmd.AddCommand(invocationBrowsersCmd)
5163
}
5264

5365
func runInvoke(cmd *cobra.Command, args []string) error {
@@ -70,12 +82,16 @@ func runInvoke(cmd *cobra.Command, args []string) error {
7082
return fmt.Errorf("version cannot be an empty string")
7183
}
7284
isSync, _ := cmd.Flags().GetBool("sync")
85+
asyncTimeout, _ := cmd.Flags().GetInt64("async-timeout")
7386
params := kernel.InvocationNewParams{
7487
AppName: appName,
7588
ActionName: actionName,
7689
Version: version,
7790
Async: kernel.Opt(!isSync),
7891
}
92+
if asyncTimeout > 0 {
93+
params.AsyncTimeoutSeconds = kernel.Opt(asyncTimeout)
94+
}
7995

8096
payloadStr, hasPayload, err := getPayload(cmd)
8197
if err != nil {
@@ -179,21 +195,21 @@ func runInvoke(cmd *cobra.Command, args []string) error {
179195
if err == nil {
180196
fmt.Println(string(bs))
181197
}
182-
// Check for terminal states
183-
if ev.Event == "invocation_state" {
184-
stateEv := ev.AsInvocationState()
185-
status := stateEv.Invocation.Status
186-
if status == string(kernel.InvocationGetResponseStatusSucceeded) {
187-
return nil
198+
// Check for terminal states
199+
if ev.Event == "invocation_state" {
200+
stateEv := ev.AsInvocationState()
201+
status := stateEv.Invocation.Status
202+
if status == string(kernel.InvocationGetResponseStatusSucceeded) {
203+
return nil
204+
}
205+
if status == string(kernel.InvocationGetResponseStatusFailed) {
206+
return fmt.Errorf("invocation failed")
207+
}
188208
}
189-
if status == string(kernel.InvocationGetResponseStatusFailed) {
190-
return fmt.Errorf("invocation failed")
209+
if ev.Event == "error" {
210+
errEv := ev.AsError()
211+
return fmt.Errorf("%s: %s", errEv.Error.Code, errEv.Error.Message)
191212
}
192-
}
193-
if ev.Event == "error" {
194-
errEv := ev.AsError()
195-
return fmt.Errorf("%s: %s", errEv.Error.Code, errEv.Error.Message)
196-
}
197213
continue
198214
}
199215

@@ -428,3 +444,60 @@ func runInvocationHistory(cmd *cobra.Command, args []string) error {
428444
}
429445
return nil
430446
}
447+
448+
func runInvocationBrowsers(cmd *cobra.Command, args []string) error {
449+
client := getKernelClient(cmd)
450+
invocationID := args[0]
451+
output, _ := cmd.Flags().GetString("output")
452+
453+
if output != "" && output != "json" {
454+
return fmt.Errorf("unsupported --output value: use 'json'")
455+
}
456+
457+
resp, err := client.Invocations.ListBrowsers(cmd.Context(), invocationID)
458+
if err != nil {
459+
return util.CleanedUpSdkError{Err: err}
460+
}
461+
462+
if resp == nil {
463+
pterm.Info.Printf("No active browsers found for invocation %s\n", invocationID)
464+
return nil
465+
}
466+
467+
if output == "json" {
468+
if len(resp.Browsers) == 0 {
469+
fmt.Println("[]")
470+
return nil
471+
}
472+
return util.PrintPrettyJSONSlice(resp.Browsers)
473+
}
474+
475+
if len(resp.Browsers) == 0 {
476+
pterm.Info.Printf("No active browsers found for invocation %s\n", invocationID)
477+
return nil
478+
}
479+
480+
table := pterm.TableData{{"Session ID", "Created At", "Headless", "Stealth", "Timeout", "CDP WS URL", "Live View URL"}}
481+
482+
for _, browser := range resp.Browsers {
483+
created := util.FormatLocal(browser.CreatedAt)
484+
liveView := browser.BrowserLiveViewURL
485+
if liveView == "" {
486+
liveView = "-"
487+
}
488+
489+
table = append(table, []string{
490+
browser.SessionID,
491+
created,
492+
fmt.Sprintf("%v", browser.Headless),
493+
fmt.Sprintf("%v", browser.Stealth),
494+
fmt.Sprintf("%d", browser.TimeoutSeconds),
495+
truncateURL(browser.CdpWsURL, 40),
496+
truncateURL(liveView, 40),
497+
})
498+
}
499+
500+
pterm.Info.Printf("Browsers for invocation %s:\n", invocationID)
501+
pterm.DefaultTable.WithHasHeader().WithData(table).Render()
502+
return nil
503+
}

cmd/root.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,11 @@ func isAuthExempt(cmd *cobra.Command) bool {
9090

9191
// Check if the top-level command is in the exempt list
9292
switch topLevel.Name() {
93-
case "login", "logout", "auth", "help", "completion", "create", "mcp", "upgrade":
93+
case "login", "logout", "help", "completion", "create", "mcp", "upgrade":
9494
return true
95+
case "auth":
96+
// Only exempt the auth command itself (status display), not its subcommands
97+
return cmd == topLevel
9598
}
9699

97100
return false
@@ -141,7 +144,6 @@ func init() {
141144
rootCmd.AddCommand(extensionsCmd)
142145
rootCmd.AddCommand(credentialsCmd)
143146
rootCmd.AddCommand(credentialProvidersCmd)
144-
rootCmd.AddCommand(agentsCmd)
145147
rootCmd.AddCommand(createCmd)
146148
rootCmd.AddCommand(mcp.MCPCmd)
147149
rootCmd.AddCommand(upgradeCmd)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ require (
99
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1
1010
github.com/golang-jwt/jwt/v5 v5.2.2
1111
github.com/joho/godotenv v1.5.1
12-
github.com/kernel/kernel-go-sdk v0.28.0
12+
github.com/kernel/kernel-go-sdk v0.33.0
1313
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
1414
github.com/pquerna/otp v1.5.0
1515
github.com/pterm/pterm v0.12.80

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
6666
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
6767
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
6868
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
69-
github.com/kernel/kernel-go-sdk v0.28.0 h1:cvaCWP25UIB5w6oOdQ5J+rVboNGq3VaWYhtmshlPrhg=
70-
github.com/kernel/kernel-go-sdk v0.28.0/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ=
69+
github.com/kernel/kernel-go-sdk v0.33.0 h1:kfk2bwrw3mbR4IW3JMnOj6Tecxor44YjM8YV153xDTY=
70+
github.com/kernel/kernel-go-sdk v0.33.0/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ=
7171
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
7272
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
7373
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=

0 commit comments

Comments
 (0)