feat(workspace): offer to install the engine a bound workspace needs

Draft
RA
2mo ago
+1150-11

Issue for this PR

Closes #1157

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

A bound workspace whose declared tools need the local engine used to report a missing engine as a 10-second toast with a command in it. This replaces that with an offer: Install now / Copy command / Not now. The install only ever runs from an explicit choice — attach still never installs anything on its own.

The part worth reviewing is how the offer reaches the TUI. I first registered a handler with the attach module from the plugin. It typechecked, unit-tested green, and did nothing in a real TUI — the user got the old toast, never the dialog. The plugin runtime loads plugins in a separate module realm, so the instance the plugin imports is not the one attach consults; a globalThis key failed likewise. The offer is therefore published on the event bus, which is what toasts already use, and since that event carries no payload the plugin re-derives the detail itself.

Deliberate details:

  • One select for every phase, with sentinel rows for installing/installed/failed. Swapping the top-level component between states drops focus and loses the phase signal, and sentinel rows avoid the disabled flag, which the filter drops.
  • Install now only appears when Node 20+ is on PATH; otherwise the command is shown with the reason it cannot be run for you.
  • Copy command reads the clipboard back before claiming success. The host's writer swallows backend failures, so a resolved write is not proof — on a box with no xclip it reported a copy that never happened.
  • Not now latches 7 days, keyed on the workspace id so a rename does not reset it, scoped by tenant and API URL.
  • Headless run prints one line to stderr instead, since a toast cannot render there and stdout is reserved for JSON events.
  • Both too-old exits route through the offer, including a reused entry below the floor.
  • The install has a real deadline, reports its result even if the dialog was dismissed while npm ran, and only one offer is raised at a time — attach re-probes each turn, and a second dialog could otherwise replace an installing one and start a concurrent global install.
  • A zero exit from npm is not trusted: the installed engine must be discoverable and clear the version floor, since npm's global prefix bin directory need not be on PATH. Install now also requires npm itself, not just Node.
  • On attach <url> the plugin runs on the client while the workspace is the server's, so the offer refuses to act there and points at the server instead; an attached run renders the notice in its own event loop.

How did you verify your code works?

Unit 309 pass / 0 fail on the workspace and plugin suites; full suite green apart from one failure already red on the base. Typecheck clean, lint at baseline on every file touched.

E2E against a real bound workspace with the engine absent from PATH: dialog with all three options and a live declared count; Install now installs into an isolated prefix and tools appear on the next message in the same session; Not now suppresses, and returns once the latch is aged past 7 days; no Node hides Install now; headless prints exactly one line across 3 identical runs.

Not verified: the successful clipboard path — this host has no clipboard backend, so only the "could not confirm" branch ran. Not verified: Windows — the npm shim fix follows existing precedent in this repo rather than a test, and deserves a check before release.

Round-by-round review log (five rounds, seven findings — six fixed here, one routed to the PR below; three were regressions from my own earlier fixes), the second capture, and detailed evidence: see the "Codex review log" comment on this PR.

Screenshots / recordings

Terminal UI, so this is captured pane output rather than an image. Workspace name redacted.

  ┃
  ┃  hi                                                              Workspace "Integrations Demo" declares esc
  ┃                                                                  52 integration tools, which need the local engine
                                                                     · npm i -g @altimateai/datamate@0.7.0
     ▣  Builder · Altimate LLM Gateway
                                                                     Search
                                                                   ● Install now Runs npm i -g @altimateai/datamate@0.7.0
                                                                     Copy command Copy the install command to your clipbo
                                                                     Not now Won't ask again for this workspace for 7 day

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR