Commit Graph

10 Commits

Author SHA1 Message Date
MarekWo 815adb5cfa feat(bridge): Add pending contacts management API endpoints
Added two new HTTP endpoints to meshcore-bridge for managing pending contacts
(contacts awaiting manual approval when manual_add_contacts mode is enabled):

New endpoints:
- GET /pending_contacts - List all pending contacts awaiting approval
  - Parses meshcli output format: "Name: <hex_public_key>"
  - Returns JSON array with {name, public_key} objects
  - Includes raw_stdout for debugging

- POST /add_pending - Approve and add a pending contact
  - Accepts JSON body: {"selector": "<name_or_pubkey>"}
  - Validates selector is non-empty string
  - Executes meshcli add_pending command via persistent session

Additional changes:
- Added curl to mc-webui Dockerfile for testing endpoints
- Updated README with Testing Bridge API section
- Included example curl commands and expected responses

Implementation notes:
- Uses existing MeshCLISession.execute_command() - no new processes
- Same persistent session and command queue architecture
- Consistent error handling with existing /cli endpoint

Enables future UI for manual contact approval workflow.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 08:09:35 +01:00
MarekWo 3a100e742d fix(bridge): Correct command name from msg_subscribe to msgs_subscribe 2025-12-28 17:14:09 +01:00
MarekWo d720d6a263 fix(bridge): Replace polling with msg_subscribe for real-time messages
Critical fixes based on user feedback:

1. **Remove auto-recv polling (30s interval)**
   - Polling with 'recv' doesn't fetch NEW messages, only reads from .msgs
   - Wasteful - creates unnecessary command traffic

2. **Add msg_subscribe for real-time message reception**
   - meshcli's msg_subscribe enables automatic message events
   - Messages arrive via EventType.CHANNEL_MSG_RECV events
   - No polling needed - truly asynchronous

3. **Make TZ configurable via .env instead of hardcoded**
   - Changed docker-compose.yml: TZ=${TZ:-UTC}
   - Added TZ=Europe/Warsaw to .env.example
   - Users can now set their own timezone

4. **Remove unused shlex import**
   - Not needed after switching to manual double-quote wrapping

Technical details:
- msg_subscribe sends subscription command to meshcli at init
- meshcli then emits events when messages arrive
- Events trigger TTY errors (harmless - meshcli tries to print_above)
- Messages are still saved to .msgs file by meshcli core

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 17:10:33 +01:00
MarekWo 34c60515ad fix(bridge): Add auto-recv thread to sync messages every 30s
CRITICAL FIX: Messages stopped arriving after switching to persistent session.
Root cause: meshcli in interactive mode (Popen stdin/stdout) doesn't receive
messages automatically - requires explicit 'recv' command.

Changes:
- Add auto_recv_thread that calls 'recv' every 30 seconds in background
- This ensures continuous message synchronization without breaking /cli
- Messages are saved to .msgs file by meshcli as usual
- Add TZ=Europe/Warsaw to both containers for correct timestamps in logs

Technical details:
- auto-recv runs in separate thread via command queue (no blocking)
- First recv after 5s delay (let session initialize)
- Uses execute_command() internally, so respects command serialization
- Thread stops gracefully on shutdown_flag

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 16:59:32 +01:00
MarekWo 36badea5a2 fix(bridge): Use double quotes instead of shlex.quote for message args
Users reported single quotes appearing in sent messages.
shlex.quote() uses single quotes which meshcli treats literally.

Changes:
- Replace shlex.quote() with custom double-quote wrapping
- Only quote args containing spaces or special chars
- Escape internal double quotes with backslash
- Example: ['public', 'To jest test'] → 'public "To jest test"'

meshcli should strip double quotes but preserve content.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 15:55:42 +01:00
MarekWo 56b7c335e8 fix(bridge): Quote command arguments to preserve spaces in messages
Users reported messages with spaces were truncated to first word.
Root cause: ' '.join(args) didn't quote arguments with spaces.

Changes:
- Import shlex module
- Use shlex.quote() for each argument in execute_command()
- Example: ['public', 'To jest test'] → "public 'To jest test'"

