Add session name & tags to browsers and acquire (kernel-go-sdk v0.65.0)#177
Merged
Conversation
Mechanical migration required by the SDK bump (no new features). The bump is needed for upcoming browser/acquire name+tags support, but v0.65.0 also ships unrelated breaking changes that the cmd packages must absorb to compile: - All list endpoints are now paginated. Update the List interface signatures and call sites for browser pools, proxies, credential providers, and extensions to take *ListParams and read OffsetPagination.Items. Because the pre-bump List returned the full unpaginated slice, these four commands gain --limit/--offset flags so they can page through results (matching the existing browsers/profiles/api-keys list convention) instead of silently truncating to the first page. - Browser pool update Size is now param.Opt[int64]. - Proxy config unions were renamed (ProxyNewParamsConfigDatacenterProxyConfig → ProxyNewParamsConfigDatacenter, OfProxyNewsConfig… → OfDatacenter, etc.). - The proxy API dropped `carrier` from both request (mobile create) and response configs, so the CLI no longer accepts --carrier or displays Carrier (mobile get/list now surface City/State instead). Mobile create also no longer supports zip/asn; passing them warns and they are ignored. The proxy command docs in README.md are trimmed to match. Tests updated to the new fake List signatures and config shapes, plus limit/offset forwarding tests for the proxy, extension, and credential-provider list commands. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surface the new Kernel API name/tags fields in the CLI, modeled on the profiles command and the hypeman CLI tag convention. browsers: - create: --name and repeatable --tag KEY=VALUE (parsed with a hypeman-style parser that warns on malformed pairs); forwarded to BrowserNewParams. - get/view/update/delete now accept <id-or-name> (SDK resolves either); name and tags are shown in the get detail table, the JSON output, and a new Name column in list. - list: --tag KEY=VALUE filter (deepObject, ANDed) and --query now also matches name. - create --pool-id/--pool-name: --name/--tag now apply to the acquired lease. - Note: name/tags are creation-time only. get/list and JSON responses echo them, but update cannot change them (the SDK BrowserUpdateParams has no name/tags), so update offers no such flags. browser-pools acquire: --name and --tag apply per-lease (cleared on release), forwarded to BrowserPoolAcquireParams and shown in the acquired-session table. The per-lease acquire params (name/tags/timeout) are built by a single shared buildAcquireParams helper used by both `browser-pools acquire` and the `browsers create --pool-*` path, so the two cannot silently diverge. Adds parseKeyValueSpecs/tagsFromFlag/formatTags helpers and tests for create, list, get (incl. JSON output), acquire, pool-list limit/offset forwarding, the buildAcquireParams forwarding contract, the parser, and the malformed-tag warning path. README is updated for the new browser and acquire flags. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Created a monitoring plan for this PR. What this PR does: Adds optional browser session names and tag filtering to the Intended effect:
Risks:
Status updates will be posted automatically on this PR as monitoring progresses. |
4 tasks
Sayan-
approved these changes
Jun 9, 2026
Sayan-
left a comment
Contributor
There was a problem hiding this comment.
thanks for making this easy to review!
2 tasks
4 tasks
bmsaadat
added a commit
that referenced
this pull request
Jun 11, 2026
) ## Summary Bumps `kernel-go-sdk` v0.65.0 → v0.66.0, which adds `Name`/`Tags` to `BrowserUpdateParams` (PATCH /browser), and uses it to give `browsers update <id-or-name>` the `--name` / `--clear-name` / `--tag KEY=VALUE` / `--clear-tags` flags that #177 had left creation-only. The flags mirror the existing `--clear-proxy` convention and honor the SDK semantics exactly (omit = unchanged, empty = clear, tags = full replace), with validation that rejects `--name`/`--clear-name` and `--tag`/`--clear-tags` conflicts, guards against an accidental `--name ""`, and stays correct via a `SetName`/`SetTags` flag signal even when a malformed `--tag` is dropped to an empty map. Also refreshes the now-stale `create --name` help and the `update --tag` cap in help/README, and adds unit tests across the new flags, clear/omit marshaling, and the malformed-tag edge cases. ## Test Plan - [x] `go build ./...`, `go vet ./cmd/`, `gofmt` — clean - [x] `go test ./...` — passing (16 new `TestBrowsersUpdate_*` cases) - [x] Binary exercised end-to-end: `--help` shows flags; every validation path errors before any API call - [x] **Live round-trip verified against a local Kernel** (`localhost:3001`): 18/19 🌐 rows pass, **0 product defects** — full-replace tags, `--name`-only does not clobber tags (omit = unchanged), clear name/tags, by-id/by-name resolution, not-found, JSON echo, name+telemetry, and the 50-tag cap (`Invalid_tags: too many tags: 51 (maximum 50)`). Sessions were cleaned up. The one unrun row, B3 (rename-collision between two **concurrent** active sessions), isn't exercisable on the local stack (it returns `Capacity_exhausted` for a second simultaneous browser); name uniqueness is server-enforced and the CLI only relays the error. ## Manual test matrix **Status:** ✅ = verified locally (path returns before any API call, or pinned by a named unit test asserting the exact forwarded params / JSON) · 🌐 = needs a live Kernel API round-trip (not yet run here). **Notes** - *Error display:* `pterm` capitalizes the first letter (`Cannot specify…`, `--Name requires…`); the raw `fmt.Errorf` strings are lowercase — same error. - *Success output:* the `Name:` / `Tags:` lines echo the **server's returned** values, not the request. Unit tests pin the **request body** (✅); the displayed value is a 🌐 observation — except after a *clear*, where `Name: -` / `Tags: -` is correct regardless. - *Scope:* `disable_default_proxy` (also new in v0.66) is intentionally **not** exposed by this PR. **Preconditions for 🌐 rows:** one session with a known starting name + tags (`browsers create --name mtx-base --tag a=1 --tag team=qa`); D2 needs the session to start with `{a=1}` then run `--tag b=2`; B2/B3 need a second session (`--name mtx-taken`) so a colliding active name exists. ### A. Build / help | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | A1 | Builds & flags wired | `go build ./cmd/kernel` | Builds clean | ✅ | | A2 | Help lists new flags | `browsers update --help` | Shows `--name`, `--clear-name`, `--tag`, `--clear-tags`; the `--tag` line carries the full-replace + "up to 50 pairs" notes | ✅ | | A3 | `Long` help documents ops | `browsers update --help` | Long text lists rename/clear-name and replace/clear-tags, and the "--tag replaces the entire tag set" note | ✅ | ### B. Set name | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | B1 | Set/rename | `browsers update <id> --name new-name` | Request body `{"name":"new-name"}` ✅; output `Updated browser <id>` then `Name: new-name` (value echoed from server response) 🌐 | ✅ (request body unit-tested); 🌐 (displayed value + persistence) | | B2 | Rename resolves by current name | `browsers update old-name --name newer` | Resolves session by name, applies rename | 🌐 | | B3 | Rename to a name used by another active session | `browsers update <id> --name taken` | API rejects: `Conflict: browser session name already exists` | 🌐 | ### C. Clear name | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | C1 | Clear name | `browsers update <id> --clear-name` | Request body `{"name":""}`; output `Name: -` | ✅ (request body unit-tested); 🌐 (persisted: get shows no name) | | C2 | `--name ""` rejected (use --clear-name) | `browsers update <id> --name ""` | Error: `--name requires a non-empty value; use --clear-name to clear the name` | ✅ | | C3 | `--name=` (explicit empty) rejected | `browsers update <id> --name=` | Same error as C2 | ✅ | ### D. Set tags (full replace) | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | D1 | Replace tag set | `browsers update <id> --tag team=backend --tag env=prod` | Request body `{"tags":{"team":"backend","env":"prod"}}` ✅; output `Tags: env=prod, team=backend` (sorted; echoed from server response) 🌐 | ✅ (request body unit-tested); 🌐 (displayed value + persistence) | | D2 | Full-replace semantics (not merge) | Session has `{a=1}`, run `--tag b=2` | Resulting tags are `{b=2}` only — `a` is gone | 🌐 | | D3 | Special-char tag key round-trips | `browsers update <id> --tag region.us=1` | Body contains `"region.us":"1"` | ✅ (parser unit-tested); 🌐 (persisted) | | D4 | 50-tag cap | `--tag` ×51 | API rejects over-limit (server-enforced) | 🌐 | | D5 | Tag value containing `=` | `browsers update <id> --tag url=a=b` | Splits on first `=` only → body `{"tags":{"url":"a=b"}}` (not dropped as malformed) | ✅ (parser unit-tested: `k=v=w`→`k:"v=w"`); 🌐 (persisted) | ### E. Clear tags | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | E1 | Clear all tags | `browsers update <id> --clear-tags` | Body `{"tags":{}}`; output `Tags: -` | ✅ (request body unit-tested); 🌐 (persisted: get shows no tags) | ### F. Combined name + tags | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | F1 | Set name + set tags together | `browsers update <id> --name combo --tag k=v` | Body has both `"name":"combo"` and `"tags":{"k":"v"}` | ✅ (unit-tested); 🌐 (persisted) | | F2 | Clear name + set tags | `browsers update <id> --clear-name --tag env=prod` | Body `{"name":"","tags":{"env":"prod"}}` | ✅ (unit-tested) | | F3 | Set name + clear tags | `browsers update <id> --name renamed --clear-tags` | Body `{"name":"renamed","tags":{}}` | ✅ (unit-tested) | ### G. Combine with existing update flags | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | G1 | Name/tags + proxy in one call | `browsers update <id> --proxy-id <pid> --name combo --tag k=v` | All three forwarded: `proxy_id`, `name`, `tags` | ✅ (unit-tested); 🌐 (persisted) | | G2 | Name + telemetry | `browsers update <id> --name n --telemetry=all` | Name applied and telemetry summary printed | 🌐 | | G3 | Partial update does NOT clobber unrelated fields | Session has tags + proxy; run `--name` only | Only name changes; tags & proxy untouched (omit = unchanged) | ✅ (omit-not-sent unit-tested); 🌐 (persisted) | ### H. Conflict & "at least one" validation | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | H1 | `--name` + `--clear-name` | `browsers update <id> --name x --clear-name` | Error: `cannot specify both --name and --clear-name` | ✅ | | H2 | `--tag` + `--clear-tags` | `browsers update <id> --tag a=1 --clear-tags` | Error: `cannot specify both --tag and --clear-tags` | ✅ | | H3 | No options at all | `browsers update <id>` | Error: `must specify at least one of: …` (full list of 10 flags, ending `--name, --clear-name, --tag, or --clear-tags`) — substring check | ✅ | | H4 | Name-only satisfies "at least one" | `browsers update <id> --name x` | Proceeds (no "at least one" error) | ✅ | | H5 | Tags-only satisfies "at least one" | `browsers update <id> --tag k=v` | Proceeds | ✅ (param path); 🌐 (persisted) | ### I. Malformed-tag edge cases | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | I1 | All `--tag` values malformed, alone | `browsers update <id> --tag foo` | Warns `Ignoring malformed tag: foo`, then error `no valid --tag KEY=VALUE pairs provided` (not the generic "at least one") | ✅ | | I2 | Malformed `--tag` + `--clear-tags` still conflicts | `browsers update <id> --tag foo --clear-tags` | Warns, then error `cannot specify both --tag and --clear-tags` (does NOT silently clear) | ✅ | | I3 | Partially malformed list warns & continues (documented sharp edge) | `browsers update <id> --tag a=1 --tag bad` | Warns on `bad`, replaces tag set with `{a=1}` only | ✅ (parser unit-tested); 🌐 (persisted) | ### J. ID-vs-name resolution & errors | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | J1 | Update by ID | `browsers update <cuid> --name n` | Resolves by ID | 🌐 | | J2 | Update by name | `browsers update <name> --name n2` | Resolves by name | 🌐 | | J3 | Not found | `browsers update nonexistent --name n` | Clean not-found error | 🌐 | ### K. JSON output | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | K1 | JSON echoes persisted name/tags | `browsers update <id> --name n --tag k=v -o json` | Prints full `BrowserUpdateResponse` JSON including `name` and `tags` | 🌐 | | K2 | `-o yaml` rejected | `browsers update <id> --name n -o yaml` | Error: `unsupported --output value "yaml"; use "json"…` | ✅ | ### L. Round-trip / persistence | # | Scenario | Command | Expected | Status | |---|----------|---------|----------|--------| | L1 | `get` reflects rename | update `--name n` → `browsers get <id>` | Detail table + JSON show new name | 🌐 | | L2 | `get` reflects tag replace | update `--tag k=v` → `browsers get <id>` | Shows exactly the new tag set | 🌐 | | L3 | `get`/`list` reflect clears | `--clear-name`/`--clear-tags` → `get` & `list --query` | No name / no tags after clear; `list --tag` no longer matches | 🌐 | | L4 | `list --tag` filter after replace | replace tags → `browsers list --tag k=v` | Session appears under the new tag, not the old | 🌐 | | L5 | `list` Name column after rename | `--name newname` → `browsers list` | Name column shows `newname` for that session | 🌐 | > Display asymmetry (L1–L3): `update`'s success output prints `Name: -` / `Tags: -` after a clear, but `get`'s detail table **omits** the Name/Tags rows entirely when empty. Both mean "no name / no tags" — a missing `get` row is not a failure. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > CLI-only changes to browser session metadata with local validation and tests; API behavior is delegated to the bumped SDK. > > **Overview** > Bumps **kernel-go-sdk** to **v0.66.0** and wires `browsers update` to the new PATCH fields for session **name** and **tags**, so renaming and retagging no longer require recreating a session. > > `kernel browsers update` gains **`--name` / `--clear-name`** and **`--tag` / `--clear-tags`**, following the same omit-vs-clear pattern as **`--clear-proxy`**: fields are omitted when unchanged, empty values clear, and **`--tag` fully replaces** the tag set (not a merge). Client-side validation rejects conflicting flag pairs, empty **`--name`**, and malformed-only **`--tag`** input (using **`SetName` / `SetTags`** so edge cases still fail before the API). Success output echoes **Name** / **Tags** when those were updated; README and **`create --name`** help note that names can be changed via update. > > Adds broad **`TestBrowsersUpdate_*`** coverage for param forwarding, JSON marshaling, and validation edge cases. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit feb115c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
nameandtagssupport to the browser commands —browsers create/get/update/delete/listandbrowser-pools acquire— modeled on the existingprofilesCLI and the hypeman--tag KEY=VALUEconvention: create/acquire set them, get/list/JSON echo them,listgains a--tagfilter plus a Name column, and lookups now accept<id-or-name>. This requires bumpingkernel-go-sdkv0.58.0 → v0.65.0, whose unrelated breaking changes are absorbed in a separate first commit — all list endpoints became paginated (sobrowser-pools/proxies/credential-providers/extensionslist now read pages and gain--limit/--offsetflags), proxy config unions were renamed, and the proxy API droppedcarrierentirely. The PR is two clean commits — migration first, then feature — and adds unit tests across the new flags, the tag parser, and the shared acquire-params helper. Note one user-visible change:kernel proxies create --carrieris no longer accepted (mobile create also no longer takeszip/asn), because the upstream API removed mobile carrier selection.Note
Medium Risk
SDK bump changes list/pagination and proxy create semantics (dropped
--carrier); behavior is covered by tests but users with mobile carrier scripts will break.Overview
Bumps kernel-go-sdk to v0.65.0 and adds browser session
nameandtagsacross the CLI:browsers create/ pool acquire accept--nameand repeatable--tag KEY=VALUE;listfilters with--tag, shows a Name column, and broadens--query; get/update/delete/view take<id-or-name>. Pool leases sharebuildAcquireParamssobrowser-pools acquireandbrowsers create --pool-*forward name/tags the same way.The SDK upgrade also drives paginated list handling for browser-pools, proxies, extensions, and credential-providers (
--limit/--offset), renamed proxy config unions, and removal of mobile--carrier(plus warnings when--zip/--asnare passed for mobile). README and unit tests cover the new flags, tag parsing, and acquire helper.Reviewed by Cursor Bugbot for commit 188bbf9. Bugbot is set up for automated code reviews on this repo. Configure here.
Manual testing
Tested end-to-end against a locally running Kernel API (
KERNEL_BASE_URL=http://localhost:3001) using the CLI built from this branch — creating, querying, and deleting real browsers, pools, and proxies and asserting both the CLI output and the request params forwarded to the API. All scenarios below passed locally; every test resource was cleaned up afterward.createexposes--name/--tag;listexposes--query/--tag/--limit/--offset;acquireexposes--name/--tag; all four migrated list commands expose--limit/--offsetget/view/update/deleteshow<id-or-name>;updateno-arg error anddeleteusage corrected--name+ repeatable--tag→ name and sorted tags in table and JSONConflict: browser session name already exists)region.us=1) round-tripsview -o json→{liveViewUrl}; not-found handled-o jsonechoes name/tags; no--name/--tagflags (creation-only)--tagdeepObject filter; multiple--tagANDed;--querymatches name; JSON carries name/tags--name/--tagapplied per-lease and echoed; cleared on release--pool-name --name --tagforwards name/tags to the acquired lease; both-pool-flags rejected--zip/--asnwarns and still creates; residential keeps zip/asn;--carrierflag gone, no Carrier in get/list/check--limit/--offseton browsers / pools / proxies listproxies create --carrier→Unknown flag;-o yamlrejected;app listunaffectedEnvironment notes (not CLI issues): the local stack throttles rapid successive creates (spaced requests succeed) and does not enforce the documented 50-tag max; custom-proxy create requires a reachable proxy (the server connection-tests it). The
create --pool-* --stealthconflict still uses an interactive confirm that blocks under non-TTY — pre-existing behavior, worth a follow-up. The ">20 items, no silent truncation" case wasn't exercised (would need many live browsers); the pagination mechanism itself was validated via--limit/--offset.🤖 Generated with Claude Code