Commit Graph

1421 Commits

Author SHA1 Message Date
Chris Coutinho a3ed321e14 fix(ci): skip existing Helm chart releases to prevent duplicate release errors
The chart-releaser workflow was failing when the Helm chart version hadn't
changed but the MCP server version was bumped. Added skip_existing: true to
gracefully handle this scenario.
2025-12-19 22:41:04 +01:00
Chris Coutinho 2bb738ed3f bump: version 0.4.0 → 0.4.1 astrolabe-v0.4.1 2025-12-19 22:31:29 +01:00
Chris Coutinho 10c8b62818 bump: version 0.3.2 → 0.4.0 2025-12-19 22:30:46 +01:00
github-actions[bot] 87abadbbfc bump: version 0.55.1 → 0.56.0 v0.56.0 2025-12-19 21:29:13 +00:00
Chris Coutinho defc55a5dc feat(ci): add --increment flag to bump scripts for manual version control
Allows forcing specific version bumps (PATCH|MINOR|MAJOR) instead of
relying solely on commitizen's automatic detection based on conventional
commits.

Usage:
  ./scripts/bump-mcp.sh --increment MINOR
  ./scripts/bump-helm.sh --increment PATCH
  ./scripts/bump-astrolabe.sh --increment MAJOR
2025-12-19 22:28:43 +01:00
github-actions[bot] 6a68e45e7c bump: version 0.3.1 → 0.3.2 astrolabe-v0.3.2 astrolabe-v0.3.3 2025-12-19 21:12:28 +00:00
Chris Coutinho a2fa4b2832 fix(astrolabe): add contents:write permission to appstore workflow
The workflow was failing to create GitHub releases with 'Not Found' error
because it lacked the required permissions. Added contents:write permission
to allow creating releases and uploading artifacts.
2025-12-19 22:12:06 +01:00
github-actions[bot] 9cfadbfc04 bump: version 0.3.0 → 0.3.1 astrolabe-v0.3.1 2025-12-19 21:04:50 +00:00
Chris Coutinho 6fed78196e fix(astrolabe): update commitizen pattern to properly update info.xml version
The pattern 'version' was too broad and matched multiple lines:
- <?xml version="1.0"?>
- <version>0.2.1</version>
- min-version="30" max-version="32"

Changed to '<version>' to specifically match only the version tag.

Also fixed version mismatch: info.xml now correctly shows 0.3.0 to match
the version in .cz.toml and package.json.
2025-12-19 22:04:26 +01:00
github-actions[bot] db430dd2c9 bump: version 0.2.0 → 0.3.0 2025-12-19 20:55:59 +00:00
Chris Coutinho 3618aed39e fix(astrolabe): prevent workflow failure when only helm/astrolabe commits exist
When filtering commits with grep -v, if all commits are filtered out,
grep returns exit code 1 which causes the pipeline to fail with set -e.

Wrap grep commands in { ... || true; } to ensure they don't fail the
pipeline when they filter out all results.

This fixes the workflow failure when a fix(astrolabe): commit is pushed
without any MCP server changes.
2025-12-19 21:55:36 +01:00
Chris Coutinho 4c083c7314 fix(astrolabe): info.xml 2025-12-19 21:48:27 +01:00
github-actions[bot] 3202640cf7 bump: version 0.55.0 → 0.55.1 v0.55.1 2025-12-19 20:45:55 +00:00
Chris Coutinho c9bbe71869 fix(ci): push all tags explicitly in bump workflow
The --follow-tags flag only pushes annotated tags by default.
Commitizen creates lightweight tags, so we need to explicitly push
all tags with --tags to ensure version tags are pushed to trigger
release workflows.
2025-12-19 21:45:06 +01:00
github-actions[bot] 00edb273cd bump: version 0.54.0 → 0.55.0 v0.55.0 2025-12-19 20:35:20 +00:00
Chris Coutinho 608b3282dd fix(ci): make MCP server default bump target for all non-scoped commits
BREAKING CHANGE: MCP server now bumps for ANY conventional commit except
those explicitly scoped to helm or astrolabe.

