Commit Graph

153 Commits

Author SHA1 Message Date
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 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 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
Developer 93da9b42a2 chore(project-map): regenerate .pi-map artifacts across the repo 2026-06-17 11:31:24 +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 b26ed7c3e4 refactor: store workspaces as {workspace_id}/{repo_name} for natural git clone layout
Working copies were stored as /data/working-copies/{repo_id}/{workspace_name}/,
so git clone was forced into a user-named directory. That meant the container
mount basename was the workspace name (e.g. main) instead of the repo name.

- Generate the workspace UUID before cloning and clone into
  /data/working-copies/{workspace_id}/ so git creates {repo_name}/ naturally
- Set workspace.path to /data/working-copies/{workspace_id}/{repo_name}/
- Update _migrate_clone_into_workspace() to use the same layout
- _get_repository_mount_name() now prefers workspace.path basename and only
  falls back to remote URL / repo.name for legacy repo-only instances
- Update unit tests to assert workspace path basename is used for mounts

Quality gates:
- pytest tests/unit: 219 passed
- ruff: clean on changed files
- mypy: clean on changed files
2026-06-15 09:40:59 +00:00
Developer 6e33e8e4e9 fix: remove explicit repo mount from pi-agent manifest and derive workspace name from remote URL
The pi-agent manifest still declared an explicit repo mount with
{{WORKSPACE_NAME}}, making the mount target dependent on tool config. The
instance service now synthesizes the repo mount, so the manifest no longer
needs the explicit mount.

- Add Alembic migration 2026_06_15_090500 to remove the source_type: repo
  mount from the built-in pi-agent manifest
- Add _get_repository_mount_name() helper to derive the workspace directory
  name from the repository remote URL (matching git clone behavior) and
  fall back to the user-provided repository name
- Use the helper for WORKSPACE_NAME/REPO_NAME in manifest, legacy dockerfile,
  and legacy compose template paths
- Update unit tests for the new migration and helper

Quality gates:
- pytest tests/unit: 218 passed
- ruff: clean on changed files
- mypy: clean on changed files
- alembic heads: single head
2026-06-15 09:10:05 +00:00
Developer f0ae9483f3 fix: use repository name for workspace mount target
WORKSPACE_NAME was computed from os.path.basename(repo_path), so when a
workspace path ended in a directory like 'main', the container mount target
became /home/user/main instead of /home/user/{repo-name}.

- Use GitRepository.name for WORKSPACE_NAME/REPO_NAME in manifest and
  legacy dockerfile flows
- Add unit test verifying prepare_manifest_instance uses repo.name even
  when the workspace path basename differs

Quality gates:
- pytest tests/unit: 213 passed
- ruff: clean on changed files
- mypy: clean on changed files
2026-06-15 08:54:01 +00:00
Developer 90992e46a8 fix: remove stale {{WORKSPACE_NAME}} directory from container home
Older cached images still contain a literal /home/user/{{WORKSPACE_NAME}}
directory baked in by the previous Dockerfile generation. Even though new
images no longer create it, existing images leave the placeholder folder
alongside the real repo-named mount.

- Add entrypoint cleanup that removes /{{WORKSPACE_NAME}} if it
  exists before creating the real workspace target and /workspace symlink
- Update unit tests to assert the stale placeholder removal

Quality gates:
- pytest tests/unit: 212 passed
- ruff: clean on changed files
- mypy: clean on changed files
2026-06-15 08:41:27 +00:00
Developer 089d802f1d fix: prevent failed containers from showing as running on dashboard
- Add final get_container_status check in start_tool_instance before
  writing status=running; mark as error and return logs if container stopped
- Treat restarting as error in HealthMonitor when DB status was already
  running, so crash loops are surfaced instead of preserved
- Disable auto-restart (restart: unless-stopped -> restart: no) for tool
  instances in manifest compiler, legacy dockerfile path, and built-in seeds

