qwen-code/packages/web-shell/client/utils
jinye 2523a36b52
feat(web-shell): workspace management sidebar with dynamic registration (daemon multi-workspace phase 4) (#6625)
* feat(web-shell): add workspace picker for new sessions (issue #6378 phase 4)

Multi-workspace daemons now show a new-session workspace picker in the sidebar (default primary, untrusted disabled); the chosen workspace cwd is sent on POST /session so the session spawns in that workspace. daemon-react-sdk createSession gains an optional per-call workspaceCwd override covering both the detached and active-session paths; omitting it preserves the previous primary behavior.

* feat(web-shell): workspace management with dynamic registration

Replace the new-session workspace picker with a full workspace
management sidebar. Registered workspaces render as a parallel,
collapsible list (folder icon per workspace), each with its own
sessions nested underneath, and a "+" entry registers an existing
directory as a new workspace at runtime with no daemon restart.

Backend: WorkspaceRegistry becomes mutable (add()/onChange()); a new
POST /workspaces route validates the directory (exists, not a
duplicate, not nested) and registers it; run-qwen-serve exposes a
runtime factory that builds a complete workspace runtime (bridge, fs
factory, channel factory, workspace service) on demand. The SDK
DaemonClient and daemon-react-sdk gain addWorkspace().

* fix(web-shell): show newly registered workspace without a reload

Registering a workspace via the sidebar "+" left the list unchanged
until a full page reload. handleAddWorkspace called
workspace.getCapabilities(), which returns a cached promise and only
feeds setCapabilities from the mount effect, so the refresh was a no-op.

Add DaemonWorkspaceProvider.refreshCapabilities(): it bypasses the
promise cache, issues a fresh /capabilities fetch, and pushes the
result into state so consumers re-render. handleAddWorkspace now awaits
it (best-effort, so a refresh failure never masks a successful
registration).

* fix(web-shell): address review feedback for workspace management

- registry: list() returns a frozen snapshot so callers can't mutate the
  internal runtimes array (restores the push()-throws invariant)
- POST /workspaces: reject relative paths on the raw input, canonicalize
  via realpath so symlink aliases can't bypass the duplicate/nesting
  checks, and serialize concurrent registrations to close a TOCTOU race
  that leaked bridge/channel infrastructure
- sidebar: restore a compact single-workspace project header (name,
  search toggle, collapse) so single-workspace users keep those
  affordances and searchOpen/projectExpanded are no longer dead
- daemon session: include the target workspace in the create-session
  failure message
- tests: rework WebShellSidebar tests for the WorkspaceSection UI (add
  the useWorkspace mock, query workspace buttons, cover primary->undefined),
  use the canonical DaemonWorkspaceCapability type, and add a createSession
  workspaceCwd forwarding test

* fix(cli): harden dynamic workspace registration per review

- POST /workspaces: bound cwd by MAX_WORKSPACE_PATH_LENGTH before any
  filesystem work, and return a generic 500 (log the full error to
  stderr) so responses can't leak internal filesystem paths
- createDynamicWorkspaceRuntime: log a stderr warning when a workspace's
  settings can't be read, matching the startup secondary-workspace path

* qwen: address PR review feedback (#6625)

Dynamic workspace reloadDaemonEnv now mirrors the startup secondary path:
after reloadEnvironment() it rebuilds the runtime env via
buildRuntimeEnvironment(), calls wsEnv.replace(), and updates the env
metadata (envFileReadFailed / envFileReadFailures / overlayKeys /
envFilePaths). Without this, .env changes on a dynamically registered
workspace never propagated to that workspace's spawned child processes.

* qwen: address PR review feedback (#6625)

Harden POST /workspaces and the workspace registry per review:
- canonicalize with realpathSync.native (matches startup) so the same
  physical dir on a case-insensitive FS can't register twice
- nesting guard now also checks in-flight registrations, closing a
  concurrent parent/child registration race
- error responses no longer echo resolved/other-workspace paths
- registry add() isolates onChange listener throws so a bad listener
  can't abort a caller after the workspace is already committed

* qwen: address PR review feedback (#6625)

- POST /workspaces: cap total registered workspaces (startup + dynamic)
  to guard against unbounded registration exhausting resources
- createDynamicWorkspaceRuntime: register shutdown-cleanup arrays only
  after the runtime is fully built, so a throw during workspace-service
  construction can't orphan the bridge/channel
- web-shell App: reset selectedWorkspaceCwd after session creation so the
  workspace picker is one-shot (next new chat defaults to primary)

* qwen: address human review suggestions (batch 1)

- WorkspaceSection: add console.warn on session-poll failure (was silent)
- WorkspaceSection: add aria-expanded for screen readers
- AddWorkspaceDialog: associate label/input (htmlFor/id), i18n the
  absolute-path error, accept Windows drive-letter paths
- i18n: remove unused workspaceUntrustedHint key, add addWorkspaceAbsError

* qwen: address human review suggestions (batch 2)

- Remove dead CSS (.workspacePickerSelect, .workspaceItem* classes from
  the old select-based picker, replaced by WorkspaceSection)
- Add title tooltip to single-workspace project name (shows full path)
- WorkspaceSection: sync expanded state on workspace.primary change

* qwen: address human review suggestions (batch 3)

- DaemonWorkspaceProvider: refreshCapabilities now clears error on
  success and sets error+status on failure (was incomplete vs mount)
- Remove unused onChange/WorkspaceRegistryEvent from workspace registry
  per simplicity-first (no consumer exists; defers API surface until
  a real subscriber like SSE push is needed)

* qwen: add workspace-management route test coverage

Tests cover: 501 (no factory), 400 (missing/empty/relative/long/
nonexistent cwd), 409 (duplicate canonical path), 201 (success),
and verifying error messages are generic (no path leak).

* qwen: fix CI build failure — add explicit types in route test

The CLI's tsconfig includes test files in tsc --build, so all
noImplicitAny violations in tests cause build failures. Add explicit
type annotations to mock parameters.

* qwen: add type/title to single-workspace add-button

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-10 16:30:20 +00:00
..
ansi.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
askUserPermission.test.ts feat(tui): Ctrl+O frozen transcript view and unified tool output rendering (#5666) 2026-07-09 23:40:29 +00:00
askUserPermission.ts feat(web-shell): add Session Overview panel and in-window split view (#6400) 2026-07-07 02:44:18 +00:00
commandDisplay.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
composerInputState.test.ts fix(web-shell): improve disconnected composer handling (#6166) 2026-07-02 07:05:55 +00:00
composerInputState.ts fix(web-shell): improve disconnected composer handling (#6166) 2026-07-02 07:05:55 +00:00
composerModels.ts feat(web-shell): restore the full composer in split-view panes (#6510) 2026-07-08 07:01:36 +00:00
copyCommand.test.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
copyCommand.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
cssUrlVar.test.ts feat(web-shell): support icon chips for mention tags (#6337) 2026-07-05 13:25:32 +00:00
cssUrlVar.ts feat(web-shell): support icon chips for mention tags (#6337) 2026-07-05 13:25:32 +00:00
dialogLabels.ts refactor(web-shell): restructure chat UI (#5775) 2026-06-23 22:43:19 +08:00
dom.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
escapeIntent.test.ts feat(web-shell): friendlier Esc interruption + queued-prompt UX (#6025) 2026-06-30 01:27:16 +00:00
escapeIntent.ts feat(web-shell): friendlier Esc interruption + queued-prompt UX (#6025) 2026-06-30 01:27:16 +00:00
formatRelativeTime.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
formatRuntime.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
formatTokenCount.ts feat(web-shell): add token-usage analytics dashboard to Daemon Status (#6388) 2026-07-06 13:43:41 +00:00
localCommandQueue.test.ts feat(web-shell): queue prompts while turns are running (#6005) 2026-06-30 14:51:28 +00:00
localCommandQueue.ts feat(web-shell): queue prompts while turns are running (#6005) 2026-06-30 14:51:28 +00:00
modelDisplay.ts feat(web-shell): polish chat UI (#5893) 2026-06-26 15:38:23 +00:00
modelEncoding.test.ts fix(web-shell): encode vision model picker selection & polish dispatch (#6236) 2026-07-03 13:51:47 +00:00
modelEncoding.ts fix(web-shell): encode vision model picker selection & polish dispatch (#6236) 2026-07-03 13:51:47 +00:00
modelSwitchMessages.test.ts feat(web-shell): polish chat UI (#5893) 2026-06-26 15:38:23 +00:00
modelSwitchMessages.ts feat(web-shell): polish chat UI (#5893) 2026-06-26 15:38:23 +00:00
queueDrain.test.ts feat(web-shell): friendlier Esc interruption + queued-prompt UX (#6025) 2026-06-30 01:27:16 +00:00
queueDrain.ts feat(web-shell): friendlier Esc interruption + queued-prompt UX (#6025) 2026-06-30 01:27:16 +00:00
sentinelMessage.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
sessionPreparation.test.ts feat(web-shell): workspace management sidebar with dynamic registration (daemon multi-workspace phase 4) (#6625) 2026-07-10 16:30:20 +00:00
sessionPreparation.ts feat(web-shell): workspace management sidebar with dynamic registration (daemon multi-workspace phase 4) (#6625) 2026-07-10 16:30:20 +00:00
slashSectionPlan.test.ts feat(web-shell): improve slash command discovery (taller menu, group counts, fuzzy search) (#6267) 2026-07-03 14:56:06 +00:00
slashSectionPlan.ts feat(web-shell): improve slash command discovery (taller menu, group counts, fuzzy search) (#6267) 2026-07-03 14:56:06 +00:00
splitUrl.test.ts feat(tui): Ctrl+O frozen transcript view and unified tool output rendering (#5666) 2026-07-09 23:40:29 +00:00
splitUrl.ts feat(web-shell): add Session Overview panel and in-window split view (#6400) 2026-07-07 02:44:18 +00:00
systemInfo.ts feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
todos.test.ts fix(web-shell): clear stale floating todos (#6425) 2026-07-07 08:59:53 +00:00
todos.ts fix(web-shell): clear stale floating todos (#6425) 2026-07-07 08:59:53 +00:00