Previous behavior:
- MCP bumped only for unscoped or scope=mcp commits
- fix(ci): commits were ignored → no version bump

New behavior:
- MCP bumps for ALL commits except scope=helm or scope=astrolabe
- fix(ci): commits now trigger MCP version bump ✓
- feat(api): commits now trigger MCP version bump ✓
- Any custom scope triggers MCP version bump ✓

This treats the MCP server as the default/primary component in the
monorepo, with Helm chart and Astrolabe as opt-in specialized components.

Changes:
1. Updated bump-version.yml workflow logic to exclude helm/astrolabe
   instead of only including mcp/unscoped
2. Updated pyproject.toml commitizen patterns to use negative lookahead:
   (?!\((?:helm|astrolabe)\))
3. Fixed docker-build-publish.yml to only trigger on v* tags (MCP only)
4. Fixed appstore-build-publish.yml action version (v1.0.4)
5. Updated test script to use grep -P for PCRE support
6. Added test cases for ci, api, and custom scopes

All 19 scope filtering tests now pass.
2025-12-19 21:34:49 +01:00
Chris Coutinho 2888bd5693 fix(ci): restrict docker build to MCP server tags only
Docker images should only be built for MCP server releases (v* tags),
not for Helm chart (nextcloud-mcp-server-*) or Astrolabe (astrolabe-v*)
releases.

Changed trigger from all tags to v* pattern only.
2025-12-19 20:48:55 +01:00
Chris Coutinho 90d95da48d fix(ci): correct appstore-push-action version to v1.0.4
The latest available version is v1.0.4, not v1.0.6. This was causing
the Astrolabe app store deployment workflow to fail.
2025-12-19 20:48:28 +01:00
Chris Coutinho 31fb52761e bump: version 0.53.0 → 0.54.0 nextcloud-mcp-server-0.54.0 2025-12-19 20:46:11 +01:00
Chris Coutinho f7e651d0bc bump: version 0.1.0 → 0.2.0 astrolabe-v0.2.0 2025-12-19 20:45:59 +01:00
Chris Coutinho ff41fb37fd feat(ci): implement monorepo-aware version bumping workflow
Replace commitizen-action with custom workflow that detects which
components have changes based on commit scopes and bumps them
independently.

The workflow:
1. Checks for commits with scope patterns since last tag for each component:
   - MCP server: scope=mcp or unscoped, tags=v*
   - Helm chart: scope=helm, tags=nextcloud-mcp-server-*
   - Astrolabe: scope=astrolabe, tags=astrolabe-v*

2. Runs appropriate bump script for components with changes:
   - ./scripts/bump-mcp.sh
   - ./scripts/bump-helm.sh
   - ./scripts/bump-astrolabe.sh

3. Pushes all created tags at once

4. Provides GitHub Actions summary showing which components were bumped

This ensures each component versions independently based on its
relevant commits, preventing the issue where all components bump
together or some components are missed.

Fixes the issue where PR #418 only bumped MCP server, leaving Helm
chart and Astrolabe at their previous versions despite having changes.
2025-12-19 20:45:47 +01:00
github-actions[bot] 776c8ad3f7 bump: version 0.53.0 → 0.54.0 v0.54.0 2025-12-19 19:34:13 +00:00
Chris Coutinho db97bf8654 Merge pull request #418 from cbcoutinho/feature/appstore-deployment
feat: add App Store deployment and commitizen monorepo support
2025-12-19 20:33:40 +01:00
Chris Coutinho e2e0ffce44 fix(ci): improve versioning and error handling
Addresses remaining high-priority code review feedback:

VERSIONING SCHEME FIXES:
- Helm chart: Changed from pep440 to semver (correct for Helm)
- Astrolabe: Changed from pep440 to semver (correct for Nextcloud apps)
- MCP server: Remains pep440 (correct for Python packages)

Helm charts must use semantic versioning per Helm specification.
Nextcloud apps use semantic versioning in info.xml and package.json.