Quality gates:
- pytest tests/unit: 210 passed
- ruff: clean on changed files
- mypy: clean on changed files
2026-06-14 21:52:02 +00:00
Developer bd94cc9bbf fix: use sudo/root to create /workspace symlink in manifest entrypoint
The previous commit moved the pi-agent repo mount from /workspace to
/home/user/{repo_name}. This exposed a permission bug: the Dockerfile
creates /workspace as a root-owned symlink in the image, and the
non-root entrypoint could not replace it because / is owned by root.

- Update compile_entrypoint to recreate /workspace via sudo when running
  as the container user, or directly when running as root
- Add unit test covering sudo/root symlink creation
- Update OpenSpec change docs with the additional root cause

Quality gates:
- pytest tests/unit: 208 passed
- ruff: clean on changed files
- mypy: clean on changed files
- alembic heads: single head
2026-06-14 20:29:03 +00:00
Developer fe82a248ec fix: pi container repo mount target and npm update permissions
- Add Alembic migration to update built-in pi-agent manifest:
  * repo mount target from /workspace to ~/{{WORKSPACE_NAME}}
  * keep /workspace as compatibility symlink via working_dir
  * update startup chown target to $HOME/$WORKSPACE_NAME
- Pass REPO_NAME and WORKSPACE_NAME to compile_compose from instance_service
- Substitute {{WORKSPACE_NAME}} in manifest mount targets and expose it as
  a container env var so the entrypoint can create the /workspace symlink
- Generate entrypoint workspace symlink from runtime WORKSPACE_NAME env var
- Install npm_global packages into {home_dir}/.npm-global with PATH so the
  non-root container user can update global packages
- Update manifest compiler unit tests for the new behavior

Quality gates:
- pytest tests/unit: 207 passed
- ruff: clean on changed files
- mypy: clean on changed files
- alembic heads: single head
2026-06-14 18:45:32 +00:00
Developer c8db6ce933 fix: disable native touch panning on mobile terminal and archive specs
- Change mobile terminal CSS to use touch-action: none and
  overscroll-behavior: none so the custom touch handler owns swipes
- Archive completed/partial OpenSpec specs to
  openspec/changes/archive/2026-06-14-completed-specs-archive/
- Regenerate project maps

Quality gates: npm run typecheck, npm run lint (apps/web)
2026-06-14 18:07:01 +00:00
Developer ac9f7a9299 fix: prevent terminal container from overflowing page on desktop
The desktop terminal page sometimes grew an outer scrollbar because the
terminal instance/wrapper/container chain lacked height constraints.
Without min/max-height enforcement, xterm.js's internal viewport could
expand its parent flex/grid track past the available space.

- Add overflow: hidden to .terminal-page.
- Add max-height: 100% and overflow: hidden to .terminal-instance.
- Add max-height: 100% to .terminal-wrapper.
- Add min-height: 0 to .terminal-container.
- Constrain .xterm-viewport to max-height/width 100% so it fills but
  never exceeds its container.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed).

Refs: openspec/changes/fix-terminal-container-overflow
2026-06-14 09:07:43 +00:00
Developer 4c14966ae3 fix: write valid multi-line tmux config in Pi Agent images
The Pi Agent dockerfile templates created ~/.tmux.conf with a literal
\n because the RUN command used single-quoted echo. Tmux never parsed
the malformed line, so mouse mode stayed off. Without tmux mouse mode,
mouse-wheel events in xterm.js fell back to Up/Down arrow keys and
cycled shell command history instead of scrolling the terminal buffer.

- Use printf '%s\n' to write real newlines in .tmux.conf.
- Apply the same fix to the ranger rc.conf where the same bug existed.
- Update tool-images/pi-agent.dockerfile and both affected alembic
  migration dockerfile strings.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed),
py_compile on changed migrations.

