Skip to content

Commit 8fa8d32

Browse files
authored
Merge branch 'main' into phani/deploy-from-github-url
2 parents 17df432 + dd88c41 commit 8fa8d32

22 files changed

+1693
-19
lines changed

.goreleaser.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ npms:
4343
repository: "https://github.com/onkernel"
4444
bugs: https://github.com/onkernel/cli/issues
4545
description: Kernel CLI
46-
homepage: https://docs.onkernel.com
46+
homepage: https://onkernel.com/docs
4747
license: MIT
4848
author: "Rafael Garcia <[email protected]>"
4949
access: public
@@ -89,7 +89,7 @@ release:
8989
9090
## What to do next?
9191
92-
- Read the [documentation](https://docs.onkernel.com)
92+
- Read the [documentation](https://onkernel.com/docs)
9393
- Join our [Discord server](https://discord.gg/FBrveQRcud)
9494
- Follow us on Twitter [here](https://twitter.com/rfgarcia) and [here](https://x.com/juecd__)
9595

DEVELOPMENT.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ brew install onkernel/tap/kernel
1313
Install the following tools:
1414

1515
- Go 1.22+ ( https://go.dev/doc/install )
16-
- [Goreleaser](https://goreleaser.com/install/)
16+
- [Goreleaser Pro](https://goreleaser.com/install/#pro) - **IMPORTANT: You must install goreleaser-pro, not the standard version, as this is required for our release process**
1717
- [chglog](https://github.com/goreleaser/chglog)
1818

1919
Compile the CLI:
@@ -60,7 +60,7 @@ A typical workflow we encounter is updating the API and integrating those change
6060

6161
Prerequisites:
6262

63-
- Make sure you have `goreleaser` _pro_ installed via `brew install --cask goreleaser/tap/goreleaser-pro`. You will need a license key (in 1pw), and then `export GORELEASER_KEY=<the key>`.
63+
- Make sure you have **goreleaser-pro** installed via `brew install --cask goreleaser/tap/goreleaser-pro`. You will need a license key (in 1pw), and then `export GORELEASER_KEY=<the key>`. **Note: goreleaser-pro is required, not the standard goreleaser version.**
6464

6565
- Grab the NPM token for our org (in 1pw) and run `npm config set '//registry.npmjs.org/:_authToken'=<the token>`
6666

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
The Kernel CLI is a fast, friendly command‑line interface for Kernel — the platform that provides sandboxed, ready‑to‑use Chrome browsers for browser automations and web agents.
1515

16-
Sign up at [onkernel.com](https://www.onkernel.com/) and read the [docs](https://docs.onkernel.com/introduction).
16+
Sign up at [onkernel.com](https://www.onkernel.com/) and read the [docs](https://onkernel.com/docs/introduction).
1717

1818
## What's Kernel?
1919

@@ -318,15 +318,15 @@ kernel browsers fs list-files my-browser --path "/tmp"
318318

319319
For complete documentation, visit:
320320

321-
- [📖 Documentation](https://docs.onkernel.com)
322-
- [🚀 Quickstart Guide](https://docs.onkernel.com/quickstart)
323-
- [📋 CLI Reference](https://docs.onkernel.com/reference/cli)
321+
- [📖 Documentation](https://onkernel.com/docs)
322+
- [🚀 Quickstart Guide](https://onkernel.com/docs/quickstart)
323+
- [📋 CLI Reference](https://onkernel.com/docs/reference/cli)
324324

325325
## Support
326326

327327
- [Discord Community](https://discord.gg/kernel)
328328
- [GitHub Issues](https://github.com/onkernel/kernel/issues)
329-
- [Documentation](https://docs.onkernel.com)
329+
- [Documentation](https://onkernel.com/docs)
330330

331331
---
332332

cmd/browsers.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ type BrowsersCreateInput struct {
8282
ProfileID string
8383
ProfileName string
8484
ProfileSaveChanges BoolFlag
85+
ProxyID string
8586
}
8687

8788
type BrowsersDeleteInput struct {
@@ -178,6 +179,11 @@ func (b BrowsersCmd) Create(ctx context.Context, in BrowsersCreateInput) error {
178179
}
179180
}
180181

182+
// Add proxy if specified
183+
if in.ProxyID != "" {
184+
params.ProxyID = kernel.Opt(in.ProxyID)
185+
}
186+
181187
browser, err := b.browsers.New(ctx, params)
182188
if err != nil {
183189
return util.CleanedUpSdkError{Err: err}
@@ -1321,6 +1327,7 @@ func init() {
13211327
browsersCreateCmd.Flags().String("profile-id", "", "Profile ID to load into the browser session (mutually exclusive with --profile-name)")
13221328
browsersCreateCmd.Flags().String("profile-name", "", "Profile name to load into the browser session (mutually exclusive with --profile-id)")
13231329
browsersCreateCmd.Flags().Bool("save-changes", false, "If set, save changes back to the profile when the session ends")
1330+
browsersCreateCmd.Flags().String("proxy-id", "", "Proxy ID to use for the browser session")
13241331

13251332
// Add flags for delete command
13261333
browsersDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")
@@ -1346,6 +1353,7 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
13461353
profileID, _ := cmd.Flags().GetString("profile-id")
13471354
profileName, _ := cmd.Flags().GetString("profile-name")
13481355
saveChanges, _ := cmd.Flags().GetBool("save-changes")
1356+
proxyID, _ := cmd.Flags().GetString("proxy-id")
13491357

13501358
in := BrowsersCreateInput{
13511359
PersistenceID: persistenceID,
@@ -1355,6 +1363,7 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
13551363
ProfileID: profileID,
13561364
ProfileName: profileName,
13571365
ProfileSaveChanges: BoolFlag{Set: cmd.Flags().Changed("save-changes"), Value: saveChanges},
1366+
ProxyID: proxyID,
13581367
}
13591368

13601369
svc := client.Browsers

cmd/invoke.go

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"syscall"
1313
"time"
1414

15+
"github.com/onkernel/cli/pkg/util"
1516
"github.com/onkernel/kernel-go-sdk"
1617
"github.com/onkernel/kernel-go-sdk/option"
1718
"github.com/pterm/pterm"
@@ -21,17 +22,31 @@ import (
2122
var invokeCmd = &cobra.Command{
2223
Use: "invoke <app_name> <action_name> [flags]",
2324
Short: "Invoke a deployed Kernel application",
24-
Args: cobra.ExactArgs(2),
2525
RunE: runInvoke,
2626
}
2727

28+
var invocationHistoryCmd = &cobra.Command{
29+
Use: "history",
30+
Short: "Show invocation history",
31+
Args: cobra.NoArgs,
32+
RunE: runInvocationHistory,
33+
}
34+
2835
func init() {
2936
invokeCmd.Flags().StringP("version", "v", "latest", "Specify a version of the app to invoke (optional, defaults to 'latest')")
3037
invokeCmd.Flags().StringP("payload", "p", "", "JSON payload for the invocation (optional)")
3138
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.")
39+
40+
invocationHistoryCmd.Flags().Int("limit", 100, "Max invocations to return (default 100)")
41+
invocationHistoryCmd.Flags().StringP("app", "a", "", "Filter by app name")
42+
invocationHistoryCmd.Flags().String("version", "", "Filter by invocation version")
43+
invokeCmd.AddCommand(invocationHistoryCmd)
3244
}
3345

3446
func runInvoke(cmd *cobra.Command, args []string) error {
47+
if len(args) != 2 {
48+
return fmt.Errorf("requires exactly 2 arguments: <app_name> <action_name>")
49+
}
3550
startTime := time.Now()
3651
client := getKernelClient(cmd)
3752
appName := args[0]
@@ -70,6 +85,8 @@ func runInvoke(cmd *cobra.Command, args []string) error {
7085
if err != nil {
7186
return handleSdkError(err)
7287
}
88+
// Log the invocation ID for user reference
89+
pterm.Info.Printfln("Invocation ID: %s", resp.ID)
7390
// coordinate the cleanup with the polling loop to ensure this is given enough time to run
7491
// before this function returns
7592
cleanupDone := make(chan struct{})
@@ -92,14 +109,24 @@ func runInvoke(cmd *cobra.Command, args []string) error {
92109
return nil
93110
}
94111

95-
// this is a little indirect but we try to fail out of the invocation by deleting the
96-
// underlying browser sessions
112+
// On cancel, mark the invocation as failed via the update endpoint
97113
once := sync.Once{}
98114
onCancel(cmd.Context(), func() {
99115
once.Do(func() {
100116
cleanupStarted.Store(true)
101117
defer close(cleanupDone)
102118
pterm.Warning.Println("Invocation cancelled...cleaning up...")
119+
if _, err := client.Invocations.Update(
120+
context.Background(),
121+
resp.ID,
122+
kernel.InvocationUpdateParams{
123+
Status: kernel.InvocationUpdateParamsStatusFailed,
124+
Output: kernel.Opt(`{"error":"Invocation cancelled by user"}`),
125+
},
126+
option.WithRequestTimeout(30*time.Second),
127+
); err != nil {
128+
pterm.Error.Printf("Failed to mark invocation as failed: %v\n", err)
129+
}
103130
if err := client.Invocations.DeleteBrowsers(context.Background(), resp.ID, option.WithRequestTimeout(30*time.Second)); err != nil {
104131
pterm.Error.Printf("Failed to cancel invocation: %v\n", err)
105132
}
@@ -177,3 +204,90 @@ func printResult(success bool, output string) {
177204
pterm.Error.Printf("Result:\n%s\n", output)
178205
}
179206
}
207+
208+
func runInvocationHistory(cmd *cobra.Command, args []string) error {
209+
client := getKernelClient(cmd)
210+
211+
lim, _ := cmd.Flags().GetInt("limit")
212+
appFilter, _ := cmd.Flags().GetString("app")
213+
versionFilter, _ := cmd.Flags().GetString("version")
214+
215+
// Build parameters for the API call
216+
params := kernel.InvocationListParams{
217+
Limit: kernel.Opt(int64(lim)),
218+
}
219+
220+
// Only add app filter if specified
221+
if appFilter != "" {
222+
params.AppName = kernel.Opt(appFilter)
223+
}
224+
225+
// Only add version filter if specified
226+
if versionFilter != "" {
227+
params.Version = kernel.Opt(versionFilter)
228+
}
229+
230+
// Build debug message based on filters
231+
if appFilter != "" && versionFilter != "" {
232+
pterm.Debug.Printf("Listing invocations for app '%s' version '%s'...\n", appFilter, versionFilter)
233+
} else if appFilter != "" {
234+
pterm.Debug.Printf("Listing invocations for app '%s'...\n", appFilter)
235+
} else if versionFilter != "" {
236+
pterm.Debug.Printf("Listing invocations for version '%s'...\n", versionFilter)
237+
} else {
238+
pterm.Debug.Printf("Listing all invocations...\n")
239+
}
240+
241+
// Make a single API call to get invocations
242+
invocations, err := client.Invocations.List(cmd.Context(), params)
243+
if err != nil {
244+
pterm.Error.Printf("Failed to list invocations: %v\n", err)
245+
return nil
246+
}
247+
248+
table := pterm.TableData{{"Invocation ID", "App Name", "Action", "Version", "Status", "Started At", "Duration", "Output"}}
249+
250+
for _, inv := range invocations.Items {
251+
started := util.FormatLocal(inv.StartedAt)
252+
status := string(inv.Status)
253+
254+
// Calculate duration
255+
var duration string
256+
if !inv.FinishedAt.IsZero() {
257+
dur := inv.FinishedAt.Sub(inv.StartedAt)
258+
duration = dur.Round(time.Millisecond).String()
259+
} else if status == "running" {
260+
dur := time.Since(inv.StartedAt)
261+
duration = dur.Round(time.Second).String() + " (running)"
262+
} else {
263+
duration = "-"
264+
}
265+
266+
// Truncate output for display
267+
output := inv.Output
268+
if len(output) > 50 {
269+
output = output[:47] + "..."
270+
}
271+
if output == "" {
272+
output = "-"
273+
}
274+
275+
table = append(table, []string{
276+
inv.ID,
277+
inv.AppName,
278+
inv.ActionName,
279+
inv.Version,
280+
status,
281+
started,
282+
duration,
283+
output,
284+
})
285+
}
286+
287+
if len(table) == 1 {
288+
pterm.Info.Println("No invocations found.")
289+
} else {
290+
pterm.DefaultTable.WithHasHeader().WithData(table).Render()
291+
}
292+
return nil
293+
}

0 commit comments

Comments
 (0)