Skip to content

feat: name your machines from web settings - #1214

Merged
tiann merged 1 commit into
tiann:mainfrom
hqhq1025:feat/machine-display-name
Jul 29, 2026
Merged

feat: name your machines from web settings#1214
tiann merged 1 commit into
tiann:mainfrom
hqhq1025:feat/machine-display-name

Conversation

@hqhq1025

Copy link
Copy Markdown
Collaborator

Closes #1210

Problem

Machines are labelled by hostname, and there is no way to give one a friendlier name.

MachineMetadataSchema (shared/src/schemas.ts:286) has declared displayName all along, and the entire read path already honours it with the fallback displayName → host → id.slice(0, 8)useMachineLabels.ts:5, MachineSelector.tsx:5, WorkspaceBrowser.tsx:56, telegram/sessionView.ts:99. Nothing could ever write it: the CLI never sends the field, the hub exposed no route that sets it, and the web UI had no editor.

This adds the missing write path. Reads are untouched — they already work.

What's here

  • PATCH /api/machines/:id taking { displayName }, behind the existing requireMachine guard. An empty value removes the key so the label falls back to the hostname; the empty string is never stored. Length is capped at 64 after trimming.
  • machineCache.renameMachine merges that one key into the stored metadata. refreshMachine then publishes machine-updated, which useSSE.ts:275 already invalidates queryKeys.machines on — so every connected client relabels with no new plumbing.
  • /settings/machines, listing online machines with inline rename. Placed between Voice and About so the four existing preference pages keep their order. Each row keeps host · platform visible, so a renamed machine stays identifiable. Offline machines are not listed, because GET /machines returns getOnlineMachinesByNamespace; widening that would affect every other consumer, so it stayed out of scope.

Every file is either new or gains an additive change — 750 insertions, 0 deletions.

Two things worth calling out

The merge reads raw stored metadata, not the cached Machine. That cached view is narrowed by MachineMetadataSchema, which strips unknown keys and collapses to null for a row that fails validation. Both are reachable — the CLI's machine-update-metadata handler (machineHandlers.ts:57-95) accepts metadata: z.unknown() and writes it unvalidated. Merging against the narrowed view would have silently dropped host/platform/workspaceRoots, or any field a newer CLI reports that this hub's schema doesn't know yet. Two regression tests cover it.

The row's save is guarded by a ref, not isPending. Disabling the focused input on submit forces a blur, so Enter reaches save a second time and fires two PATCHes; the second can lose the optimistic-version race and report a failure for a rename that actually succeeded. jsdom doesn't reproduce disable-forces-blur, so there's an explicit test driving that sequence.

mergeMachineMetadata (store/machines.ts:41-46) already preserves hub-side fields when the CLI re-registers — with displayName as its own test fixture — so a reconnect doesn't clobber the name. Covered by a test here too.

Tests

31 new tests: 10 in machineCache.test.ts (merge semantics, clearing, schema-invalid rows, unknown-key preservation, event publication, CLI re-registration), 9 in machines.test.ts (route: trim, clear, over-length, 404/403, 409/500 mapping), 12 in web/.../machines.test.tsx (rendering, editing, blur/Enter/Escape, single-submit, error state, empty state).

Verified against this commit:

  • bun test in hub/: 662 pass, 3 skip, 0 fail
  • bun run test in web/: 1601 pass, 0 fail
  • bun run typecheck in shared/, hub/, web/, cli/: all clean

The web tests were checked for real coverage by mutation — removing the trim, making Escape save, closing the editor on error, and dropping the double-submit guard each fail the corresponding test.

i18n

English and Simplified Chinese strings added for the new page and its nav summary.

Machines are labelled by hostname with no way to give them a friendlier
name. `MachineMetadataSchema` has declared `displayName` all along and the
whole read path already honours it (`displayName → host → id`), but nothing
could ever write it: the CLI never sends the field, the hub exposed no route
that sets it, and the web UI had no editor.

Add the missing write path:

- `PATCH /api/machines/:id` with `{ displayName }`, guarded by the existing
  `requireMachine`. An empty value removes the key so the label falls back to
  the hostname; the empty string is never stored.
- `machineCache.renameMachine` merges that one key into the stored metadata
  and lets `refreshMachine` publish `machine-updated`, which `useSSE` already
  invalidates on — so every connected client relabels without new plumbing.
- A `/settings/machines` page listing online machines with inline rename,
  placed between Voice and About so the existing preference pages keep their
  order. Each row keeps the hostname visible, so a renamed machine is still
  identifiable.

The merge reads the raw stored metadata rather than the cached `Machine`
view. That view is narrowed by `MachineMetadataSchema`, which strips unknown
keys and yields `null` for a row that fails validation — reachable, since the
CLI's `machine-update-metadata` handler accepts `z.unknown()`. Merging
against it would have written those fields out of existence.

The row's save is guarded by a ref rather than `isPending`: disabling the
focused input forces a blur, so Enter otherwise reaches `save` twice and
fires two PATCHes, the second of which can lose the version race and report
a failure for a rename that succeeded.

`mergeMachineMetadata` already preserves hub-side fields on CLI
re-registration, so a reconnect does not clobber the name.

Closes tiann#1210

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings
None.

Summary
Review mode: initial
No issues found in the added/modified lines. Residual risk: tests were not run in this review pass; assessment is based on the GitHub diff plus surrounding source context. The PR includes focused hub cache/route coverage and web editor coverage for the new machine rename flow.

Testing
Not run (automation)

HAPI Bot

@tiann
tiann merged commit 8d1f84e into tiann:main Jul 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: let users set a machine display name (displayName is already wired for reads)

2 participants