Refs: openspec/changes/fix-tmux-mouse-config
2026-06-14 08:48:39 +00:00
Developer a12d6a8169 feat: restore delete buttons in mobile list views and fix edit action bar
- Wire MobileListView onItemDelete/onItemDuplicate callbacks to render
  action buttons in each list row.
- Pass onItemDelete in ToolWorkshopMobileView list view.
- Add CSS for mobile-list-item-action buttons.
- Fix MobileEditView sticky bottom action bar that was hidden behind
  the 64px mobile navigation bar; raise to bottom: 64px and z-index 110.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed)

Refs: openspec/changes/mobile-list-delete-button
2026-06-14 08:10:58 +00:00
Developer 23fc0a6b82 feat: mobile edit-as-default with sticky save/delete actions
- Update MobileEditView to render Save and optional Delete in a
  sticky bottom action bar; header now shows Cancel + title only.
- Make ConfigProfilesMobileView open edit view on profile tap.
- Make ToolWorkshopMobileView open edit view on tool type tap.
- Wire delete into MobileEditView for existing profiles and tool
  types.
- Stay on edit view after saving an existing item; create flow
  returns to list as before.
- Update ToolWorkshopPage cancel to return to list.
- Add mobile-edit-actions and mobile-edit-delete CSS.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed)

Refs: openspec/changes/mobile-edit-default-bottom-actions
2026-06-13 22:29:15 +00:00
Developer 98d4393387 feat: redesign mobile Config Profiles detail, preview, and edit pages
- Rewrite ConfigProfilesMobileView to match desktop functionality:
  full detail view with all fields, preview action showing resolved
  profile, and edit view with project/tool selects, includes,
  environment variables, runtime hints, files, mounts with nested
  files, and git mounts.
- Update useConfigProfiles.handleSubmit to return boolean success.
- Update ConfigProfilesPage to pass required state and callbacks.
- Add mobile-specific CSS for config profile forms, includes,
  mount/file cards, and preview panels.
- Allow MobileDetailView to render extra children.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed)

Refs: openspec/changes/mobile-config-profiles-ui
2026-06-13 22:04:17 +00:00
Developer 350b393457 feat: rework mobile UI for Tool Workshop and Profile pages
- Rework ToolWorkshopMobileView to support full desktop functionality:
  definition type selection (Compose/Dockerfile/Manifest), manifest editor,
  conditional port, startup command, readiness probe, required variables,
  and validation feedback.
- Add ProfileMobileView and wire ProfilePage to render it on mobile.
- Update useToolWorkshop hook to return boolean success from submit.
- Add responsive CSS for mobile forms, edit views, and manifest editor.
- Update project maps.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed)

Refs: openspec/changes/mobile-tool-profile-ui
2026-06-13 21:41:42 +00:00
Developer 1e0f95f8bd chore: archive completed OpenSpec changes
Move the following audited-and-implemented changes into
openspec/changes/archive/2026-06-12-completed-changes-archive/:

- tool-config-mount-cleanup
- config-profile-directory-mounts

Update archive README count and project map index/files accordingly.
openspec/changes/ now contains only the archive directory.

Quality gates: pytest 313 passed/34 skipped, npm run typecheck/lint clean,
npm test -- --run 87 passed
2026-06-13 14:24:30 +00:00
Developer d395aaf574 fix: stage config-profile mounts as directories so ~/.config stays writable
- Switch apply_resolved_profile from per-file bind mounts to one
  directory-level bind mount per ResolvedMount target.
- Stage all configured files under instance_dir/mounts/<sanitized_target>
  and bind-mount that directory, so Docker no longer creates a root-owned
  parent directory such as ~/.config.
- Propagate read-only mode ('ro') as the 'readonly' flag on volume entries.
- Update unit tests to expect directory-level mounts and add coverage for
  readonly/writable flags.

Quality gates: python3 -m py_compile, pytest (313 passed, 34 skipped),
npm run typecheck, npm run lint.
2026-06-13 11:56:48 +00:00
Developer 19291d6be9 chore: patch project maps after tool-config mount cleanup 2026-06-13 11:40:03 +00:00
Developer 8bde9a213c feat: remove config/state mounts from built-in tool configs
- Remove pi_state and pi_config mounts from the pi-agent manifest.
- Add Alembic data migration to strip those mounts from existing DB rows.
- Remove opencode_home:/tmp volume and HOME=/tmp override from the opencode
  built-in compose template; config/state now belongs in config profiles.
- Workspace and SSH key mounts remain unchanged.

Quality gates: python3 -m py_compile, pytest (311 passed, 34 skipped),
npm run typecheck, npm run lint
2026-06-13 11:35:55 +00:00
Developer 8c1948d226 chore: archive final 4 completed OpenSpec changes
Move the following completed changes from openspec/changes/ to
openspec/changes/archive/2026-06-12-completed-changes-archive/:
- multi-session-terminal-ux
- reorganize-long-files
- working-copies
- workspace-first-ui

Update parent and archive .pi-map*.md indexes to reflect the move and
remove the transient active-changes-archive grouping.

openspec/changes/ now contains only the archive/ directory.
2026-06-12 21:11:11 +00:00
Developer ce8b5dc86d feat: complete reorganize-long-files cleanup
- Extract tool instance lifecycle endpoints (start/stop/restart/delete) from
  api/tool/tool_instances.py into new api/tool/tool_lifecycle.py.
- Register tool_lifecycle_router in main.py and api/tool/__init__.py.
- Extract inline WorkspaceDetailPage components into
  components/features/workspace/: detail header, tab bars, file/git/tools/settings
  panels. Slim page from ~446 to ~62 lines.
- Update OpenSpec reorganize-long-files tasks to reflect completed work and
  current source state; mark change completed.
- Regenerate project maps.

Quality gates: python3 -m py_compile (backend clean), npm run typecheck,
npm run lint, npm test -- --run (87 passed), pytest workspace integration
and unit tests (27 passed, 1 skipped).
2026-06-12 18:53:23 +00:00
Developer c26e9eacfa feat: complete workspace-first-ui cleanup and tests
- Delete dead repo-workspace code: RepoWorkspacePage, useRepoWorkspace,
  WorkspaceLayout, FileBrowser, old git components (git-toolbar, file-editor,
  commit-panel), and repo-workspace.css.
- Fix stale backend test imports for moved models/services.
- Add GitOperations unit tests.
- Add integration tests for workspace files, git, and instances endpoints.
- Add frontend tests for WorkspaceDetailPage and ProjectCard.
- Update OpenSpec workspace-first-ui tasks and mark change completed.
- Regenerate project maps.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed),
python3 -m py_compile on changed backend files, pytest backend workspace tests.
2026-06-12 17:25:09 +00:00
Developer 6b947b7593 feat: complete working-copies workspace-first cleanup
- Remove clone_mode/branch/new_branch from frontend create session flow.
- Add workspace picker to CreateSessionForm; auto-create default workspace when repo selected.
- Fix tool-starter.tsx and use-start-tool.ts createInstance signatures after API change.
- Remove clone mode badge from SessionCard.
- Delete stale backend unit tests referencing removed clone_mode schema fields.
- Update OpenSpec working-copies tasks and mark change completed.
- Regenerate project maps.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (82 passed),
python3 -m py_compile on changed backend files.
2026-06-12 15:47:26 +00:00
Developer 40ac931c65 chore: patch project maps for multi-session-terminal-ux metadata updates 2026-06-12 14:43:22 +00:00
Developer 7623f29ffb docs: complete multi-session-terminal-ux OpenSpec metadata
- Fill empty apply-pr2.md with backend API + frontend client apply report
- Mark all 12 tasks as completed in tasks.md
- Update .openspec.yaml status from exploring to completed

The implementation was already merged to dev across PR 1, PR 2, and PR 3.
This commit only synchronizes the OpenSpec change metadata.
2026-06-12 14:41:12 +00:00
Developer caadd59441 chore: archive 15 completed OpenSpec changes
Move the following audited-and-implemented changes into
openspec/changes/archive/2026-06-12-completed-changes-archive/:

- backend-frontend-refactoring
- config-profile-git-mounts
- config-profile-includes-ui
- config-profile-multi-repo-mounts
- container-monitoring-notifications
- git-mount-url-validation
- home-path-expansion
- mobile-terminal-ux
- mount-specificity-ordering
- notification-center
- persistent-terminal-sessions
- session-list-overhaul
- ssh-key-mounting
- terminal-fullscreen-unified-header
- tool-session-progress-and-updates

Also regenerated .pi-map*.md files for openspec/changes so the
remaining active changes (multi-session-terminal-ux, reorganize-long-files,
working-copies, workspace-first-ui) reflect the new layout.
2026-06-12 14:26:55 +00:00
Developer 070cd4d5a5 chore: track all project map and index files
Add generated .pi-map.md and .pi-map.index.md files across the repository
so the project navigation maps are shared and versioned. These artifacts
are maintained by project_map_init/patch/validate and must be kept in
sync with source edits.

Note: .cache/ remains ignored (added in previous commit).
2026-06-12 13:46:23 +00:00
Developer 7440720b7b feat: implement tool-session progress panel and live list updates
- Add SessionOperationsContext + SessionProgressPanel for global,
  non-blocking lifecycle progress (create/start/stop/restart/delete/
  recreate-tunnel) driven by SSE events.
- Promote SessionsContext to authoritative shared session state with
  refresh, addOrUpdateSession, and removeSession helpers.
- Wire AppShell, DashboardPage, SessionsPage, useInstanceActions,
  ToolStarter, and InstanceList into shared state so lists update
  immediately after create/delete without manual refresh.
- Remove legacy blocking overlays from CreateSessionForm, SessionCard,
  and InstanceList; keep disabled states and inline spinners only.
- Update DashboardPage tests to wrap with SessionsProvider and
  SessionOperationsProvider.
- Add .cache/ to .gitignore.