This ensures meshcli receives multi-word messages correctly.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 15:52:06 +01:00
MarekWo 693b211a71 fix(bridge): Replace echo markers with timeout-based response detection
meshcli doesn't support 'echo' command, causing "Unknown command" errors.
Changed strategy from end-markers to timeout-based detection:

- Remove echo "___END_{cmd_id}___" markers
- Add _monitor_response_timeout() thread per command
- Track last_line_time timestamp for each response
- Complete command when no new lines received for 300ms
- Update _append_to_current_response() to update timestamps

This approach is more robust and doesn't depend on meshcli's command set.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 15:44:53 +01:00
MarekWo 34afa6908d feat(bridge): Implement persistent meshcli session with advert logging
Major refactor of meshcore-bridge to maintain a single long-lived meshcli
process instead of spawning new processes per request.

Changes:
- Add MeshCLISession class managing persistent subprocess.Popen session
- Implement thread-safe command queue with event-based synchronization
- Add stdout/stderr reader threads with JSON advert detection
- Log adverts automatically to {device_name}.adverts.jsonl with timestamp
- Add end-of-response markers (echo "___END_{cmd_id}___") for multiplexing
- Implement watchdog thread for auto-restart on meshcli crash
- Update /cli endpoint to delegate commands through persistent session
- Add MC_CONFIG_DIR and MC_DEVICE_NAME env vars to docker-compose.yml

Architecture benefits:
- No more USB port conflicts between concurrent requests
- Continuous advert logging without breaking /cli compatibility
- Better error recovery with automatic session restart
- Reduced overhead from process spawning

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 15:41:41 +01:00
MarekWo c19a5bac88 fix: Add request locking and caching to prevent USB conflicts
Root cause: Multiple concurrent meshcli calls were fighting for USB access,
causing "Protocol error" and 504 Gateway Timeouts.

Changes to meshcore-bridge:
- Add threading.Lock to serialize meshcli subprocess calls
- Prevent concurrent USB access that causes OSError [Errno 71]
- Reduce DEFAULT_TIMEOUT from 30s to 10s
- Add detailed logging for lock acquisition and release

Changes to main API:
- Implement 30s cache for get_channels() to reduce USB calls
- Cache invalidation after channel create/join/delete operations
- Use cached channels in /api/channels and /api/messages/updates
- Reduce HTTP timeout from 30s to 12s (10s bridge + 2s buffer)

Impact:
- Eliminates race conditions when page loads (multiple API calls)
- Prevents USB port conflicts and protocol errors
- Faster response times due to caching
- No need for manual USB resets after container restarts

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 22:02:57 +01:00
MarekWo 4608665e82 refactor: Implement 2-container architecture to solve USB stability issues
BREAKING CHANGE: Switched from single-container to multi-container setup

This commit introduces a meshcore-bridge service that isolates USB device
access from the main application, resolving persistent USB timeout and
deadlock issues in Docker + VM environments.

Changes:
- Add meshcore-bridge/ - Lightweight HTTP API wrapper for meshcli
  - Flask server exposes /cli endpoint (port 5001, internal only)
  - Exclusive USB device access via --device flag
  - Health check endpoint at /health

- Refactor app/meshcore/cli.py
  - Replace subprocess calls with HTTP requests to bridge
  - Add requests library dependency
  - Better error handling for bridge communication

- Update docker-compose.yml
  - Define meshcore-bridge and mc-webui services
  - Create meshcore-net Docker network
  - Add depends_on with health check condition
  - Bridge gets USB device, main app uses HTTP only

- Modify Dockerfile
  - Remove meshcore-cli installation from main app
  - Lighter image without gcc dependencies

- Update config.py
  - Add MC_BRIDGE_URL environment variable
  - Remove meshcli_command property (no longer needed)

- Update documentation (README.md, .claude/instructions.md)
  - Document 2-container architecture
  - Add troubleshooting section for bridge
  - Update prerequisites (no host meshcore-cli needed)
  - Add architecture diagram in project structure

Benefits:
 Solves USB device locking after container restarts
 Restartable main app without USB reset
 Better separation of concerns
 Easier debugging (isolated meshcli logs)
 No manual USB recovery scripts needed

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 08:39:10 +01:00