ENHANCED ERROR HANDLING IN BUMP SCRIPTS:
All three bump scripts now include:
- Comprehensive validation checks
  * Tool availability (uv)
  * Directory structure (must run from repo root)
  * Required files exist (Chart.yaml, info.xml, package.json)
- Better error messages
  * Stderr output for errors
  * Captured commitizen output on failure
  * Common failure causes listed
- Success confirmation
  * Clear indication of what was updated
  * Next steps guidance (git push --follow-tags)
- Robust shell options (set -euo pipefail)

Scripts now provide helpful guidance when:
- No conventional commits found
- No commits with required scope
- Git working directory not clean
- Required dependencies missing
2025-12-19 19:38:24 +01:00
Chris Coutinho 2f3a3e0be4 fix(ci): address critical workflow and validation issues
Addresses code review feedback on PR #418:

CRITICAL FIXES:
1. Workflow trigger: Changed from release:published to push:tags
   - Enables "tag and publish in one step" workflow as intended
   - Automatically creates GitHub release on tag push
   - Removed redundant if condition (filtering now via trigger)
   - Added prerelease detection based on tag (-alpha, -beta, -rc)

2. Server path: Explicitly pass server_dir to make command
   - Fixes path mismatch between local (../../server) and CI
   - Uses absolute path: server_dir=${{ github.workspace }}/server
   - Prevents signing failures in GitHub Actions

3. Regex validation: Added test script for commitizen patterns
   - Validates scope filtering works correctly
   - Tests all three components: mcp, helm, astrolabe
   - Tests unscoped commits (default to mcp)
   - Tests breaking changes and invalid commits
   - Location: scripts/test-commitizen-scopes.sh

WORKFLOW IMPROVEMENTS:
- Release creation now automatic on tag push
- Better step naming for clarity
- Consistent prerelease handling across GitHub and App Store
- Explicit server_dir prevents reliance on fragile relative paths

All 16 test cases pass for scope filtering patterns.
2025-12-19 19:34:21 +01:00
Chris Coutinho c5f7221fb2 fix(astrolabe): address code review feedback
CRITICAL FIXES:
- Fix tag parsing in workflow to strip "astrolabe-v" instead of "v"
  For tag astrolabe-v0.1.0, now correctly extracts "0.1.0"
- Add workflow filtering to only run on astrolabe-v* tags
  Prevents wasting CI resources on MCP/Helm releases

RECOMMENDED IMPROVEMENTS:
- Make Nextcloud server path configurable in Makefile
  Can now override: make appstore server_dir=/path/to/server
- Add dependency validation to Makefile
  Checks for composer, npm, php before building
- Add signing prerequisite validation
  Verifies server/occ, private key, and certificate exist
- Add dependency checks to all bump scripts
  Validates uv is installed before running cz bump

These changes improve local build experience and prevent common
errors with clear error messages and installation guidance.
2025-12-19 18:34:14 +01:00
Chris Coutinho 4a42b947bc feat(astrolabe): add Nextcloud App Store deployment automation
Add complete CI/CD pipeline for automated Astrolabe app releases:
- GitHub Actions workflow for build, sign, and publish
- Makefile for app store package creation
- Version synchronization between info.xml and package.json
- CHANGELOG.md with v0.1.0 release notes

feat: configure commitizen monorepo with independent versioning

Enable independent versioning for three components using scope-based commits:
- MCP Server (feat(mcp) or unscoped): v* tags, updates pyproject.toml + Chart.yaml:appVersion
- Helm Chart (feat(helm)): nextcloud-mcp-server-* tags, updates Chart.yaml:version
- Astrolabe App (feat(astrolabe)): astrolabe-v* tags, updates info.xml + package.json

Changes:
- Add .cz.toml configs for Astrolabe and Helm chart
- Update root pyproject.toml with scope filtering and tag ignores
- Create bump helper scripts (bump-mcp.sh, bump-helm.sh, bump-astrolabe.sh)
- Add CONTRIBUTING.md with version management documentation
- Create component-specific changelogs
- Configure appVersion/version separation for Helm chart

This allows each component to release independently while maintaining
proper version tracking and changelog generation.
2025-12-19 18:06:39 +01:00
github-actions[bot] 46b260641f bump: version 0.52.1 → 0.53.0 nextcloud-mcp-server-0.53.0 v0.53.0 2025-12-19 13:23:12 +00:00
Chris Coutinho 60d80970a4 Merge pull request #401 from cbcoutinho/feature/nc-app-ui
feat(astrolabe): Nextcloud app UI with PDF viewer, webhooks, and OAuth refresh
2025-12-19 14:22:42 +01:00
Chris Coutinho daabd90359 fix(security): address critical security issues from PR #401 code review
Implemented 6 critical security fixes identified during PR #401 review:

1. Token Rotation Race Condition (Issue 1)
   - Added in-progress marker pattern to prevent concurrent refresh
   - Prevents token invalidation when multiple requests refresh simultaneously
   - File: token_broker.py:324, 343-390

2. Hardcoded Localhost URL (Issue 2)
   - Added getNextcloudBaseUrl() with fallback chain
   - Supports overwrite.cli.url, trusted_domains, and localhost fallback
   - File: IdpTokenRefresher.php:38-61, 116

3. Error Information Leakage (Issue 3)
   - Replaced 13 instances of str(e) with sanitized errors
   - Prevents exposure of stack traces, paths, and tokens
   - File: management.py:368, 444, 492, 510, 546, 571, 625, 643, 695, 750, 919, 956, 1121

4. Input Validation Gaps (Issue 4)
   - Added validation helpers: _parse_int_param, _parse_float_param, _validate_query_string
   - Applied bounds checking to get_chunk_context and unified_search
   - File: management.py:119-164, 807-835, 1197-1212

5. PHP Refresh Token Validation (Issue 5)
   - Added explicit refresh_token presence check
   - Prevents silent token rotation failures
   - File: IdpTokenRefresher.php:122-132

6. Cookie Security Configuration (Issue 6)
   - Added _should_use_secure_cookies() with auto-detection
   - Supports explicit COOKIE_SECURE env var or auto-detect from NEXTCLOUD_HOST
   - Files: browser_oauth_routes.py:27-44, 470; env.sample:54-57

Testing:
- Unit tests: 195 passed
- Integration tests: 102 passed, 4 skipped
- OAuth tests: 9 passed
- All linting and type checks passed

Follow-up work tracked in issues #408-#417

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-19 13:57:33 +01:00
Chris Coutinho fe54733a39 fix(oauth): enable PKCE for all clients and add token_broker to oauth_context
This commit fixes two OAuth issues in the Astrolabe app:

1. **Always use PKCE (RFC 9207)**:
   - PKCE is now used for all OAuth flows (public and confidential clients)
   - Previous code only used PKCE for public clients, causing failures
   - Confidential clients now use both PKCE + client_secret (defense in depth)
   - Nextcloud OIDC provider requires PKCE, so token exchange was failing

2. **Add token_broker to oauth_context**:
   - Token broker is now stored in oauth_context for management API access
   - Fixes "Token broker not configured" error when revoking access
   - Revoke endpoint needs token_broker to delete refresh tokens and invalidate cache

Changes:
- OAuthController.php: Always generate PKCE verifier/challenge for all clients
- OAuthController.php: Always include code_verifier in token exchange
- app.py: Store token_broker in oauth_context after creation

Fixes:
- Astrolabe OAuth flow now works with Nextcloud OIDC
- Revoke/disconnect functionality now works in Astrolabe settings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 01:55:04 +01:00
Chris Coutinho 8d6eff2792 fix(astrolabe): revert invalid files_pdfviewer URL for file links
The files_pdfviewer app route is internal to Nextcloud and not a valid
external URL. Reverted to using the standard Files app viewer URL for
all file types.

- Removed PDF-specific handling that used /apps/files_pdfviewer/
- All files now link to /apps/files/files/{id} (standard Files viewer)
- Fixes broken links in chunk modal titles and search results

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 01:54:39 +01:00
Chris Coutinho e4f3beee01 fix: resolve type checking warnings for CI
- Add type casts for Starlette app state access
- Add assertions for cipher, card, board, stack after initialization
- Add None checks for XML element text attributes
- Handle __package__ being None in tracing setup
- Fix TokenBrokerService initialization to use storage credentials

Resolves 42 type warnings from ty-check, enabling CI linting to pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:44:58 +01:00
Chris Coutinho 54b69f0d68 fix: move Alembic to package submodule for Docker compatibility
- Move alembic/ directory to nextcloud_mcp_server/alembic/ subpackage
- Update migrations.py to use package location instead of alembic.ini
- Update env.py to set script_location dynamically
- Update alembic.ini for development CLI usage
- Fix Dockerfile typo: .vnev -> .venv

This fixes FileNotFoundError when running in Docker with non-editable
install. The alembic package is now installed with the main package,
making it work in both development and production environments.

Resolves: Docker startup error 'alembic.ini not found at
/app/.venv/lib/python3.12/site-packages/alembic.ini'

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:42:59 +01:00
Chris Coutinho c4b3df04a0 docs(astrolabe): rewrite README for release with pitch integration
Rewrote Astrolabe README to be user-friendly and release-ready by
incorporating pitch.md content and moving technical details to linked
documentation.

Key changes:
- Incorporated compelling pitch narrative as opening
- Restructured around "What You Can Do" rather than architecture
- Added clear use cases for individuals, teams, and developers
- Simplified installation to 3 steps
- Moved OAuth flow and architecture details to ADR links
- Added emoji sections for visual appeal
- Focused on benefits over implementation

Sections:
- What You Can Do (search, visualization, AI agents)
- Installation (app store + manual)
- Quick Start (3-step setup)
- Features (personal, admin, unified search)
- Use Cases (research, collaboration, RAG workflows)
- Requirements (Nextcloud 30+, MCP server, OAuth)
- Documentation (links to installation, configuration, ADRs)
- Troubleshooting (quick fixes with links to detailed guides)

This README is now suitable for Nextcloud App Store submission.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:09 +01:00
Chris Coutinho d4c0da85da docs: update running guide to prioritize Docker usage
Updated docs/running.md to use Docker container examples instead of
direct Python commands. This aligns with the CLI change to require
explicit 'run' subcommand while maintaining backward compatibility
for Docker users (ENTRYPOINT includes 'run').

Key changes:
- Quick Start: Use Docker commands instead of uv run
- Running Locally → Running with Docker: All examples use Docker
- Development Mode: Added CLI subcommands documentation (run/db)
- Database Migrations: Documented Alembic integration for developers
- Server Options: Docker port mapping instead of --host/--port flags
- Process Management: Simplified to Docker Compose only (removed systemd)
- Performance Tuning: Production Docker Compose with resource limits
- Troubleshooting: Docker logs and debug commands

Updated Dockerfile ENTRYPOINT:
- Changed from: ["/app/.venv/bin/nextcloud-mcp-server", "--host", "0.0.0.0"]
- Changed to: ["/app/.venv/bin/nextcloud-mcp-server", "run", "--host", "0.0.0.0"]

No breaking changes for Docker/Helm users - container interface unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:09 +01:00
Chris Coutinho 3fa376905c feat: add Alembic database migration system
Implements Alembic for managing token storage database schema versions.
Migrations run automatically on startup with full backward compatibility.

**Changes:**
- Add Alembic dependency (1.14.0+) and SQLAlchemy (auto-installed)
- Create migration infrastructure in alembic/ directory
- Add initial migration (001) capturing current schema
- Modify RefreshTokenStorage.initialize() to run migrations via anyio
- Add CLI commands: db upgrade, current, history, downgrade, migrate
- Add comprehensive migration documentation

**Backward Compatibility:**
- Pre-Alembic databases automatically stamped with revision 001
- No schema changes for existing databases
- Automatic upgrade on first startup after update

**Migration Strategy:**
Three scenarios handled:
1. New database → Run migrations from scratch
2. Pre-Alembic database → Stamp with 001 (no changes)
3. Alembic-managed → Upgrade to latest

**Architecture:**
- Uses anyio.to_thread.run_sync() for structured concurrency
- Alembic env.py runs with anyio.run() in worker thread
- SQLite-friendly migration patterns documented
- No ThreadPoolExecutor needed (anyio handles it)

**CLI Usage:**
```bash
nextcloud-mcp-server db upgrade    # Upgrade to latest
nextcloud-mcp-server db current    # Show version
nextcloud-mcp-server db history    # View changelog
nextcloud-mcp-server db downgrade  # Rollback (with confirmation)
nextcloud-mcp-server db migrate "description"  # Create migration
```

**Testing:**
- All 13 webhook storage tests pass
- New/pre-Alembic database scenarios validated
- anyio integration tested

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:09 +01:00
Chris Coutinho a4a34e46a8 feat: make chunk modal title clickable link to documents
- Add clickable link to modal title with OpenInNew icon
- Store currentResult to enable document navigation
- Fix deck_card URLs to use metadata.board_id
- Fix news_item URLs to use external article URL from metadata.url
- Add hover styling for title link and icon

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:09 +01:00
Chris Coutinho d235dfa023 chore: Rename Astroglobe -> Astrolabe 2025-12-18 00:02:08 +01:00
Chris Coutinho 24898439cb fix: update unified search results to match chunk viz display
Update the unified search provider to show only chunk/page metadata
in search results, consistent with the chunk visualization result list.
Also fix news item URLs to link directly to the specific item.

Changes to SemanticSearchProvider:

1. Result display improvements:
   - Remove excerpt text from search result subline
   - Show only chunk/page metadata (e.g., "Chunk 2/5 · Page 3/10")
   - Consistent with chunk visualization UI in App.vue

2. News item URL fix:
   - Change from generic news index to specific item URL
   - Format: /apps/news/item/{id}
   - Allows direct navigation to the news article

3. Code cleanup:
   - Remove unused $excerpt variable
   - Remove unused truncateExcerpt() method
   - Simplify transformResult() logic

Benefits:
- Cleaner, more scannable search results
- Consistent UX between unified search and app UI
- Functional links to news items instead of generic news page
- Reduced code complexity
2025-12-18 00:02:08 +01:00
Chris Coutinho 6da98b4e7b feat: add native Plotly hover styling for clickable points
Replace expensive Plotly.restyle() hover handlers with native hoverlabel
styling to indicate clickable points without performance degradation.

