Commit Graph

739 Commits

Author SHA1 Message Date
alex d14cdc1151 fix(terminal): support browser clipboard shortcuts
Copy selected terminal output with Ctrl/Cmd+C without sending an interrupt, and route browser paste consistently through the terminal transport.
2026-07-22 12:52:19 +02:00
alex 5331a0f110 fix(config-profiles): preserve mounted file inodes
Update files within profile directory mounts in place so editor saves reach running containers.
2026-07-22 11:52:42 +02:00
alex 5610017f50 fix(config-profiles): preserve bound file inodes
Overwrite individually bind-mounted profile files in place so editor saves remain visible to running containers.
2026-07-22 11:37:53 +02:00
alex 2247ec47c9 fix(config-profiles): synchronize shared mount working copies
Use canonical profile files and writable Git working copies so editor and container changes share one source. Require confirmation before destructive Git refreshes and overlay profile files without composite snapshots.
2026-07-22 11:23:19 +02:00
alex 900a8e47a5 fix(config-profiles): show Git mount refresh progress
Disable duplicate refresh requests and show in-progress feedback while Git mount sources are refreshed.
2026-07-21 21:14:08 +02:00
alex 16984b7cf6 fix(containers): compose profile and Git mounts safely
Stage profile sources per instance and compose overlapping bind mounts so Docker cannot mask Git content or leave writable files root-owned.\n\n- preserve shared Git clones while applying profile overlays\n- add mount composition and ownership regression coverage\n- update OpenSpec tracking
2026-07-21 20:49:03 +02:00
Developer fc52353b2e fix: break config profile refresh import cycle 2026-07-21 15:46:35 +00:00
Developer 886c863260 feat: refresh shared Git config mounts live
- Use profile-scoped canonical Git clone sources with locked refreshes
- Mount shared Git configuration read-only and isolate profile content
- Add API and desktop/mobile actions for live Git mount refresh

Quality gates: frontend build and backend py_compile passed.
Skipped: backend pytest/Ruff unavailable; Docker/manual checks not approved.
2026-07-21 14:37:42 +00:00
Developer add7c1b500 feat: add live config profile refresh
- Standardize built-in tool users for shared writable profile mounts
- Mount canonical non-Git profile sources across compatible instances
- Report restart-required outcomes and guard active profile deletion
- Surface restart feedback in config profile editing

Quality gates: frontend build passed; backend py_compile and LSP passed.
Skipped: backend pytest/Ruff unavailable; Docker/manual checks not approved.
2026-07-21 11:12:41 +00:00
Developer f9f9372ee7 fix: merge web terminal resilience 2026-07-18 10:32:56 +00:00
Developer ea42165ed2 fix: harden web terminal paste and reconnect
- Queue bounded ordered terminal input so acknowledgements remain responsive
- Prevent stale sockets and retries from replacing healthy connections
- Preserve desktop scrollback behavior and add terminal regression coverage

Quality gates: frontend tests (91 passed), typecheck, lint, build, Python compilation, LSP diagnostics. Backend pytest skipped by user request.
2026-07-17 22:11:24 +00:00
Developer b5e961ebe9 fix: reuse cached config profile git mounts
- Reuse valid deterministic git mount clones on repeated starts
- Remove incomplete clone destinations before retrying
- Add regression coverage for cached and partial clones
- Document OpenSpec change fix-config-profile-git-mount-clone-reuse

