feat(cli): put ls/list on the hub /api/v1 boxes endpoint (thin CLI Step 3) - #281
Conversation
…ep 3)
agentbox list now reads GET /api/v1/boxes only (via fetchBoxListing) and
renders straight off the enriched HubApiBox view model — no /admin/store
merge path. The Box payload gains the adoption/reconstruction fields cloud
boxes need (sandboxId, originUrl, publicHost, image, webPort, previewUrls,
lastAgent, topology) plus branch/shellCount, so Steps 4 and 7 can rebuild a
BoxRecord and read endpoints from the payload alone.
--live moves server-side as GET /api/v1/boxes?live=1, mirroring the
providers?freshness=1 opt-in-expensive pattern: the in-process host topology
runs an authoritative provider.probeState per cloud box (docker skipped,
4s/box timeout, best-effort) before mapping. The persisted-state plane
ignores it.
The offline fallback (~/.agentbox/hub-boxes-cache.json) is re-keyed onto the
API { boxes } payload. hub-merge.ts / list-merged.ts / fetchHubListing survive
because dashboard.ts still consumes them; deleting them and converting the
dashboard is deferred to Step 7/11 (noted in the plan doc). fetchHubListing's
cache was repointed to hub-registrations-cache.json to avoid a schema clash.
Claude-Session: https://claude.ai/code/session_01QcvgzAktjnWXx1hmieqeTe
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Bugbot #281: after switching ls to HubApiBox, stateCell fell back to `state ?? status` but agentSummary and cmuxStatusCell gated on the host-only `state` alone. A read-only Postgres/plane box carries `status: 'running'` with no `state`, so it read `running` in STATE yet `-` in AGENT and `[running]` in the cmux glyph. Route all three through a shared effectiveState() helper so a plane box renders its agent, while `state` still wins for paused/stopped boxes and job rows still bracket their creating/error status. Claude-Session: https://claude.ai/code/session_01QcvgzAktjnWXx1hmieqeTe
|
bugbot run |
Bugbot #281 (High): project-scoped `ls` on a thin client silently dropped every control-box-created box. The filter matched originUrl only when projectRoot was absent, but host mapBox rows carry the hub's absolute projectRoot (meaningless on the laptop) and so never matched the local root. Extract the scope predicate as boxInProject and match by repo identity (originUrl) for a box with no local projectRoot OR any box when the hub is remote, while keeping folder-based scoping locally so two clones of one repo stay apart. mapBox now threads each box's registration originUrl onto the payload (was cloud-only). Unit-tested via boxInProject. Residual noted for Step 4: registerBoxWithRelay doesn't send originUrl for plain docker/direct-cloud creates (only --via-hub does), so capturing it at registration across providers is the cross-step half of this fix. Claude-Session: https://claude.ai/code/session_01QcvgzAktjnWXx1hmieqeTe
|
Addressed both Bugbot findings. 1. "Missing state breaks agent display" (Medium) — fixed in 2. "Thin client project scope broken" (High) — fixed in
Residual (genuinely cross-step, noted in |
|
bugbot run |
Bugbot #281 (Medium): the round-2 scope fix keyed "cross-machine" off resolveHubTarget().mode === 'remote', but `agentbox hub expose` is remote-MODE reached over loopback on the SAME machine — its boxes keep local projectRoots. So origin-only matching wrongly folded boxes from another local clone of the same repo into the current project's listing. Add a pure isCrossMachineHub(target): remote-mode AND a non-loopback URL host. A local hub and an exposed loopback hub both scope by folder; only a configured remote control box scopes by repo identity. Unit-tested. Claude-Session: https://claude.ai/code/session_01QcvgzAktjnWXx1hmieqeTe
|
Re the re-reported "Missing state breaks agent display" on |
|
bugbot run |
Bugbot #281 (Medium): keying "cross-machine" off the hub URL host can't tell a same-machine `hub expose` (loopback) from a genuinely remote hub reached via an SSH tunnel to 127.0.0.1 — the two are indistinguishable by URL, and whichever way the heuristic leans breaks the other (drop a remote hub's boxes, or leak a local clone's). Replace the per-hub URL heuristic with a per-box filesystem probe: boxInProject scopes by originUrl only when the box's projectRoot names no directory on this machine (existsSync) — that path is a remote hub's own, so folder matching would drop it; a local folder (local hub OR exposed loopback hub) does exist and scopes by folder, keeping two same-origin clones apart. This one signal disambiguates both loopback cases the URL cannot. Drops isCrossMachineHub; boxInProject tests updated. Claude-Session: https://claude.ai/code/session_01QcvgzAktjnWXx1hmieqeTe
|
The re-reported "Missing state breaks agent display" remains a stale anchor — Note: the |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 83c74fa. Configure here.
Step 3 — Listing (
ls/list) onto/api/v1Converts
agentbox ls/listto a pure/api/v1REST client, perdocs/hub-api-single-path-plan.md.What changed
agentbox listreadsGET /api/v1/boxesonly (viafetchBoxListing) and renders straight off the enrichedHubApiBoxview model — no more client-side/admin/storemerge. Same wire the tray and web UI read, in both local-hub and remote-control-box modes.apps/hub/lib/boxes/types.ts+hub-api-client.ts) with the adoption/reconstruction fields cloud boxes need —sandboxId,originUrl,publicHost,image,webPort,previewUrls,lastAgent,topology, plusbranch/shellCount. These are on the critical path for Step 4 (registrationToBoxRecordrebuild) and Step 7 (urlreads endpoints off the payload); the exact field map + what stays a re-minted secret is documented in the plan doc's "Notes for later steps".--livemoves server-side asGET /api/v1/boxes?live=1, mirroring the establishedGET /api/v1/providers?freshness=1opt-in-expensive pattern: on the in-process host topology the hub runs an authoritativeprovider.probeStateper cloud box (docker skipped, 4s/box timeout, best-effort). The hub is now the only place holding provider credentials, so a persistedlastStatethat lies (platform-side stop) can still be corrected — dropping the flag would have been a real regression.{ boxes }payload (~/.agentbox/hub-boxes-cache.json).lsstill prints the last listing (marked stale) when the hub is unreachable.Deferred (noted in the plan doc)
hub-merge.ts/list-merged.ts/hub-list.ts'sfetchHubListing(the/admin/storeadmin path) were not deleted —commands/dashboard.ts(an IO-plane TUI, outside Step 3's file set) still consumes them at runtime. Deleting them + converting the dashboard is Step 7/11 cleanup.fetchHubListing's cache was repointed tohub-registrations-cache.jsonto avoid a schema clash with the new API cache.Verification
pnpm typecheckgreen (31/31); hub suite (79) + CLI suite (1099) + newbox-listing.test.ts(4) + rewrittenlist-cmux.test.tsgreen.job:(creating) box:lsandls -grender correctly, project-scoping groups right, cloud rows carry the enriched fields (sandboxId/image/webPort/previewUrls/topology/branch) verified straight off the/api/v1/boxesresponse body.relay.controlPlaneUrl+AGENTBOX_HUB_API_KEY): identical output — same code, both modes.ls -gprints all boxes from cache withhub unreachable — showing boxes as of just now.https://claude.ai/code/session_01QcvgzAktjnWXx1hmieqeTe
Note
Medium Risk
Listing and project scoping now depend entirely on hub API shape and
originUrl(still missing for many non–via-hub creates);--liveadds hub-side provider probes with timeouts but wrong persisted state could still show until probed.Overview
agentbox ls/listis now a thin hub client: it fetches a singleGET /api/v1/boxeslisting viafetchBoxListingand rendersHubApiBoxrows. The old client-side merge of local docker state with/admin/storeregistrations (list-merged,hub-merge,on hub/orphanqualifiers) is gone from this command; legacy admin listing remains only fordashboard.ts.The hub Box API payload is expanded so the CLI can drive the table and dock without local probes:
webUrl/vncUrl, agent activity and session titles,shellCount, and a non-secret adoption/reconstruction block (sandboxId,originUrl,publicHost,image,webPort,previewUrls,lastAgent,topology) for Step 4.--liveis now?live=1on the hub (cloud SDKprobeStateper box on the in-process backend; Postgres plane ignores it).Project-scoped
lsuses exportedboxInProject: folder match onprojectRoot, ororiginUrlwhen the box path is absent or foreign (existsSyncdisambiguates local clones vs remote hub paths). Offline behavior keepshub-boxes-cache.jsonon{ boxes }, withno-tokenmessaging for remote hubs missingAGENTBOX_HUB_API_KEY; admin registrations cache moved tohub-registrations-cache.json.Reviewed by Cursor Bugbot for commit 83c74fa. Configure here.