Implementation:
- Add hoverlabel configuration to document trace with distinct styling
- Bright blue background (#0082c9) to make hover state obvious
- Larger font size (15px) for better visibility
- White text for contrast against blue background
- Handled natively by Plotly - no JavaScript event handlers needed

Benefits:
- Zero performance impact - no chart re-renders on hover
- Smooth, responsive hover feedback
- Clear visual indication that points are clickable
- Consistent with existing hover tooltip pattern

Removed:
- Expensive handlePlotHover() and handlePlotUnhover() methods
- Plotly.restyle() calls that caused severe lag and freezing
- hover/unhover event listener registrations

The hover tooltip now uses the styled hoverlabel to stand out visually,
providing clear feedback that points are interactive without any
performance cost.
2025-12-18 00:02:08 +01:00
Chris Coutinho fba4b9b785 feat: add click interactivity to Plotly 3D scatter chart
Enable users to click on points in the vector space visualization to
open the chunk viewer modal, providing a more direct interaction
method alongside the existing "Show Chunk" button.

Implementation details:
- Register plotly_click event handler in renderPlot() after chart creation
- Add handlePlotClick() method to process click events
- Use point index mapping to access full result object from this.results
- Add loading guard in viewChunk() to prevent concurrent chunk loading
- Add cursor styling: pointer for result points, default for query point
- Add beforeDestroy() lifecycle hook to cleanup event handlers

Features:
- Both interaction methods work: click chart points OR "Show Chunk" button
- Only result points (trace 0) are clickable, query point (trace 1) ignored
- Pointer cursor on hover indicates clickable points
- Loading state prevents rapid clicks from causing issues
- Memory leak prevention through proper event handler cleanup

Technical approach:
- Uses index mapping (not data duplication) for efficiency
- Results and coordinates arrays have guaranteed 1:1 mapping from API
- Event handler re-registered on each chart re-render
- CSS-based cursor styling (more performant than JS hover handlers)

Testing:
- ESLint validation passed
- Follows Vue 2.7 component property order conventions
- Compatible with existing chunk viewer modal
2025-12-18 00:02:08 +01:00
Chris Coutinho b246a03ac4 feat: improve chunk viewer with fixed navigation and markdown rendering
This commit implements three UI improvements for the chunk viewer:

1. Fixed modal footer with navigation controls
   - Moved PDF navigation buttons to a fixed footer
   - Footer remains visible while scrolling content
   - Three-section layout: fixed header, scrollable body, fixed footer

2. Removed duplicate navigation controls
   - Removed previous/next buttons from PDFViewer component
   - Controls now only in App.vue modal footer
   - Cleaned up unused imports and CSS

3. Markdown rendering for chunk content
   - Created MarkdownViewer component using markdown-it
   - Renders markdown content aligned with Nextcloud design system
   - Removed problematic markdown-it-task-checkbox dependency
   - Combines before/chunk/after context with visual separators

4. Cleaned up search results display
   - Removed excerpt snippets from results list
   - Kept only chunk/page metadata for cleaner UI

The modal structure now has:
- Fixed header (title + close button)
- Scrollable body (PDF canvas or markdown content)
- Fixed footer (page navigation - always visible)

Fixes markdown rendering "require is not defined" error by using
only markdown-it without CommonJS plugins.
2025-12-18 00:02:08 +01:00
Chris Coutinho 04c64e97b0 fix(astrolabe): handle OAuth refresh token rotation
Fixes 401 errors after first token refresh when using IdPs that
implement refresh token rotation (Keycloak, modern OAuth providers).

**Root Cause**:
McpTokenStorage::getAccessToken() was discarding the new refresh token
returned by the IdP after successful refresh, always keeping the old one.
This caused:
- First refresh: works (uses original refresh token)
- Second refresh: fails with 401 (old refresh token invalidated by IdP)

**Solution**:
Use new refresh token from IdP response if provided, fall back to old
token for providers that don't rotate refresh tokens.

**Changed**:
- lib/Service/McpTokenStorage.php:184
  From: $token['refresh_token']  // Always old token
  To:   $newTokenData['refresh_token'] ?? $token['refresh_token']

**Verified**:
ApiController already handles rotation correctly using the same pattern.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:02 +01:00
Chris Coutinho af9a55cebd feat(astrolabe): enable multi-select for document types and refactor PDF viewer
This commit includes two improvements to the Astroglobe semantic search UI:

1. **Multi-select Document Types** (App.vue):
   - Changed NcCheckboxRadioSwitch binding from v-model to :checked/:update:checked
   - Implemented toggleDocType() method to manually manage selectedDocTypes array
   - Fixes issue where only single document type could be selected at a time
   - Users can now filter search results by multiple doc types simultaneously

2. **PDF Viewer Reactive Rendering** (PDFViewer.vue):
   - Refactored canvas rendering to use Vue reactive watcher pattern
   - Added watcher on 'loading' state that triggers rendering when canvas available
   - Removed imperative renderPage() call from loadPDF() method
   - Inspired by files_pdfviewer's promise/event-based initialization approach
   - Improves alignment with Vue's reactive data flow

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:54 +01:00
Chris Coutinho 44391d3d1d fix: address critical code review issues (4 fixes)
This commit addresses 4 critical issues identified in code review:

1. **Token Rotation Race Condition** (token_broker.py)
   - Added per-user locking mechanism to prevent concurrent refresh token corruption
   - Implemented double-check pattern for cache after acquiring lock
   - Users can now safely refresh concurrently without token desync

2. **Hardcoded OAuth Client ID** (PHP files)
   - Made client ID configurable via `astroglobe_client_id` in system config
   - Updated McpServerClient to provide getClientId() method
   - Injected McpServerClient into IdpTokenRefresher and OAuthController
   - Updated admin settings UI to display client ID configuration status
   - App gracefully handles missing client ID with warnings in admin UI

3. **Missing Cache Invalidation** (management.py:revoke_user_access)
   - Added cache.invalidate() call when revoking user access
   - Ensures both storage AND cache are cleared atomically
   - Prevents stale cached tokens from being used after revocation

4. **Error Message Exposure** (management.py)
   - Created _sanitize_error_for_client() helper function
   - Updated all error handlers to log detailed errors internally
   - Returns generic messages to clients to prevent information leakage
   - Protects against exposing database paths, API URLs, tokens, etc.

All changes are backward compatible and preserve existing functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:54 +01:00
Chris Coutinho 619c62d89a ci: Remove --headed from pyproject.toml 2025-12-18 00:01:54 +01:00
Chris Coutinho dfc81923ba fix: resolve CI linting issues for Astroglobe
Fix all ESLint, Stylelint, PHP CS Fixer, and Psalm workflow errors.

Changes:
- ESLint fixes:
  - Remove unused APP_NAME constant
  - Remove unused TextBoxOutline and TextBoxRemoveOutline components
  - Remove unused container variable in adminSettings.js
  - Auto-fix trailing commas, line breaks, attribute ordering
- PHP CS Fixer:
  - Add trailing commas after last function parameters
  - Convert double quotes to single quotes in log messages
  - Remove unused NoCSRFRequired import
  - Fix arrow function formatting
- Stylelint:
  - Update config to use @nextcloud/stylelint-config
  - Fix extends directive (was using non-existent package)
- Psalm workflow:
  - Fix jq object indexing (.include[0] instead of .[0])
  - Correctly extract OCP version from matrix output

All checks now pass locally.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:54 +01:00
Chris Coutinho 5a6205476a ci: add consolidated GitHub workflow for Astroglobe app
Create single workflow that includes all key checks from Nextcloud app
skeleton instead of copying 14 separate workflow files.

Changes:
- Create astroglobe-ci.yml workflow:
  - Triggers on PRs modifying third_party/astroglobe/
  - Detects frontend vs PHP changes separately
  - Frontend checks: Node.js build, ESLint, Stylelint
  - PHP checks: CS Fixer, Psalm static analysis
  - Uses official Nextcloud actions (version-matrix, read-package-engines)
  - Runs checks only for changed file types
  - Summary job for branch protection rules

Benefits:
- Consolidated workflow easier to maintain than 14 files
- Follows Nextcloud app quality standards
- Catches issues before deployment
- Automatic checks on every PR

Based on Nextcloud app skeleton workflows from:
https://github.com/nextcloud/.github

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:53 +01:00
Chris Coutinho be7f512244 docs: document deployment modes and Nextcloud log querying
Update ADR-018 with comprehensive deployment architecture and add
Nextcloud application log querying patterns to CLAUDE.md.

Changes:
- ADR-018 deployment modes documentation:
  - Mode 1: Basic single-user (development/simple)
  - Mode 2: Basic multi-user pass-through (no OIDC)
  - Mode 3: OAuth multi-user with progressive consent
  - Authentication flows for each mode
  - Communication path diagrams
  - Implementation examples
  - Use cases and limitations
- CLAUDE.md additions:
  - Nextcloud application log querying patterns
  - Common jq filters for debugging
  - Log structure documentation
  - App-specific filtering examples

Benefits:
- Clear guidance on deployment architecture selection
- Documented authentication flows for all scenarios
- Easier debugging with log query patterns
- Complete reference for mode-specific configurations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:53 +01:00