Quality gates: pytest (13 passed), ruff, mypy
2026-07-17 20:54:01 +00:00
Developer 671540ded9 feat: add Headquarter favicon
- Add a geometric evergreen Headquarter mark for browser tabs\n- Register the SVG favicon and matching browser theme color\n\nOpenSpec: add-project-favicon\nQuality gates: npm run build
2026-07-15 13:06:40 +00:00
Developer 415aecc0dd fix: constrain dialog content to the viewport
- Add dynamic viewport bounds and scrollable body regions to dialogs and modals\n- Make tool-launch popups use the shared scrollable body pattern\n- Keep mobile sheets, action sheets, and notification popups scroll-contained\n- Restore the ProjectsPage test setup required for the frontend suite\n\nOpenSpec: fix-dialog-scroll-containment\nQuality gates: npm run typecheck, npm run lint, npm test (88 passed), npm run build
2026-07-15 10:20:20 +00:00
Developer bb38b37ceb fix: reinitialize terminal on mobile viewport changes
Recreate the terminal when responsive classification changes so mobile scrollback and touch listeners are installed.\n\nOpenSpec: fix-mobile-terminal-scrolling\nQuality gates: npm run typecheck, npm run lint, npm test (89 passed), npm run build
2026-07-14 19:41:34 +00:00
Developer 6698c20f25 fix: restore mobile terminal scrolling
- Retain xterm normal-buffer history on mobile while preserving desktop zero-scrollback behavior\n- Repair ProjectsPage tests for session context and current project list markup\n- Add focused terminal scrollback coverage\n\nOpenSpec: fix-mobile-terminal-scrolling\nQuality gates: npm run typecheck, npm run lint, npm test (89 passed), npm run build
2026-07-14 19:07:02 +00:00
Developer 49180e4c6d fix: fully drain terminal paste writes to PTY
- Make the host PTY master non-blocking and wait for write readiness.
- Retry partial writes so a large bracketed paste always delivers its closing
  marker instead of leaving pi in paste mode.
- Add regression coverage for partial PTY writes and resolve diagnostics.

Quality gates: ruff, mypy, focused pytest (3 passed)
2026-07-14 10:34:33 +00:00
Developer 41d24beade fix: forward browser terminal pastes as bracketed input
- Track application bracketed-paste mode from terminal output.
- Capture browser and mobile clipboard pastes only while that mode is enabled,
  normalizing line endings and sending a single BPM-framed input event.
- Remove the synchronous output decoding and console diagnostics that could
  stall terminal rendering under output load.
- Keep the terminal's zero-scrollback configuration and resolve existing
  no-case-declarations lint blockers in terminal keyboard shortcuts.

Quality gates: npm run typecheck, npm run lint
2026-07-14 10:02:30 +00:00
Developer 691bbaa87b fix: restore web terminal multiline paste by reverting scrollback to 10000
- Revert xterm.js scrollback from 0 to 10000; empirical correlation with
  bracketed-paste mode failing (each line submitted as separate command).
- Add temporary browser-console diagnostics to confirm whether xterm receives
  pi's \e[?2004h enable sequence and whether outbound paste is BPM-wrapped.
- Keep CSS scrollbar hiding and wheel-sensitivity 0 so the original scroll-jank
  fix remains effective.

Typecheck: passed (apps/web)
2026-07-13 13:42:56 +00:00
Developer 468f34202a fix(terminal): hide scrollbar and stop stale-frame wheel scroll for TUI tools
The web terminal only hosts full-screen TUI tools (pi-agent, opencode),
which repaint in place in the normal buffer and do not use the alternate
screen or mouse tracking. With scrollback enabled, every repaint
accumulated as history, so xterm's viewport scrollbar appeared and the
mouse-wheel scrolled through stale frames instead of interacting with the
app; the scrollbar column also perturbed FitAddon's column count.

- terminal.tsx: set scrollback:0 and scrollSensitivity/fastScrollSensitivity:0
  so only the live viewport is kept (no bar, no stale-frame wheel jank).
- utilities.css: hide .xterm-viewport scrollbar (scrollbar-width:none +
  ::-webkit-scrollbar display:none) as belt-and-suspenders.

Wheel no longer scrolls stale frames; in-app scrolling uses the tool's own
keys. Headquarter-only change; no tool is touched.
2026-07-11 12:07:47 +00:00
Developer ff7fcb6e8d fix(terminal): stop heartbeat pong leaking into the PTY as input
The WebSocket control-frame heuristic only recognized resize/ack/reset,
so the frontend's heartbeat reply {"type":"pong"} fell through to
write_input() and was typed into the shell / pi every ~30s. That garbage
corrupted the foreground app: stray text in the input line, rerenders,
and scroll-position resets (visible on resize/scroll redraws).

Treat any text frame that parses to a JSON object carrying a "type" field
as control traffic that must NEVER reach the PTY: handle known types and
ignore unknown ones. Keystrokes, bracketed-paste content, and plain text
are still forwarded as raw input.
2026-07-11 11:48:48 +00:00
Developer a1a77c99a6 fix: enable multiline paste in web terminal
Multiline pastes into the web terminal (especially into pi) were split
into one prompt per line because bracketed-paste markers were not
reaching the foreground app intact.

- Put the host PTY into raw mode (tty.setraw) after openpty() so it acts
  as a pass-through pipe. The default canonical line discipline was
  line-buffering input, splitting multiline pastes at newlines, and
  mangling bracketed-paste markers before docker exec / pi could see
  them. The in-container PTY (docker exec -t) provides real discipline.
- Route the mobile Paste button through xterm.js (term.paste) instead of
  sending raw clipboard text to the WebSocket, so content is wrapped in
  bracketed-paste markers when the app has enabled BPM.
- Treat a text frame as a control message only when it is a JSON object
  with a known type (resize/ack/reset); otherwise forward as raw input
  so JSON-shaped pastes are no longer silently dropped.

Quality gates: ruff, mypy (changed files), pytest unit (227 passed),
tsc, eslint
2026-07-11 11:32:23 +00:00
alex 10955dfe8e fix(tool): stop defaulting manifest working_dir to /workspace
The /workspace compatibility symlink was removed from the manifest
compiler/entrypoint in 3e59a25. Tool definitions that still set
runtime.working_dir to /workspace therefore start in an empty directory
instead of /home/user/{repo_name}.

- manifest-editor.tsx: default working_dir to empty instead of /workspace;
  update startup-script placeholder to reference /home/alex/.
- Add Alembic migration 2026_06_19_113000 that clears the stale
  runtime.working_dir = /workspace from the built-in pi-agent manifest.
- Add migration import test.

Quality gates: pytest tests/api tests/services/test_terminal_manager_multi.py tests/unit (248 passed), ruff check (clean), npx tsc --noEmit (clean), eslint (clean).
2026-06-19 12:10:42 +02:00
alex b32fea671f fix(terminal): avoid lazy-loading tool manifest in async websocket handler
The container-user resolver introduced in 9f72093 accessed
'tool_type.manifest', which triggers a SQLAlchemy lazy load inside the
async WebSocket coroutine and raises MissingGreenlet. Fetch the manifest
explicitly with db_session.get() instead, matching the pattern used in
instance_service.py.

- Replace relationship access with explicit async loads in
  _resolve_container_user().
- Add unit tests covering manifest, base-definition, legacy, and missing
  manifest cases.
- Update project map artifacts.

Quality gates: pytest tests/api tests/services/test_terminal_manager_multi.py tests/unit (247 passed), ruff check (clean).
2026-06-19 11:48:53 +02:00
Developer 19f91c085e Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-06-17 20:57:27 +00:00
Developer 9f720930ea fix: run tool terminal sessions as container user instead of root
- Remove compose-level user: 0:0 override from manifest_compiler.py so the
  entrypoint can start as root, fix mount ownership, and drop privileges to
  the container user internally.
- Add get_manifest_container_user() helper to resolve the manifest-declared
  container user (with uid:gid fallback).
- Pass container user through TerminalSession, TerminalManager, and the
  terminal WebSocket handler so docker exec is invoked with --user <user>.
- Update and add unit tests for the manifest compiler and terminal session.
- Record the additional root-user fix in the fix-pi-container-mount-permissions
  OpenSpec change/tasks.

Quality gates: pytest tests/unit/ (226 passed), pytest tests/services/test_terminal_manager_multi.py (7 passed), ruff check on changed files (clean), mypy on changed files (clean)
2026-06-17 20:51:46 +00:00
alex 1c6dbcede8 fix: resolve project variable in create_tool_instance
create_tool_instance fetched `project` only inside the auto-display-name
branch, so callers supplying display_name left it unbound and the
manifest/dockerfile/compose branches crashed with UnboundLocalError at
_get_repository_mount_name (in-container layout /home/user/{project_name}).

Resolve project unconditionally after repo validation with a not-found
guard (narrowing Project|None -> Project), and drop the now-redundant
fetch from the auto-name branch. Add a regression test covering the
manifest + display_name path.

