feat(lab-envs): UI for backend-agnostic ephemeral lab envs - #2
feat(lab-envs): UI for backend-agnostic ephemeral lab envs#2devin-ai-integration[bot] wants to merge 1 commit into
Conversation
Adds a /lab/envs page that lists, launches, extends, and archives ephemeral JupyterLab environments against the backend-agnostic /api/v1/lab/envs API introduced in concave. - types.ts: LabEnv, LabStorage, LabEnvsResponse types. - lib/labEnvs.ts: API helpers + countdown formatting. - views/LabEnvsView.vue: launch drawer, env list with live TTL countdown, per-row extend/archive/open-jupyter actions, admin-only storage and driver configuration panels. - router + AppShell: new Lab Envs nav entry. - test/labEnvs.test.ts: unit tests for countdown/remaining helpers. Role gating matches the server: any viewer can list, developers can launch and extend, operators can archive-now, admins can reconfigure storage tiers and the active driver. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| storageForm.value = { ...payload.storage } | ||
| activeDriverForm.value = payload.active ?? '' |
There was a problem hiding this comment.
🟡 Background poll overwrites admin configuration forms every 15 seconds
The refresh() function at lines 56-57 unconditionally overwrites storageForm and activeDriverForm with fresh server data. Since refresh() is called every 15 seconds by the poll timer (frontend/src/views/LabEnvsView.vue:135), any in-progress edits an admin makes to the "Storage tiers" or "Active driver" forms will be silently discarded. The admin may not notice the reset and either give up or accidentally submit stale values.
Prompt for agents
In LabEnvsView.vue, the refresh() function (called on mount and every 15s via setInterval) overwrites storageForm and activeDriverForm with server data on every call (lines 56-57). This destroys any in-progress admin edits in the Storage tiers and Active driver forms.
Approach: Only populate storageForm and activeDriverForm on the initial load, not on subsequent poll refreshes. One way is to add a boolean flag (e.g. initialized) that is false initially and set to true after the first successful refresh. On subsequent refreshes, skip the lines that overwrite storageForm and activeDriverForm. Alternatively, separate the initial data fetch from the poll refresh logic so that display-only data (envs, storage, drivers, activeDriver) is updated on poll, but the form-bound refs (storageForm, activeDriverForm) are only set once on mount.
Was this helpful? React with 👍 or 👎 to provide feedback.
| image: form.value.image, | ||
| display_name: form.value.display_name || undefined, | ||
| driver: form.value.driver || undefined, | ||
| gpus: form.value.gpus || undefined, |
There was a problem hiding this comment.
🟡 gpus: 0 silently converted to undefined due to falsy OR check
At line 77, gpus: form.value.gpus || undefined uses a falsy check to omit the field. Since 0 is falsy in JavaScript, explicitly requesting 0 GPUs (the form's default value, set at frontend/src/views/LabEnvsView.vue:33) results in gpus being omitted from the JSON payload. This means the server never receives an explicit gpus: 0 — it receives no gpus field at all, and will apply its own default, which may not be 0.
| gpus: form.value.gpus || undefined, | |
| gpus: form.value.gpus != null && form.value.gpus > 0 ? form.value.gpus : undefined, |
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Adds the web surface for the backend-agnostic ephemeral lab env API introduced in concave#1. New route:
/lab/envs. Lists, launches, extends, and archives ephemeral JupyterLab environments (Docker today; Slurm/Proxmox drivers plug into the same UI in follow-ups).Changes
src/types.ts:LabEnv,LabStorage,LabEnvsResponsetypes.src/lib/labEnvs.ts: API helpers (fetchLabEnvs,launchLabEnv,extendLabEnv,archiveLabEnv,updateLabStorage,setActiveDriver) and countdown helpers.src/views/LabEnvsView.vue:+1h TTLbutton (developer+),Archive nowbutton (operator+).src/router/index.ts:/lab/envsroute, viewer-gated.src/components/layout/AppShell.vue: nav entry between Gradient Lab and Teams.test/labEnvs.test.ts: unit tests forformatCountdownandremainingSeconds.Gradient-specific touches
Review & Testing Checklist for Human
concave serve+concave web). Confirm/lab/envsrenders,Launchspawns a Docker container, and the TTL countdown ticks down in real time.archivedand the tarball + manifest show up under the configured cold tier.Notes
peer_id + env_idto restore an archived env from a mesh peer. Deferred until the Slurm driver PR (#4).Link to Devin session: https://app.devin.ai/sessions/5d19efa113054ca4953d9ed9309ce705
Requested by: @ElFariss