Quality gates: npm run typecheck, npm run lint, npm test -- --run
(82 passed).
2026-06-12 13:19:58 +00:00
Developer 7070867393 docs: update reorganize-long-files spec to include CSS reorganization
Add Phase 2 (CSS Reorganization) covering:
- Restore styles/ directory (tokens, global, utilities, syntax-highlight)
- Restore styles/pages/*.css for page-specific styles
- Restore 11 component CSS modules from monolithic styles.css
- Delete styles.css after extraction

Shift frontend page extraction phases to 3-7.
Add visual regression checks to integration phase.

Quality gates unchanged: tsc, build, py_compile, file size limits
2026-06-05 10:33:10 +00:00
Developer d472c41092 docs: add OpenSpec spec for reorganize-long-files
Create SDD proposal, spec, and tasks for splitting monolithic pages
and routers into focused components and services.

Targets:
- Frontend pages: 50-150 lines max (from 300-1600)
- Backend routers: 300-400 lines max (from 800-2900)
- Follow main branch pattern: thin pages + extracted components

Quality gates: tsc, build, py_compile, file size limits
2026-06-05 10:19:27 +00:00
alex 0127d283a6 docs: update refactoring spec with submodule architecture
Enforces max 5-10 files per directory using proper subpackages:
- api/tool/, api/config/, api/workspace/, api/user/, api/project/, api/system/
- services/docker/, services/instance/, services/config/, services/git/,
  services/build/, services/terminal/, services/shared/
- models/tool/, models/config/, models/user/, models/project/, models/system/
- schemas/tool/, schemas/config/, schemas/user/, schemas/project/, schemas/system/

Updated design.md module map and tasks.md with 9 phases.
2026-06-04 09:49:55 +02:00
alex 2757ef3b4f docs: add OpenSpec change for backend-frontend refactoring
Recovers and adapts the structural refactoring from overwritten
main merge (b6f89f9) to current dev reality.

Scope:
- Schema extraction into apps/api/src/schemas/
- Docker service split into services/docker/ package
- Instance lifecycle extraction from api/tool_instances.py
- Config profile service extraction from api/config_profiles.py
- Auth dependency refactor (get_current_user)
- Frontend reorganization into features/ dirs + kebab-case naming

Exclusions (already in dev): seeding, defaults, unique constraint,
SSH key mounting, terminal backend, tunnel regex, session auto-numbering.
2026-06-04 09:42:03 +02:00
alex e7587ca9f5 feat: workspace-first UI refresh - PR-1 backend endpoints
- Add FileService for workspace-scoped file operations
- Add GitOperations service for workspace-scoped git commands
- Add workspace_files API: GET/POST /workspaces/{id}/files
- Add workspace_git API: status, branches, commit, push, pull, fetch, checkout, history
- Add workspace_instances API: list instances per workspace
- Add top-level POST /workspaces/ (accepts repo_id directly)
- Enrich GET /projects/ with nested repositories and workspaces
- Register all new routers in main.py
- 23 tests passing (17 existing + 6 new)

Quality gates: ruff clean
2026-06-01 16:47:09 +02:00
alex d567225bf7 feat: workspace backend foundation (PR-1)
- Add workspaces table migration (2026_06_01_add_workspaces)
- Create Workspace model with repo_id, user_id, branch, path, status
- Add workspace_id nullable FK to ToolInstance
- Create GitService for clone/fetch/pull/branch_exists_remotely
- Create WorkspaceManager for create/delete/sync lifecycle
- Create workspace CRUD API with 409 handling for duplicates and instances
- Wire workspace routes into FastAPI app
- 17 tests passing (8 unit + 9 integration), 1 skipped

Quality gates: ruff clean
2026-05-31 23:02:45 +02:00
alex 19242b4152 feat: notification center toast coordination (PR-4)
- EventToastBridge checks notification_toast_level and notification_mute_categories
- toast-rules.ts: event-to-category/severity mapping functions
- Settings page: notification preferences section (toast level dropdown, mute checkboxes)
- Settings API types extended with notification preference fields
- 17 frontend tests (toast-rules + bridge)
- Preference hierarchy: mute categories → toast level → show/hide

Quality gates: vitest 17 passed, tsc clean, eslint clean
2026-05-29 13:52:49 +02:00
Alex Blank ceaed9af66 Merge remote dev branch 2026-05-29 13:34:08 +02:00
Alex Blank e9364fa70f feat: instance-level SSH key selection for container mounting
- Revert mistaken ssh_key_id from ConfigProfile (model, API, resolver, frontend)
- Add ssh_key_ids JSON column to tool_instances via migration
- Update create_instance to accept and store ssh_key_ids
- Update start_instance to mount selected SSH keys to {home_dir}/.ssh
- Update list_instances to return ssh_key_ids
- Frontend CreateSessionForm: multi-select SSH key checkboxes
- Frontend instance-list: SSH key selector for start/restart actions
- Maintain separate SSH key dirs per key to avoid conflicts

Quality gates: pytest (231 passed, 6 pre-existing), tsc --noEmit clean
2026-05-29 13:30:53 +02:00
alex 2bec205a30 feat: notification center frontend core (PR-3)
- Bell icon in icon registry (Phosphor Bell)
- NotificationProvider context with polling (15s unread / 30s list)
- useNotifications() hook with optimistic updates
- NotificationCenter component: bell + badge + dropdown panel
- NotificationItem component: severity icon, title, relative time, actions
- AppShell integration: mount in header-actions, hidden on mobile
- CSS styles: dropdown, items, unread/read states, empty state
- formatRelativeTime utility (custom, no new deps)
- 25 frontend tests (9 hook + 6 item + 10 center)

Quality gates: vitest 25 passed, tsc clean, eslint clean
2026-05-29 13:17:22 +02:00
Alex Blank cbd3436ff7 Merge remote dev branch 2026-05-29 12:55:03 +02:00
Alex Blank 57ff236f2d feat: add ssh_key_id to config profiles for container key mounting
- Add ssh_key_id column to ConfigProfile model and migration
- Update config profile API to accept/return ssh_key_id
- Include ssh_key_id in ResolvedProfile and resolver logic
- Mount selected SSH key into container home dir at start_instance
- Frontend config profile form with SSH key selector dropdown
- Git mount URL validation defaults to profile's SSH key

Quality gates: pytest (231 passed, 6 pre-existing), tsc --noEmit clean
2026-05-29 12:53:51 +02:00
alex 6085859874 feat: notification center backend integration (PR-2)
- Wire lifecycle_hooks.py to NotificationService after event bus publish
- Wire health_monitor.py to NotificationService after state changes
- Category/severity mapping: instance.* → info, error → error, unhealthy → warning
- Extend UserConfig API with notification_mute_categories and notification_toast_level
- 6 integration tests for event-to-notification flow
- All producer calls wrapped in try/except — failures logged, pipeline continues

Quality gates: pytest 41 passed (monitoring + lifecycle), ruff clean
2026-05-29 12:40:15 +02:00
Alex Blank d413fb84a5 Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-05-29 12:16:40 +02:00
Alex Blank 090edf7ef6 feat: git mount URL validation with branch detection
- Add POST /config-profiles/validate-git-url endpoint:
  - Parses URL using existing parse_git_url utility
  - Suggests corrected URL for browser URLs
  - Runs git ls-remote --heads to verify reachability
  - Lists available branches from remote
  - Supports SSH key for private repos
  - Returns structured response: valid, suggested_url, branches,
    default_branch, error, error_code

- Update frontend GitMountEditor:
  - Add Check button next to URL field with loading state
  - Show validation result: valid (green), suggestion (yellow),
    invalid (red)
  - Suggestion includes Use this button to apply corrected URL
  - Branch field becomes dropdown when URL is validated,
    populated with remote branches
  - Mappings section disabled until URL is validated
  - Shows hint: Validate the URL first

- Quality gates: pytest (218 passed, 6 pre-existing),
  tsc --noEmit (clean)
2026-05-29 12:15:30 +02:00
alex cbaebcf649 feat: notification center backend core (PR-1)
- Add notifications table with Alembic migration
- Notification model with user-scoped indexing and partial index on unread
- NotificationService singleton with create/list/count/mark-read/dismiss
- FastAPI router: GET /notifications, GET /unread, PATCH /{id}/read,
  POST /mark-all-read, DELETE /{id}
- Mute categories filtering from UserConfig
- 13 unit tests for NotificationService
- 10 integration tests for API endpoints
- Updated test_models.py with new table registration

Quality gates: pytest 23 new passed, ruff clean
2026-05-29 12:09:14 +02:00
Alex Blank 0952aa8217 fix: sort mount volumes by specificity to prevent parent mounts hiding children
When git repo mounts and regular file mounts have overlapping target
paths, broader parent mounts hide deeper child mounts because Docker
Compose applies volumes in array order.

- Add sort_volumes_by_specificity() to docker.py:
  - Sorts by target path depth (parent paths first, child paths last)
  - Logs warnings for duplicate targets
  - Handles :bind and :ro suffixes correctly

- Integrate into manifest flow (compile_compose):
  - Sorts manifest mounts + EXTRA_VOLUMES before writing compose

- Integrate into legacy flow (_modify_compose_file):
  - Sorts after appending extra_volumes to existing template volumes

- Add 6 unit tests covering parent/child ordering, stable sort,
  type suffixes, empty list, single volume, and duplicate warnings.

Quality gates: pytest (214 passed, 6 pre-existing), tsc --noEmit (clean)
2026-05-29 11:35:27 +02:00