Quality gates: ruff, mypy, pytest (74 unit tests passed)
2026-06-17 16:12:22 +02:00
Developer 3e59a257dc feat(terminal): use project name for in-container cwd and clone directory 2026-06-17 13:26:16 +00:00
Developer 93da9b42a2 chore(project-map): regenerate .pi-map artifacts across the repo 2026-06-17 11:31:24 +00:00
Developer 0dd5f2317b fix(web/ui): improve nav-item hover contrast 2026-06-17 09:53:33 +00:00
Developer ab93863ea9 fix(web/ui): restore nav-item anchor styling for live session items 2026-06-17 09:47:37 +00:00
Developer 8b6a4f7712 fix(web/ui): resolve padding and dialog layout issues from audit
- Remove conflicting .dialog/.dialog-body rules from utilities.css
- Fix live-session sidebar alignment and padding
- Wrap ProjectDialog, RepositoryCreateDialog, WorkspacesPage modals in .dialog-header/.dialog-body
- Migrate SessionsPage dirty-delete modal from .modal-* to .dialog-*
- Add padding to .project-card
- Fix session-card-actions border token (var(--border))
- Remove duplicate .dialog-actions rule in global.css
- Add pb-20 bottom clearance to ConfigProfile/ToolType editor scroll containers
- Add .card-md padding to ErrorState
2026-06-16 21:54:55 +00:00
Developer bf698b3ff2 feat(web/ui): unify session opening and add repo card in project list
- Add openSession utility with tab deduplication/focus
- Use openSession in navbar live sessions, use-instance-actions, and project tool links
- Pass onAddRepository to ProjectListItem and add dashed 'Add Repository' card
- Style add-repo card in projects.css
2026-06-16 21:31:38 +00:00
Developer 563ff13969 feat(web/ui): rework project pane into flat list with repo/workspace/tool hierarchy
- Add ProjectListItem component with project header actions
- Show repositories horizontally with branch labels
- List workspaces vertically under each repo
- Show running tools per workspace from useSessions
- Link workspace names to /workspaces/:id
- Link tool names to web URL or terminal page
- Keep existing mobile view and dialogs unchanged
2026-06-16 21:20:04 +00:00
Developer 39bac24100 fix(web/ui): align project heading left and keep add-repo button near title 2026-06-16 21:00:47 +00:00
Developer 889c598129 fix(web/ui): make config-section-actions sticky for both editors 2026-06-16 16:25:23 +00:00
Developer ac56c5e391 fix(web/ui): correct project card layout and unify config section actions
- Fix .project-card flex direction so expanded repos align left
- Add .config-section-actions for consistent save bars
- Replace sticky footer in Config Profiles with config-section-actions
- Replace dialog-actions in Tool Type editor with config-section-actions
- Convert manifest editor preview block to .config-section
2026-06-16 16:06:08 +00:00
Developer 972dae64f2 feat(web/ui): unify config sections, restore home padding, improve project repos
- Add shared .config-section component class and migrate Tool Workshop
  and Config Profiles editors to use it.
- Restore --space-4 padding on home hero, summary cards, and sections
  with --space-3 on mobile.
- Restructure expandable project card: add 'Repositories' header with
  repo count and move 'Add Repository' button into the section header.
- Left-align repository blocks so they fill from the project detail edge.
2026-06-16 15:55:58 +00:00
Developer 6e419f815d feat(web/ui): consolidate dialogs, cards, and breakpoints (Pass 3)
- Unify modal/dialog system; .modal-* are now aliases of .dialog-*
- Migrate WorkspacesPage, start-tool-modal, merge-dialog, workspace-tools-panel to .dialog-*
- Add .card-sm/.card-md/.card-lg/.card-elevated/.card-borderless modifiers
- Apply card utilities across workspaces, projects, ssh-keys, workspace-detail, git-history
- Remove duplicated card-like background/border/padding from page CSS
- Remove 860px breakpoint; standardize on 767px/768px mobile split
- Add docs/development/ui-review-checklist.md
- Archive web-ui-spacing-typography-rework OpenSpec change
2026-06-16 15:27:45 +00:00
Developer 5d5b39bec1 feat(web/ui): refactor component inline styles into utility classes
Pass 2 of the web UI spacing/typography/visual-rhythm rework.

- Add layout, spacing, typography, visual, card, and component utilities
- Add form-section, form-row, form-help, text-error, alert-success
- Unify .form-group and .form-field; add .status-badge family
- Alias legacy button classes to .btn primitives
- Refactor ToolTypeListSidebar and ConfigProfileListSidebar to use .sidebar
  and var(--sidebar-width) instead of hardcoded 280px
- Refactor ToolTypeEditorPanel, ConfigProfileEditorPanel,
  git-mount-editor, and manifest-editor to use utility classes

Quality gates: npm run typecheck, npm run lint, npm run build pass.
Inline style blocks in target components reduced from 198 to 11.
2026-06-16 14:47:37 +00:00
Developer 454a7845bb feat(web/ui): replace inline font-size violations and use new primitives in components 2026-06-16 12:24:04 +00:00
Developer 50e3af0d91 feat(web/ui): add mobile detail/search, ssh/profile utilities and nav-height tokens 2026-06-16 12:23:59 +00:00
Developer 8e1af41cd3 feat(web/ui): expand design tokens and add primitive component styles 2026-06-16 12:23:53 +00:00
Developer 355b471067 feat(web): redesign navbar live session items for scanability
Restructure live session nav entries into a 3-line layout:
- workspace/repository context on top
- session display name in the middle
- tool type and project on the bottom

Tool icon now has a status-dot badge, and the tooltip lists
display name, tool, project, context, and status.
2026-06-16 11:13:10 +00:00
Developer 82ff8b8801 fix: route terminal callbacks and status per session
The recent redraw fix keeps all xterm instances mounted (display:none)
when switching sessions. However, sendData/focus/font-size refs and the
header connection status were still stored globally, so the last-mounted
hidden session could own keyboard input, font-size buttons, and the
status dot for the active session.

- Pass sessionId to onTerminalReady from TerminalComponent.
- Store terminal callbacks and status keyed by sessionId in use-terminal-page.
- Use activeSessionId to route special-key input, font-size changes, and header status.
- Clean up per-session refs and status when sessions are closed.
- Update MobileTerminalWrapper signature for the new callback shape.

Quality gates: npm run typecheck, npm run lint, npm test (87 passed)
2026-06-15 13:53:09 +00:00
Developer a6ad8268b9 fix: preserve terminal buffer across tab switches and focus regain
Render all terminal sessions and hide inactive ones with display:none so
xterm instances are no longer unmounted/remounted when switching tabs.

Remove terminal.clear() on the connected status message: the server keeps
the session buffer, and clearing was erasing visible content when the tab
regained focus or reconnected.

- Keep TerminalComponent instances alive in both Desktop and Mobile views.
- Only refit/resize on reconnect instead of clearing.

Closes terminal redraw on focus regain.
2026-06-15 13:11:08 +00:00
Developer 3358af57c2 fix: stack profile file mounts onto profile git-mounts to avoid masking
When a config profile declares both a git_mount and a mounts entry for the
same directory (e.g. ~/.pi), the generated bind-mounts would mask each other
inside the container. Instead, copy the static profile files into the
instance-scoped git-mount source directory so the container sees both the
cloned repo contents and the static files through a single bind-mount.

- Add _stack_profile_mounts_with_git_mounts helper to merge overlapping
  profile mounts into git-mount sources.
- Integrate stacking into start_tool_instance after resolving both mount
  types.
- Add unit tests for exact, descendant, non-overlapping, and file cases.
2026-06-15 12:50:19 +00:00
Developer 6a61669294 fix: git repo mount race and path resolution
- Include branch in git-mount clone dir hash so different branches of the
  same repo get separate directories and no longer race.
- Resolve profile working_directory before git mounts so relative targets
  are not silently skipped.
- Fall back to tool_type.home_directory for non-manifest tools.
- Fix glob target calculation to avoid duplicate directory segment.
- Add exc_info logging for git mount clone failures.
2026-06-15 12:15:50 +00:00
Developer 29e48cdb65 fix: force interactive login shell for bash containers
Detached containers with tty: true still exited immediately because a
plain /bin/bash invocation exits with code 0 when stdin is not connected.

- Detect when the container CMD is /bin/bash or bash and exec an
  interactive login shell () after dropping privileges
- Keep the generic  path for non-shell commands
- Bump compiler_version to v4 to force a fresh image build

Quality gates:
- pytest tests/unit: 219 passed
- ruff: clean on changed files
- mypy: clean on changed files
2026-06-15 11:18:28 +00:00
Developer ed1f7805f6 fix: bump compiler version to v3 for runuser entrypoint
The previous compiler_version v2 already produced an image tag
(3d34c636) for the su-based fix. Images built under v2 still had the
broken su entrypoint that exited immediately. Bump to v3 so the runuser
change forces a fresh image build with the corrected privilege drop.

Quality gates:
- pytest tests/unit: 219 passed
2026-06-15 11:09:20 +00:00