docs: initial Hermes Mobile planning scaffold
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# dependencies
|
||||
node_modules/
|
||||
.pnpm-store/
|
||||
|
||||
# builds
|
||||
dist/
|
||||
build/
|
||||
.out/
|
||||
.next/
|
||||
coverage/
|
||||
|
||||
# env/secrets
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
*.pem
|
||||
*.key
|
||||
|
||||
# runtime data
|
||||
data/
|
||||
uploads/
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
logs/
|
||||
|
||||
# OS/editor
|
||||
.DS_Store
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
|
||||
# python bridge
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
venv/
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Bonzi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,166 @@
|
||||
# Proposed Project Structure
|
||||
|
||||
```text
|
||||
hermes-mobile/
|
||||
README.md
|
||||
PROJECT_STRUCTURE.md
|
||||
LICENSE
|
||||
.gitignore
|
||||
.editorconfig
|
||||
package.json # pnpm workspace root, later
|
||||
pnpm-workspace.yaml
|
||||
tsconfig.base.json
|
||||
install.sh # one-liner installer entrypoint, later
|
||||
docs/
|
||||
PRODUCT_PLAN.md
|
||||
ARCHITECTURE.md
|
||||
DESIGN_SYSTEM.md
|
||||
COMPANION_SERVER.md
|
||||
MOBILE_APP.md
|
||||
INSTALL.md
|
||||
TESTING.md
|
||||
ROADMAP.md
|
||||
apps/
|
||||
mobile/ # Android-first PWA
|
||||
package.json
|
||||
vite.config.ts
|
||||
index.html
|
||||
public/
|
||||
manifest.webmanifest
|
||||
icons/
|
||||
src/
|
||||
app/
|
||||
App.tsx
|
||||
router.tsx
|
||||
providers.tsx
|
||||
screens/
|
||||
AskScreen/
|
||||
ActivityScreen/
|
||||
CronScreen/
|
||||
FilesScreen/
|
||||
SettingsScreen/
|
||||
components/
|
||||
agent-status-orb/
|
||||
composer/
|
||||
tool-card/
|
||||
event-timeline/
|
||||
upload-tray/
|
||||
voice-recorder/
|
||||
cron-job-card/
|
||||
approval-card/
|
||||
bottom-nav/
|
||||
app-shell/
|
||||
features/
|
||||
tasks/
|
||||
sessions/
|
||||
cron/
|
||||
files/
|
||||
approvals/
|
||||
notifications/
|
||||
pairing/
|
||||
settings/
|
||||
lib/
|
||||
api-client.ts
|
||||
realtime.ts
|
||||
push.ts
|
||||
audio.ts
|
||||
file-utils.ts
|
||||
format.ts
|
||||
styles/
|
||||
globals.css
|
||||
tokens.css
|
||||
main.tsx
|
||||
companion/ # local server installed beside Hermes Agent
|
||||
package.json
|
||||
src/
|
||||
app.ts
|
||||
config/
|
||||
index.ts
|
||||
schema.ts
|
||||
auth/
|
||||
pairing.ts
|
||||
sessions.ts
|
||||
middleware.ts
|
||||
db/
|
||||
client.ts
|
||||
migrations/
|
||||
repositories/
|
||||
events/
|
||||
bus.ts
|
||||
types.ts
|
||||
websocket.ts
|
||||
sse.ts
|
||||
hermes/
|
||||
HermesAdapter.ts
|
||||
CliHermesAdapter.ts
|
||||
ApiHermesAdapter.ts
|
||||
PythonHermesAdapter.ts
|
||||
promptFormatting.ts
|
||||
tasks/
|
||||
TaskManager.ts
|
||||
TaskQueue.ts
|
||||
taskTypes.ts
|
||||
uploads/
|
||||
uploadRoutes.ts
|
||||
UploadStore.ts
|
||||
fileValidation.ts
|
||||
notifications/
|
||||
NotificationProvider.ts
|
||||
WebPushProvider.ts
|
||||
NtfyProvider.ts
|
||||
GotifyProvider.ts
|
||||
NoopProvider.ts
|
||||
cron/
|
||||
CronService.ts
|
||||
HermesCronAdapter.ts
|
||||
approvals/
|
||||
ApprovalService.ts
|
||||
routes/
|
||||
health.ts
|
||||
tasks.ts
|
||||
uploads.ts
|
||||
sessions.ts
|
||||
cron.ts
|
||||
approvals.ts
|
||||
settings.ts
|
||||
system/
|
||||
hermesHealth.ts
|
||||
logs.ts
|
||||
index.ts
|
||||
scripts/
|
||||
hermes_worker.py # direct Python bridge later
|
||||
packages/
|
||||
shared/ # shared TS types/schemas
|
||||
package.json
|
||||
src/
|
||||
events.ts
|
||||
api.ts
|
||||
tasks.ts
|
||||
cron.ts
|
||||
files.ts
|
||||
notifications.ts
|
||||
ui/ # optional shared UI primitives later
|
||||
package.json
|
||||
src/
|
||||
scripts/
|
||||
install.sh
|
||||
uninstall.sh
|
||||
dev-hermes-smoke.sh
|
||||
build-release.sh
|
||||
deploy/
|
||||
systemd/
|
||||
hermes-mobile-companion.service
|
||||
nginx/
|
||||
hermes-mobile.conf
|
||||
caddy/
|
||||
Caddyfile.example
|
||||
```
|
||||
|
||||
## Modularity Notes
|
||||
|
||||
- `apps/mobile` should never import Hermes-specific server internals directly. It uses `packages/shared` schemas and API client only.
|
||||
- `apps/companion/src/hermes` is the only layer that knows how to talk to Hermes Agent.
|
||||
- `apps/companion/src/notifications` is provider-swappable.
|
||||
- `apps/companion/src/cron` wraps Hermes cron rather than replacing it.
|
||||
- `packages/shared` defines stable event types used by both app and server.
|
||||
- `scripts/` and `deploy/` stay boring and auditable.
|
||||
@@ -0,0 +1,153 @@
|
||||
# Hermes Mobile
|
||||
|
||||
Native-feeling Android-first PWA and companion server for [Hermes Agent](https://github.com/NousResearch/hermes-agent).
|
||||
|
||||
Hermes Mobile is not a generic LLM chat UI. It is a self-hosted, Hermes-native control surface designed for phone use: prompt by text or voice, upload files, watch tool calls live, approve actions, manage cron jobs, and get notified when long-running agent work is done.
|
||||
|
||||
Repo: https://git.molberg.cloud/bonzi/hermes-mobile
|
||||
|
||||
## Status
|
||||
|
||||
Planning scaffold only. No production code yet.
|
||||
|
||||
## Product Goals
|
||||
|
||||
- Feel like a real Android app, not a web page pretending to be chat.
|
||||
- Run privately on the same Linux machine as Hermes Agent.
|
||||
- Keep Hermes Agent untouched where possible; operate as a companion layer above/alongside it.
|
||||
- Expose Hermes-specific concepts: tool calls, sessions, jobs, generated files, approvals, background tasks.
|
||||
- Make installation one-liner simple for a homelab machine.
|
||||
- Stay modular enough that the app, companion server, Hermes integration, notification providers, and install scripts can evolve independently.
|
||||
|
||||
## Planned Components
|
||||
|
||||
```text
|
||||
Android / mobile browser
|
||||
|
|
||||
| HTTPS + WebSocket/SSE + Web Push
|
||||
v
|
||||
Hermes Mobile App (PWA / later Capacitor)
|
||||
|
|
||||
v
|
||||
Hermes Companion Server
|
||||
|
|
||||
+-- Hermes CLI / Python AIAgent bridge
|
||||
+-- Hermes API server bridge
|
||||
+-- Hermes session DB reader
|
||||
+-- Hermes cron manager
|
||||
+-- Upload/file store
|
||||
+-- Push notification provider
|
||||
```
|
||||
|
||||
## MVP Feature Set
|
||||
|
||||
1. Mobile-first app shell
|
||||
- Installable PWA
|
||||
- Android-style bottom navigation
|
||||
- Offline-friendly shell
|
||||
- Dark, playful visual system inspired by happy.engineering / Happy app
|
||||
|
||||
2. Chat / prompt screen
|
||||
- Text prompts
|
||||
- Voice note recording
|
||||
- File uploads: zip, png, jpg, pdf, txt, logs, folders later
|
||||
- Streaming responses
|
||||
- “Agent is working” state
|
||||
- Completion notification
|
||||
|
||||
3. Hermes-native activity timeline
|
||||
- Tool call started / finished
|
||||
- Terminal commands
|
||||
- File reads/writes
|
||||
- Browser actions
|
||||
- Home Assistant actions
|
||||
- Cron changes
|
||||
- Subagent delegation
|
||||
- Expand/collapse raw output
|
||||
|
||||
4. Sessions
|
||||
- Recent sessions
|
||||
- Resume session
|
||||
- Rename / pin / archive
|
||||
- Per-session attachments and generated files
|
||||
|
||||
5. Cron
|
||||
- List cron jobs
|
||||
- Run now
|
||||
- Pause/resume/remove
|
||||
- View last output
|
||||
- Delivery target: app notification / in-app inbox
|
||||
|
||||
6. Approvals
|
||||
- Push approval to phone for dangerous commands
|
||||
- Approve / deny with short audit log
|
||||
|
||||
7. Companion settings
|
||||
- Connect to a companion server URL
|
||||
- Pair using one-time code or passkey
|
||||
- Configure notification backend
|
||||
- View Hermes health/status
|
||||
|
||||
## Docs
|
||||
|
||||
- [Product Plan](docs/PRODUCT_PLAN.md)
|
||||
- [Architecture](docs/ARCHITECTURE.md)
|
||||
- [Design System](docs/DESIGN_SYSTEM.md)
|
||||
- [Companion Server](docs/COMPANION_SERVER.md)
|
||||
- [Mobile App Structure](docs/MOBILE_APP.md)
|
||||
- [Install Strategy](docs/INSTALL.md)
|
||||
- [Testing Strategy](docs/TESTING.md)
|
||||
- [Roadmap](docs/ROADMAP.md)
|
||||
|
||||
## Proposed Stack
|
||||
|
||||
Frontend:
|
||||
- TypeScript
|
||||
- React + Vite or Next.js static/PWA mode
|
||||
- Tailwind CSS
|
||||
- Zustand or TanStack Query for client state
|
||||
- Service Worker + Web Push
|
||||
- Capacitor later, only if PWA limitations matter
|
||||
|
||||
Companion server:
|
||||
- TypeScript Node.js initially, because file uploads, WebSocket/SSE, install scripts, and PM2/systemd are straightforward
|
||||
- Fastify or Hono
|
||||
- SQLite for app metadata
|
||||
- Local filesystem upload store
|
||||
- Python bridge subprocess or direct Hermes module calls where safe
|
||||
|
||||
Why not Open WebUI/LibreChat:
|
||||
- They are model chat UIs.
|
||||
- Hermes needs agent-native events, cron, approvals, file artifacts, process logs, sessions, and tool timelines.
|
||||
- Forking a generic app would probably become more work than a clean, small purpose-built app.
|
||||
|
||||
## One-liner install target
|
||||
|
||||
Final goal:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.molberg.cloud/bonzi/hermes-mobile/raw/branch/main/install.sh | bash
|
||||
```
|
||||
|
||||
or, for local/self-hosted raw auth limitations, a release artifact URL:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash
|
||||
```
|
||||
|
||||
The installer should:
|
||||
- Detect Hermes Agent install and config path
|
||||
- Create a system user or use current user depending mode
|
||||
- Install Node runtime if missing
|
||||
- Download release bundle
|
||||
- Create config at `/etc/hermes-mobile/config.yaml` or `~/.config/hermes-mobile/config.yaml`
|
||||
- Configure systemd service `hermes-mobile-companion`
|
||||
- Print pairing URL / one-time setup code
|
||||
|
||||
## Development Philosophy
|
||||
|
||||
- Companion server owns mobile UX state, not Hermes core.
|
||||
- Hermes remains the agent runtime/source of truth for actual work.
|
||||
- Every Hermes integration goes through an adapter interface so we can switch between CLI, API server, direct Python, or future Hermes-native event APIs.
|
||||
- Tool-call UI should prefer structured events, but gracefully degrade to parsed CLI/API logs during early versions.
|
||||
- Keep secrets out of repo. Use environment/config files and future vault integration.
|
||||
@@ -0,0 +1,311 @@
|
||||
# Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
Hermes Mobile is split into two deployable parts:
|
||||
|
||||
1. Mobile app
|
||||
- Installable PWA designed for Android.
|
||||
- Talks only to the companion server.
|
||||
- Does not need direct access to Hermes files or credentials.
|
||||
|
||||
2. Companion server
|
||||
- Runs on the same Linux machine as Hermes Agent.
|
||||
- Bridges mobile UI actions to Hermes Agent.
|
||||
- Owns upload storage, push notifications, pairing/auth, app metadata, and realtime event fanout.
|
||||
|
||||
```text
|
||||
+---------------------------+
|
||||
| Android PWA |
|
||||
| - Chat |
|
||||
| - Tool timeline |
|
||||
| - Cron UI |
|
||||
| - Files |
|
||||
| - Approvals |
|
||||
+-------------+-------------+
|
||||
|
|
||||
| HTTPS REST + WebSocket/SSE + Web Push
|
||||
v
|
||||
+---------------------------+
|
||||
| Hermes Companion Server |
|
||||
| - Auth/pairing |
|
||||
| - Task orchestrator |
|
||||
| - Hermes adapters |
|
||||
| - Event bus |
|
||||
| - Upload store |
|
||||
| - Cron wrapper |
|
||||
| - Notification providers |
|
||||
+------+------+-------------+
|
||||
| |
|
||||
| +--------------------+
|
||||
| |
|
||||
v v
|
||||
+-------------+ +--------------+
|
||||
| Hermes CLI | | Hermes files |
|
||||
| Hermes API | | sessions/db |
|
||||
| AIAgent | | cron jobs |
|
||||
+-------------+ +--------------+
|
||||
```
|
||||
|
||||
## Key Architectural Rule
|
||||
|
||||
The companion server should depend on stable Hermes surfaces first, but isolate every integration behind interfaces.
|
||||
|
||||
```ts
|
||||
interface HermesAdapter {
|
||||
health(): Promise<HermesHealth>
|
||||
startTask(input: StartTaskInput): AsyncIterable<HermesEvent>
|
||||
continueSession(sessionId: string, input: StartTaskInput): AsyncIterable<HermesEvent>
|
||||
stopTask(taskId: string): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
Possible implementations:
|
||||
- `CliHermesAdapter`: spawns `hermes chat -q` or interactive process. Easiest baseline, weakest event detail.
|
||||
- `ApiHermesAdapter`: calls Hermes API server. Good for compatibility, but generic OpenAI API hides some tool events.
|
||||
- `PythonHermesAdapter`: imports Hermes Agent modules and uses callbacks for stream/tool progress. Best long-term event fidelity.
|
||||
- `GatewayHermesAdapter`: uses existing gateway/session machinery if exposed.
|
||||
|
||||
## Companion Server Modules
|
||||
|
||||
```text
|
||||
companion/
|
||||
src/
|
||||
app.ts # server bootstrap
|
||||
config/ # env/yaml config loading
|
||||
auth/ # pairing, sessions, tokens, passkeys later
|
||||
db/ # SQLite schema/migrations/repositories
|
||||
events/ # event bus + SSE/WebSocket fanout
|
||||
hermes/ # adapter interface + implementations
|
||||
tasks/ # task lifecycle/orchestration
|
||||
uploads/ # multipart upload handling + retention
|
||||
notifications/ # webpush/ntfy/gotify providers
|
||||
cron/ # Hermes cron wrapper
|
||||
approvals/ # approval queue + Hermes callbacks later
|
||||
routes/ # REST API routes
|
||||
ws/ # realtime endpoints
|
||||
system/ # service health, logs, install info
|
||||
```
|
||||
|
||||
## Event Bus
|
||||
|
||||
Everything that happens in the companion becomes an event.
|
||||
|
||||
Core event shape:
|
||||
|
||||
```ts
|
||||
type CompanionEvent = {
|
||||
id: string
|
||||
type: string
|
||||
taskId?: string
|
||||
sessionId?: string
|
||||
ts: string
|
||||
level?: 'debug' | 'info' | 'warn' | 'error'
|
||||
payload: unknown
|
||||
}
|
||||
```
|
||||
|
||||
Important event types:
|
||||
|
||||
```text
|
||||
task.created
|
||||
task.started
|
||||
task.status
|
||||
task.finished
|
||||
task.failed
|
||||
task.cancelled
|
||||
|
||||
assistant.delta
|
||||
assistant.message
|
||||
assistant.final
|
||||
|
||||
tool_call.started
|
||||
tool_call.output
|
||||
tool_call.finished
|
||||
tool_call.failed
|
||||
|
||||
file.uploaded
|
||||
file.generated
|
||||
|
||||
approval.required
|
||||
approval.resolved
|
||||
|
||||
cron.job.listed
|
||||
cron.job.updated
|
||||
cron.job.finished
|
||||
|
||||
notification.sent
|
||||
notification.failed
|
||||
```
|
||||
|
||||
The mobile app subscribes by:
|
||||
- WebSocket for active app foreground.
|
||||
- SSE fallback.
|
||||
- Push notifications when app is background/closed.
|
||||
|
||||
## Data Storage
|
||||
|
||||
### SQLite
|
||||
|
||||
Companion-owned metadata:
|
||||
- paired devices
|
||||
- auth sessions
|
||||
- tasks
|
||||
- task events
|
||||
- uploaded files metadata
|
||||
- notification subscriptions
|
||||
- user settings/cache
|
||||
|
||||
Do not duplicate full Hermes session content unless needed for mobile indexing. Hermes session DB remains source of truth.
|
||||
|
||||
### Filesystem
|
||||
|
||||
Default paths:
|
||||
|
||||
```text
|
||||
/var/lib/hermes-mobile/
|
||||
hermes-mobile.db
|
||||
uploads/
|
||||
<task-id>/
|
||||
generated/
|
||||
cache/
|
||||
logs/
|
||||
```
|
||||
|
||||
User-mode install paths:
|
||||
|
||||
```text
|
||||
~/.local/share/hermes-mobile/
|
||||
~/.config/hermes-mobile/config.yaml
|
||||
```
|
||||
|
||||
## API Surface
|
||||
|
||||
### Auth / Pairing
|
||||
|
||||
```http
|
||||
POST /api/pair/start
|
||||
POST /api/pair/confirm
|
||||
POST /api/auth/login
|
||||
POST /api/auth/logout
|
||||
GET /api/me
|
||||
```
|
||||
|
||||
Pairing flow:
|
||||
1. Installer prints `https://host/pair?code=123-456`.
|
||||
2. User opens on phone.
|
||||
3. Server binds device and issues token.
|
||||
4. Future sessions use secure cookie or bearer token.
|
||||
|
||||
### Tasks
|
||||
|
||||
```http
|
||||
POST /api/tasks
|
||||
GET /api/tasks
|
||||
GET /api/tasks/:id
|
||||
POST /api/tasks/:id/stop
|
||||
GET /api/tasks/:id/events
|
||||
WS /api/tasks/:id/stream
|
||||
```
|
||||
|
||||
### Uploads
|
||||
|
||||
```http
|
||||
POST /api/uploads
|
||||
GET /api/files
|
||||
GET /api/files/:id/download
|
||||
DELETE /api/files/:id
|
||||
```
|
||||
|
||||
### Sessions
|
||||
|
||||
```http
|
||||
GET /api/sessions
|
||||
GET /api/sessions/:id
|
||||
POST /api/sessions/:id/continue
|
||||
PATCH /api/sessions/:id
|
||||
```
|
||||
|
||||
### Cron
|
||||
|
||||
```http
|
||||
GET /api/cron/jobs
|
||||
POST /api/cron/jobs/:id/run
|
||||
POST /api/cron/jobs/:id/pause
|
||||
POST /api/cron/jobs/:id/resume
|
||||
DELETE /api/cron/jobs/:id
|
||||
```
|
||||
|
||||
### System
|
||||
|
||||
```http
|
||||
GET /api/health
|
||||
GET /api/hermes/health
|
||||
POST /api/hermes/test
|
||||
GET /api/logs
|
||||
```
|
||||
|
||||
## Hermes Integration Notes
|
||||
|
||||
Existing useful surfaces observed:
|
||||
- Hermes API server exposes `/v1/chat/completions`, `/v1/responses`, `/v1/models`, `/health`.
|
||||
- `AIAgent` supports `stream_delta_callback` and `tool_progress_callback` constructor params.
|
||||
- Cron jobs are manageable through Hermes CLI/tooling and stored under Hermes home.
|
||||
- Gateway platforms already handle STT/media patterns, but mobile should not depend on Telegram.
|
||||
|
||||
Long-term ideal:
|
||||
- Add/consume a Hermes-native event stream from AIAgent that emits structured tool events.
|
||||
- Companion server can either import Hermes modules or call a local IPC endpoint.
|
||||
|
||||
## Security Model
|
||||
|
||||
Default deployments:
|
||||
1. LAN/Tailscale only, simple pairing token.
|
||||
2. Public HTTPS, pairing token + device token + optional passkey.
|
||||
|
||||
Security requirements:
|
||||
- No unauthenticated access to prompt endpoints.
|
||||
- Upload size limits.
|
||||
- File paths normalized; no arbitrary file read from upload endpoints.
|
||||
- CSRF protection if cookie auth used.
|
||||
- Audit log for approvals.
|
||||
- Secrets remain in Hermes config/vault, not in mobile app.
|
||||
|
||||
## Notification Architecture
|
||||
|
||||
Provider interface:
|
||||
|
||||
```ts
|
||||
interface NotificationProvider {
|
||||
send(device: Device, message: NotificationMessage): Promise<void>
|
||||
test(device: Device): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
Providers:
|
||||
- `WebPushProvider`
|
||||
- `NtfyProvider`
|
||||
- `GotifyProvider`
|
||||
- `NoopProvider`
|
||||
|
||||
## Deployment Modes
|
||||
|
||||
### User mode
|
||||
|
||||
- Runs as current user.
|
||||
- Uses `~/.config/hermes-mobile` and `~/.local/share/hermes-mobile`.
|
||||
- Good for dev/single-user boxes.
|
||||
|
||||
### System mode
|
||||
|
||||
- Creates `hermes-mobile` system user or runs under Hermes user.
|
||||
- Uses `/etc/hermes-mobile` and `/var/lib/hermes-mobile`.
|
||||
- systemd service.
|
||||
- Better production posture.
|
||||
|
||||
## Future Native Wrapper
|
||||
|
||||
If Android PWA is insufficient:
|
||||
- Add Capacitor app under `apps/android-shell`.
|
||||
- Reuse same web app.
|
||||
- Gain native share target, richer push, microphone/file APIs.
|
||||
@@ -0,0 +1,254 @@
|
||||
# Companion Server Plan
|
||||
|
||||
## Purpose
|
||||
|
||||
The companion server is the local service installed beside Hermes Agent. It turns Hermes into a phone-friendly app backend without forcing Hermes core to become a mobile backend.
|
||||
|
||||
Responsibilities:
|
||||
- Authenticate/pair phones
|
||||
- Accept prompts, voice recordings, and files
|
||||
- Start/continue Hermes tasks
|
||||
- Stream Hermes activity to the app
|
||||
- Store app metadata
|
||||
- Send push notifications
|
||||
- Expose cron/job/session/file APIs
|
||||
- Provide health checks and install/update status
|
||||
|
||||
## Runtime
|
||||
|
||||
Preferred MVP runtime: Node.js + TypeScript.
|
||||
|
||||
Reasons:
|
||||
- Great fit for HTTP/WebSocket/SSE/file uploads.
|
||||
- Easy bundling for one-liner installs.
|
||||
- PM2/systemd deployment is straightforward in Zeb's environment.
|
||||
- Frontend and backend can share TypeScript event schemas.
|
||||
|
||||
Potential framework:
|
||||
- Fastify: mature, fast, good plugins.
|
||||
- Hono: simple, edge-like API, also good.
|
||||
|
||||
Initial pick: Fastify unless we want ultra-minimal.
|
||||
|
||||
## Config
|
||||
|
||||
Config locations:
|
||||
|
||||
System mode:
|
||||
```text
|
||||
/etc/hermes-mobile/config.yaml
|
||||
/var/lib/hermes-mobile/
|
||||
```
|
||||
|
||||
User mode:
|
||||
```text
|
||||
~/.config/hermes-mobile/config.yaml
|
||||
~/.local/share/hermes-mobile/
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
host: 0.0.0.0
|
||||
port: 8787
|
||||
public_url: https://hermes-mobile.molberg.cloud
|
||||
|
||||
auth:
|
||||
pairing_enabled: true
|
||||
token_ttl_days: 90
|
||||
passkeys_enabled: false
|
||||
|
||||
hermes:
|
||||
home: /root/.hermes
|
||||
mode: python # python | api | cli
|
||||
cli_bin: /root/.local/bin/hermes
|
||||
api_base_url: http://127.0.0.1:18793/v1
|
||||
api_key: ""
|
||||
|
||||
storage:
|
||||
data_dir: /var/lib/hermes-mobile
|
||||
max_upload_mb: 512
|
||||
retention_days: 30
|
||||
|
||||
notifications:
|
||||
provider: webpush # webpush | ntfy | gotify | none
|
||||
ntfy_url: https://ntfy.example.com/hermes
|
||||
```
|
||||
|
||||
## Hermes Adapter Strategy
|
||||
|
||||
### Phase 1: CLI/API hybrid
|
||||
|
||||
- Use Hermes CLI for health tests and simple prompts.
|
||||
- Use Hermes API server if available for OpenAI-compatible chat.
|
||||
- Parse coarse progress where possible.
|
||||
|
||||
Pros:
|
||||
- Requires few/no Hermes changes.
|
||||
- Easy to prove app UX.
|
||||
|
||||
Cons:
|
||||
- Tool-call visibility limited.
|
||||
|
||||
### Phase 2: Direct Python adapter
|
||||
|
||||
Run a Python worker process that imports Hermes `AIAgent` and communicates with Node over stdio or local socket.
|
||||
|
||||
Benefits:
|
||||
- Can pass `stream_delta_callback` and `tool_progress_callback`.
|
||||
- More structured event stream.
|
||||
- Better session control.
|
||||
|
||||
Potential shape:
|
||||
|
||||
```text
|
||||
Node companion <-- JSON lines --> Python hermes_worker.py <-- imports --> AIAgent
|
||||
```
|
||||
|
||||
Worker commands:
|
||||
- `health`
|
||||
- `start_task`
|
||||
- `continue_session`
|
||||
- `cancel_task`
|
||||
- `list_sessions`
|
||||
- `cron_action`
|
||||
|
||||
Worker emits events:
|
||||
- `assistant.delta`
|
||||
- `tool_call.started`
|
||||
- `tool_call.output`
|
||||
- `tool_call.finished`
|
||||
- `assistant.final`
|
||||
|
||||
### Phase 3: Hermes-native plugin/API
|
||||
|
||||
If Hermes Agent grows a stable event API, switch adapter implementation without rewriting app/server.
|
||||
|
||||
## Task Lifecycle
|
||||
|
||||
```text
|
||||
created -> queued -> running -> finished
|
||||
-> failed
|
||||
-> cancelled
|
||||
-> waiting_approval
|
||||
```
|
||||
|
||||
Task record:
|
||||
|
||||
```ts
|
||||
type Task = {
|
||||
id: string
|
||||
sessionId?: string
|
||||
title?: string
|
||||
status: TaskStatus
|
||||
inputText: string
|
||||
attachmentIds: string[]
|
||||
createdAt: string
|
||||
startedAt?: string
|
||||
finishedAt?: string
|
||||
error?: string
|
||||
}
|
||||
```
|
||||
|
||||
## Upload Handling
|
||||
|
||||
Use multipart upload endpoint.
|
||||
|
||||
Pipeline:
|
||||
1. Validate file count/size/type.
|
||||
2. Store under `uploads/<task-id-or-upload-id>/original-name`.
|
||||
3. Record metadata in SQLite.
|
||||
4. Include absolute file paths in Hermes prompt.
|
||||
|
||||
Prompt augmentation example:
|
||||
|
||||
```text
|
||||
The user uploaded these files:
|
||||
- screenshot.png: /var/lib/hermes-mobile/uploads/abc/screenshot.png (image/png, 320 KB)
|
||||
- source.zip: /var/lib/hermes-mobile/uploads/abc/source.zip (application/zip, 12 MB)
|
||||
|
||||
User prompt:
|
||||
<actual user prompt>
|
||||
```
|
||||
|
||||
## Voice Handling
|
||||
|
||||
MVP options:
|
||||
|
||||
1. Client records `.webm`/`.ogg`, server stores it, then sends file path to Hermes with instruction to transcribe/analyze.
|
||||
2. Server transcribes with local faster-whisper and sends text prompt.
|
||||
|
||||
Recommended MVP:
|
||||
- Server-side transcription using local faster-whisper if available.
|
||||
- Fallback: pass audio file to Hermes as attachment.
|
||||
|
||||
## Notifications
|
||||
|
||||
Events that trigger notifications:
|
||||
- task completed after app backgrounded
|
||||
- task failed
|
||||
- approval required
|
||||
- cron job finished/failed
|
||||
|
||||
Use provider abstraction:
|
||||
|
||||
```ts
|
||||
interface NotificationProvider {
|
||||
sendCompletion(task: Task): Promise<void>
|
||||
sendFailure(task: Task): Promise<void>
|
||||
sendApproval(approval: Approval): Promise<void>
|
||||
sendCron(job: CronEvent): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
## Pairing/Auth
|
||||
|
||||
MVP pairing:
|
||||
1. Installer/server logs one-time code.
|
||||
2. Phone opens `/pair` and enters code or uses link.
|
||||
3. Server stores device and creates token.
|
||||
4. Token stored in secure-ish PWA storage. Later use passkeys.
|
||||
|
||||
For public exposure, add:
|
||||
- Rate limiting
|
||||
- Token rotation
|
||||
- Session revoke UI
|
||||
- Optional Tailscale-only mode
|
||||
|
||||
## Cron Integration
|
||||
|
||||
Initial implementation can shell out to Hermes CLI:
|
||||
|
||||
```bash
|
||||
hermes cron list --json
|
||||
hermes cron run <id>
|
||||
hermes cron pause <id>
|
||||
hermes cron resume <id>
|
||||
```
|
||||
|
||||
If CLI JSON is not available, import cron modules from Hermes Python or parse job files carefully.
|
||||
|
||||
## System Health
|
||||
|
||||
Health endpoint should check:
|
||||
- companion server up
|
||||
- DB writable
|
||||
- upload dir writable
|
||||
- Hermes CLI exists
|
||||
- Hermes config exists
|
||||
- Hermes API server reachable if configured
|
||||
- Python worker reachable if configured
|
||||
- notification provider configured
|
||||
|
||||
## Logging
|
||||
|
||||
- Structured JSON logs for service.
|
||||
- Human-readable recent logs in UI.
|
||||
- Do not log secrets, bearer tokens, or full uploaded file content.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Should companion run as same user as Hermes Agent for direct module/file access?
|
||||
- Should public access be Cloudflare Access/Tailscale-first instead of app auth-first?
|
||||
- How much Hermes core should be extended for better event streaming?
|
||||
@@ -0,0 +1,222 @@
|
||||
# Design System
|
||||
|
||||
## Visual Direction
|
||||
|
||||
Target: visually inspired by `happy.engineering` and the Happy app: polished, playful, soft, modern, high-motion, emotionally warm, and app-like.
|
||||
|
||||
Not a terminal dashboard. Not a generic ChatGPT clone. It should feel like a friendly command center for a personal agent.
|
||||
|
||||
## Keywords
|
||||
|
||||
- Native Android feel
|
||||
- Playful but competent
|
||||
- Soft cards
|
||||
- Friendly gradients
|
||||
- Rounded app surfaces
|
||||
- Large touch targets
|
||||
- Motion-rich microinteractions
|
||||
- Calm dark mode
|
||||
- “Agent is alive” status cues
|
||||
|
||||
## Initial Theme Direction
|
||||
|
||||
Because Zeb likes both stark terminal aesthetics and polished UIs, Hermes Mobile should combine:
|
||||
|
||||
- Happy-style colorful/soft app surfaces
|
||||
- Hermes identity through icons, agent activity, and subtle terminal/tool-call details
|
||||
- Dark-first interface for phone use
|
||||
- Optional light mode later
|
||||
|
||||
## Color Palette Draft
|
||||
|
||||
```text
|
||||
Background: #080A0F
|
||||
Surface: #10141D
|
||||
Elevated surface: #171D29
|
||||
Card border: rgba(255,255,255,0.08)
|
||||
Text primary: #F7F8FC
|
||||
Text secondary: #AAB2C5
|
||||
Muted: #687085
|
||||
|
||||
Hermes gold: #F2C14E
|
||||
Happy blue: #6EA8FF
|
||||
Happy purple: #A78BFA
|
||||
Happy green: #63E6BE
|
||||
Danger coral: #FF6B6B
|
||||
Warning amber: #FFD166
|
||||
```
|
||||
|
||||
Gradients:
|
||||
|
||||
```css
|
||||
--gradient-orb: radial-gradient(circle at 30% 20%, #6EA8FF 0%, #A78BFA 45%, #080A0F 100%);
|
||||
--gradient-action: linear-gradient(135deg, #6EA8FF, #A78BFA, #63E6BE);
|
||||
--gradient-hermes: linear-gradient(135deg, #F2C14E, #FF8A65);
|
||||
```
|
||||
|
||||
## Typography
|
||||
|
||||
Primary UI:
|
||||
- Inter, Geist, or system Android sans.
|
||||
|
||||
Mono/details:
|
||||
- JetBrains Mono for tool outputs, command snippets, paths, logs.
|
||||
|
||||
Type scale:
|
||||
- Screen title: 28-34px, 700
|
||||
- Section title: 18-20px, 650
|
||||
- Body: 15-16px
|
||||
- Metadata: 12-13px
|
||||
- Tool output: 12-13px mono
|
||||
|
||||
## Layout
|
||||
|
||||
### Mobile shell
|
||||
|
||||
- Full-screen app layout.
|
||||
- Safe-area aware.
|
||||
- Bottom navigation with 4-5 tabs:
|
||||
- Ask
|
||||
- Activity
|
||||
- Cron
|
||||
- Files
|
||||
- Settings
|
||||
|
||||
### Ask screen
|
||||
|
||||
```text
|
||||
┌─────────────────────────┐
|
||||
│ Hermes status orb │
|
||||
│ │
|
||||
│ Recent / current task │
|
||||
│ ┌───────────────────┐ │
|
||||
│ │ assistant answer │ │
|
||||
│ └───────────────────┘ │
|
||||
│ ┌───────────────────┐ │
|
||||
│ │ tool timeline │ │
|
||||
│ └───────────────────┘ │
|
||||
│ │
|
||||
│ [ + ] [ hold voice ] │
|
||||
│ [ prompt input ↑ ] │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
Input should be thumb-first:
|
||||
- Attachment left
|
||||
- Voice pill / mic
|
||||
- Send button right
|
||||
- Input grows to 4-6 lines max
|
||||
|
||||
## Components
|
||||
|
||||
### Agent Status Orb
|
||||
|
||||
A small animated orb/avatar indicating:
|
||||
- idle
|
||||
- thinking
|
||||
- using tools
|
||||
- waiting for approval
|
||||
- done
|
||||
- error
|
||||
|
||||
This gives the app life without pretending to be a person.
|
||||
|
||||
### Tool Card
|
||||
|
||||
States:
|
||||
- queued
|
||||
- running
|
||||
- success
|
||||
- failed
|
||||
- cancelled
|
||||
|
||||
Collapsed view:
|
||||
```text
|
||||
[terminal icon] terminal • 2.4s • success
|
||||
npm test
|
||||
```
|
||||
|
||||
Expanded view:
|
||||
- Arguments summary
|
||||
- Output preview
|
||||
- Copy button
|
||||
- Open file if applicable
|
||||
- Error highlighting
|
||||
|
||||
### Timeline
|
||||
|
||||
Vertical event feed with icons and subtle connecting line.
|
||||
|
||||
Events:
|
||||
- Prompt received
|
||||
- Tool started
|
||||
- Tool output
|
||||
- File generated
|
||||
- Notification sent
|
||||
- Final answer
|
||||
|
||||
### Approval Card
|
||||
|
||||
Should feel serious but not scary.
|
||||
|
||||
```text
|
||||
Approval required
|
||||
Hermes wants to run:
|
||||
rm -rf /tmp/build-cache
|
||||
|
||||
Risk: destructive file operation
|
||||
[ Deny ] [ Approve once ]
|
||||
```
|
||||
|
||||
### Cron Job Card
|
||||
|
||||
```text
|
||||
Homelab health check
|
||||
Every day 09:00
|
||||
Last run: success · 2h ago
|
||||
[Run now] [Pause]
|
||||
```
|
||||
|
||||
### Upload Tray
|
||||
|
||||
Shows selected files before sending:
|
||||
- thumbnail for images
|
||||
- file icon for zip/pdf/log
|
||||
- remove button
|
||||
- total size
|
||||
|
||||
## Motion
|
||||
|
||||
Use motion sparingly but meaningfully:
|
||||
- status orb breathing while thinking
|
||||
- tool card expands smoothly
|
||||
- file upload progress liquid/soft bar
|
||||
- send button morphs to spinner
|
||||
- completion haptic-like visual pulse
|
||||
|
||||
Prefer CSS transitions and Framer Motion if bundle size acceptable.
|
||||
|
||||
## Android-native Feeling
|
||||
|
||||
- Installable PWA manifest with proper icons.
|
||||
- Standalone display mode.
|
||||
- No browser chrome when installed.
|
||||
- Bottom nav like native Material apps.
|
||||
- Pull-to-refresh disabled; in-app refresh buttons where needed.
|
||||
- Back gesture should close panels before navigating away.
|
||||
- Use Android share target later so user can share files/text to Hermes.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Minimum 44px tap targets.
|
||||
- Color is not the only status indicator.
|
||||
- Reduced motion support.
|
||||
- Good contrast in dark mode.
|
||||
- Voice actions have text alternatives.
|
||||
|
||||
## Design TODO
|
||||
|
||||
- Capture screenshots/references from happy.engineering and Happy app.
|
||||
- Build a visual moodboard.
|
||||
- Design first mockups for Ask, Tool Timeline, Cron, Settings.
|
||||
- Decide exact logo/avatar direction.
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
# Install Strategy
|
||||
|
||||
## Goal
|
||||
|
||||
A one-liner installer that sets up the companion server beside an existing Hermes Agent install.
|
||||
|
||||
Target UX:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash
|
||||
```
|
||||
|
||||
or from Gitea release/raw if accessible without auth:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.molberg.cloud/bonzi/hermes-mobile/raw/branch/main/install.sh | bash
|
||||
```
|
||||
|
||||
Because Zeb's Gitea raw URLs may require auth, release assets or a mirrored static install URL are safer for the public one-liner.
|
||||
|
||||
## Installer Responsibilities
|
||||
|
||||
1. Detect OS/package manager.
|
||||
2. Detect Hermes Agent:
|
||||
- `command -v hermes`
|
||||
- `~/.hermes/config.yaml`
|
||||
- optional `HERMES_HOME`
|
||||
3. Check Node.js version or install bundled binary/runtime.
|
||||
4. Download latest Hermes Mobile companion release.
|
||||
5. Create config.
|
||||
6. Create data directories.
|
||||
7. Install systemd service.
|
||||
8. Start service.
|
||||
9. Print pairing URL and one-time code.
|
||||
|
||||
## Installation Modes
|
||||
|
||||
### System mode (default for servers)
|
||||
|
||||
Paths:
|
||||
|
||||
```text
|
||||
/opt/hermes-mobile/ app bundle
|
||||
/etc/hermes-mobile/config.yaml config
|
||||
/var/lib/hermes-mobile/ DB/uploads/cache
|
||||
/var/log/hermes-mobile/ logs if not journald-only
|
||||
```
|
||||
|
||||
Service:
|
||||
|
||||
```text
|
||||
hermes-mobile-companion.service
|
||||
```
|
||||
|
||||
Run user:
|
||||
- Option A: current user/root if Hermes is root-installed.
|
||||
- Option B: `hermes-mobile` user with group/read permissions to Hermes paths.
|
||||
|
||||
For Zeb's box, same user/root is simplest initially because Hermes files live under `/root/.hermes`.
|
||||
|
||||
### User mode
|
||||
|
||||
```bash
|
||||
curl ... | bash -s -- --user
|
||||
```
|
||||
|
||||
Paths:
|
||||
|
||||
```text
|
||||
~/.local/opt/hermes-mobile/
|
||||
~/.config/hermes-mobile/config.yaml
|
||||
~/.local/share/hermes-mobile/
|
||||
~/.config/systemd/user/hermes-mobile-companion.service
|
||||
```
|
||||
|
||||
## Service Unit Draft
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Hermes Mobile Companion Server
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/hermes-mobile
|
||||
Environment=NODE_ENV=production
|
||||
Environment=HERMES_MOBILE_CONFIG=/etc/hermes-mobile/config.yaml
|
||||
ExecStart=/usr/bin/node /opt/hermes-mobile/server/index.js
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## Config Generation
|
||||
|
||||
Installer should write:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
host: 0.0.0.0
|
||||
port: 8787
|
||||
public_url: ""
|
||||
|
||||
hermes:
|
||||
home: /root/.hermes
|
||||
cli_bin: /root/.local/bin/hermes
|
||||
mode: python
|
||||
api_base_url: http://127.0.0.1:18793/v1
|
||||
|
||||
storage:
|
||||
data_dir: /var/lib/hermes-mobile
|
||||
max_upload_mb: 512
|
||||
|
||||
auth:
|
||||
pairing_enabled: true
|
||||
|
||||
notifications:
|
||||
provider: webpush
|
||||
```
|
||||
|
||||
## Pairing Output
|
||||
|
||||
After start:
|
||||
|
||||
```text
|
||||
Hermes Mobile installed.
|
||||
|
||||
Open this on your phone:
|
||||
http://SERVER-IP:8787/pair?code=123-456
|
||||
|
||||
Pairing code:
|
||||
123-456
|
||||
|
||||
Service:
|
||||
systemctl status hermes-mobile-companion
|
||||
|
||||
Logs:
|
||||
journalctl -u hermes-mobile-companion -f
|
||||
```
|
||||
|
||||
## Update
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash -s -- --update
|
||||
```
|
||||
|
||||
Should:
|
||||
- Download latest release.
|
||||
- Preserve config and data.
|
||||
- Restart service.
|
||||
- Run DB migrations.
|
||||
|
||||
## Uninstall
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash -s -- --uninstall
|
||||
```
|
||||
|
||||
Should ask/flag whether to keep data:
|
||||
- `--keep-data`
|
||||
- `--purge`
|
||||
|
||||
## Release Layout
|
||||
|
||||
Build artifact:
|
||||
|
||||
```text
|
||||
hermes-mobile-linux-x64.tar.gz
|
||||
server/
|
||||
index.js
|
||||
package.json
|
||||
node_modules/ or bundled single executable
|
||||
public/
|
||||
PWA assets
|
||||
scripts/
|
||||
hermes_worker.py
|
||||
migrations/
|
||||
```
|
||||
|
||||
Better later:
|
||||
- single static binary via Bun/Node SEA if stable enough.
|
||||
- Docker image optional but not the primary install path.
|
||||
|
||||
## One-liner Safety
|
||||
|
||||
Installer should:
|
||||
- Show what it will do.
|
||||
- Avoid deleting data unless explicitly asked.
|
||||
- Not overwrite existing config without backup.
|
||||
- Use `set -euo pipefail`.
|
||||
- Log to temp file.
|
||||
- Verify download checksum if release metadata supports it.
|
||||
|
||||
## Development Install
|
||||
|
||||
From cloned repo:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Companion dev server:
|
||||
|
||||
```bash
|
||||
pnpm --filter companion dev
|
||||
```
|
||||
|
||||
Mobile dev server:
|
||||
|
||||
```bash
|
||||
pnpm --filter mobile dev --host 0.0.0.0
|
||||
```
|
||||
|
||||
## Future Cloudflare/Tailscale
|
||||
|
||||
Expose options:
|
||||
- Tailscale only: bind to `100.x` IP or localhost behind `tailscale serve`.
|
||||
- Cloudflare Tunnel: companion prints local port and expected reverse proxy config.
|
||||
- Nginx/Caddy snippets generated by installer.
|
||||
@@ -0,0 +1,221 @@
|
||||
# Mobile App Plan
|
||||
|
||||
## App Type
|
||||
|
||||
Start as a PWA optimized for Android Chrome.
|
||||
|
||||
Why PWA first:
|
||||
- Fast iteration.
|
||||
- Easy self-hosting.
|
||||
- No app store.
|
||||
- Installable to home screen.
|
||||
- Web Push support on Android is good.
|
||||
- Same frontend can later be wrapped with Capacitor.
|
||||
|
||||
Potential later native shell:
|
||||
- Capacitor Android for native share target, richer notifications, and better file/mic APIs.
|
||||
|
||||
## Frontend Stack
|
||||
|
||||
Recommended:
|
||||
- Vite + React + TypeScript
|
||||
- Tailwind CSS
|
||||
- TanStack Query for server data
|
||||
- Zustand for local UI/session state
|
||||
- Framer Motion for motion where useful
|
||||
- Workbox or Vite PWA plugin
|
||||
- Zod for API schema validation
|
||||
|
||||
Alternative:
|
||||
- Next.js if SSR/server components become useful, but for a companion-served app Vite is simpler.
|
||||
|
||||
## App Package Structure
|
||||
|
||||
```text
|
||||
apps/mobile/
|
||||
src/
|
||||
app/
|
||||
App.tsx
|
||||
router.tsx
|
||||
providers.tsx
|
||||
screens/
|
||||
AskScreen/
|
||||
ActivityScreen/
|
||||
SessionsScreen/
|
||||
CronScreen/
|
||||
FilesScreen/
|
||||
SettingsScreen/
|
||||
components/
|
||||
agent-status-orb/
|
||||
composer/
|
||||
tool-card/
|
||||
event-timeline/
|
||||
upload-tray/
|
||||
voice-recorder/
|
||||
cron-job-card/
|
||||
approval-card/
|
||||
bottom-nav/
|
||||
app-shell/
|
||||
features/
|
||||
tasks/
|
||||
sessions/
|
||||
cron/
|
||||
files/
|
||||
approvals/
|
||||
notifications/
|
||||
pairing/
|
||||
settings/
|
||||
lib/
|
||||
api-client.ts
|
||||
realtime.ts
|
||||
push.ts
|
||||
audio.ts
|
||||
file-utils.ts
|
||||
format.ts
|
||||
styles/
|
||||
globals.css
|
||||
tokens.css
|
||||
assets/
|
||||
icons/
|
||||
splash/
|
||||
main.tsx
|
||||
public/
|
||||
manifest.webmanifest
|
||||
service-worker.ts
|
||||
```
|
||||
|
||||
## Navigation
|
||||
|
||||
Bottom tabs:
|
||||
- Ask
|
||||
- Activity
|
||||
- Cron
|
||||
- Files
|
||||
- Settings
|
||||
|
||||
Sessions can be either:
|
||||
- a sub-screen from Ask, or
|
||||
- included in Activity initially.
|
||||
|
||||
## Ask Screen Behavior
|
||||
|
||||
States:
|
||||
- idle
|
||||
- composing
|
||||
- uploading
|
||||
- sending
|
||||
- running
|
||||
- waiting_approval
|
||||
- finished
|
||||
- failed
|
||||
|
||||
Composer:
|
||||
- Text input
|
||||
- Attach file button
|
||||
- Hold/tap voice recording button
|
||||
- Send button
|
||||
- Optional mode chips: New / Continue / Background
|
||||
|
||||
When task starts:
|
||||
- Input collapses to bottom.
|
||||
- Task timeline appears.
|
||||
- Assistant response streams.
|
||||
- Tool cards appear in chronological order.
|
||||
|
||||
## Realtime
|
||||
|
||||
Use WebSocket first:
|
||||
- `/api/realtime?token=...`
|
||||
- Subscribe to task/session events.
|
||||
|
||||
Fallback:
|
||||
- SSE `/api/tasks/:id/events/stream`
|
||||
- Polling last resort.
|
||||
|
||||
Frontend event store should append events idempotently by event ID.
|
||||
|
||||
## Push Notifications
|
||||
|
||||
PWA flow:
|
||||
1. User enables notifications in Settings.
|
||||
2. App registers service worker.
|
||||
3. App subscribes to PushManager.
|
||||
4. Subscription sent to companion server.
|
||||
5. Server sends Web Push on completion/approval/cron events.
|
||||
|
||||
Fallback if Web Push fails:
|
||||
- Configure ntfy link in Settings.
|
||||
|
||||
## Voice Recording
|
||||
|
||||
Use MediaRecorder.
|
||||
|
||||
Preferred mime order:
|
||||
- `audio/webm;codecs=opus`
|
||||
- `audio/ogg;codecs=opus`
|
||||
- browser default fallback
|
||||
|
||||
UI:
|
||||
- Press/tap mic to start.
|
||||
- Big recording sheet with timer/waveform.
|
||||
- Cancel / send.
|
||||
- Upload progress.
|
||||
|
||||
## File Upload UX
|
||||
|
||||
- Android file picker through `<input type=file multiple>`.
|
||||
- Share Target API later for “Share to Hermes”.
|
||||
- Preview thumbnails for images.
|
||||
- File chips for archives/docs.
|
||||
- Upload before task send or as part of task multipart.
|
||||
|
||||
## Offline/Background Behavior
|
||||
|
||||
- App shell should load offline.
|
||||
- Draft prompt persists locally.
|
||||
- Running task state reloads from server after reconnect.
|
||||
- No attempt to run Hermes offline.
|
||||
|
||||
## Settings UX
|
||||
|
||||
First launch:
|
||||
1. Enter companion URL or scan QR/pairing link.
|
||||
2. Pair device.
|
||||
3. Test connection.
|
||||
4. Enable notifications.
|
||||
5. Land on Ask.
|
||||
|
||||
Settings fields:
|
||||
- Companion URL
|
||||
- Device name
|
||||
- Notification status/test
|
||||
- Theme
|
||||
- Default task mode
|
||||
- Upload retention display
|
||||
- Hermes health
|
||||
|
||||
## Android Polish Checklist
|
||||
|
||||
- `display: standalone` manifest.
|
||||
- Proper app icon masks/adaptive icon assets.
|
||||
- Theme color matching dark background.
|
||||
- Avoid viewport resize bugs with keyboard.
|
||||
- Safe-area padding.
|
||||
- Disable browser pull-to-refresh where possible and add in-app refresh.
|
||||
- Haptics via Vibration API where appropriate.
|
||||
- Back button behavior for modals/sheets.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Visible focus states.
|
||||
- Reduced motion mode.
|
||||
- ARIA labels for icon buttons.
|
||||
- Captions/transcript for voice notes.
|
||||
|
||||
## Future Capacitor Features
|
||||
|
||||
- Native push via FCM.
|
||||
- Native share target.
|
||||
- Background upload reliability.
|
||||
- Local biometric unlock.
|
||||
- Better notification actions: Approve/Deny from notification.
|
||||
@@ -0,0 +1,250 @@
|
||||
# Product Plan
|
||||
|
||||
## Working Name
|
||||
|
||||
Hermes Mobile
|
||||
|
||||
## One-line Pitch
|
||||
|
||||
A private Android-first app for talking to Hermes Agent with voice, files, live tool calls, approvals, cron controls, and completion notifications.
|
||||
|
||||
## Target User
|
||||
|
||||
Primary: Zeb / homelab power user who already runs Hermes Agent on a Linux machine and wants a better phone interface than Telegram.
|
||||
|
||||
Secondary later:
|
||||
- Developers running Hermes locally/remotely
|
||||
- Homelab admins
|
||||
- People who want an agent command center rather than a chatbot
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Not a generic OpenAI chat frontend.
|
||||
- Not a multi-provider model playground.
|
||||
- Not a public SaaS.
|
||||
- Not a replacement for Hermes Agent internals.
|
||||
- Not a Matrix/Discord/Telegram clone.
|
||||
|
||||
## Design Principles
|
||||
|
||||
1. Agent-first, not chat-first
|
||||
- The UI should show what Hermes is doing, not just the final response.
|
||||
- Tool calls are first-class objects.
|
||||
|
||||
2. Phone-native ergonomics
|
||||
- Thumb-reachable controls.
|
||||
- Large tap targets.
|
||||
- Voice and upload actions always nearby.
|
||||
- Background task status survives app close/reopen.
|
||||
|
||||
3. Private by default
|
||||
- Self-hosted companion server.
|
||||
- Local uploads.
|
||||
- Optional LAN/Tailscale-only mode.
|
||||
- No third-party notification requirement unless chosen.
|
||||
|
||||
4. Modular integrations
|
||||
- Hermes CLI bridge now.
|
||||
- Hermes API bridge where useful.
|
||||
- Direct Python/event bridge later.
|
||||
- Notification providers swappable.
|
||||
|
||||
5. Install should feel boring
|
||||
- One command.
|
||||
- Clear service status.
|
||||
- Easy update/uninstall.
|
||||
|
||||
## Core Screens
|
||||
|
||||
### 1. Home / Ask
|
||||
|
||||
Purpose: send a prompt fast.
|
||||
|
||||
Features:
|
||||
- Text box with multiline support
|
||||
- Hold-to-record voice note
|
||||
- Attach button
|
||||
- Model/session selector compact chip
|
||||
- “New task” vs “continue session” toggle
|
||||
- Streaming answer
|
||||
- Activity timeline under/alongside answer
|
||||
- Completion toast/push
|
||||
|
||||
### 2. Activity
|
||||
|
||||
Purpose: see active and recent agent runs.
|
||||
|
||||
Features:
|
||||
- Running tasks
|
||||
- Tool-call cards
|
||||
- Logs/output previews
|
||||
- Generated files
|
||||
- Error states
|
||||
- Stop/cancel if supported
|
||||
|
||||
### 3. Sessions
|
||||
|
||||
Purpose: browse/resume history.
|
||||
|
||||
Features:
|
||||
- Recent conversations
|
||||
- Search
|
||||
- Rename/pin/archive
|
||||
- Session metadata: platform, started, last active, tool count, attachments
|
||||
|
||||
### 4. Cron
|
||||
|
||||
Purpose: manage scheduled Hermes tasks from phone.
|
||||
|
||||
Features:
|
||||
- Job list grouped by active/paused/completed
|
||||
- Run now
|
||||
- Pause/resume/remove
|
||||
- Edit prompt/schedule later
|
||||
- Last output viewer
|
||||
- Delivery target setting: in-app inbox, push, Matrix/Telegram/etc.
|
||||
|
||||
### 5. Files
|
||||
|
||||
Purpose: manage uploaded/generated artifacts.
|
||||
|
||||
Features:
|
||||
- Upload inbox
|
||||
- Generated media/files from responses
|
||||
- Download/share
|
||||
- Link file into a new prompt
|
||||
- Retention controls later
|
||||
|
||||
### 6. Approvals
|
||||
|
||||
Purpose: approve dangerous actions safely.
|
||||
|
||||
Features:
|
||||
- Pending approvals queue
|
||||
- Command/action preview
|
||||
- Risk label
|
||||
- Approve once / deny
|
||||
- Expiry countdown
|
||||
- Audit log
|
||||
|
||||
### 7. Settings
|
||||
|
||||
Purpose: connect to companion server and configure app.
|
||||
|
||||
Features:
|
||||
- Server URL
|
||||
- Pairing code / passkey login
|
||||
- Notification test
|
||||
- Theme
|
||||
- Hermes health
|
||||
- Storage/retention
|
||||
- STT/TTS preferences
|
||||
|
||||
## Feature Details
|
||||
|
||||
### Text Prompting
|
||||
|
||||
Modes:
|
||||
- Quick ask: starts new task/session.
|
||||
- Continue: appends to selected session.
|
||||
- Background task: app can be closed, push when finished.
|
||||
|
||||
### Voice Notes
|
||||
|
||||
MVP flow:
|
||||
1. Browser MediaRecorder captures audio.
|
||||
2. Upload audio file to companion.
|
||||
3. Companion either:
|
||||
- passes audio path to Hermes gateway/STT path, or
|
||||
- transcribes with configured local Whisper/faster-whisper, then sends text prompt.
|
||||
4. UI shows transcript for confirmation if desired.
|
||||
|
||||
Later:
|
||||
- Voice-to-voice replies using Hermes TTS.
|
||||
- Streaming partial transcript.
|
||||
|
||||
### File Uploads
|
||||
|
||||
Supported MVP:
|
||||
- png/jpg/webp/gif
|
||||
- zip/tar/gz
|
||||
- pdf
|
||||
- txt/log/md/json/csv
|
||||
- arbitrary binary as attachment
|
||||
|
||||
Prompt format to Hermes:
|
||||
- Store file locally in companion upload store.
|
||||
- Send prompt with absolute paths and metadata.
|
||||
- Example: `User uploaded files: /var/lib/hermes-mobile/uploads/.../archive.zip`
|
||||
|
||||
### Tool Calls
|
||||
|
||||
Event model:
|
||||
- `tool_call.started`
|
||||
- `tool_call.delta`
|
||||
- `tool_call.finished`
|
||||
- `tool_call.failed`
|
||||
|
||||
Tool cards show:
|
||||
- Icon/name
|
||||
- Arguments summary
|
||||
- Status
|
||||
- Duration
|
||||
- Expandable result
|
||||
- Copy/open file actions where relevant
|
||||
|
||||
### Notifications
|
||||
|
||||
MVP notification events:
|
||||
- task finished
|
||||
- task failed
|
||||
- approval required
|
||||
- cron job completed/failed
|
||||
|
||||
Providers:
|
||||
- Web Push for installed PWA
|
||||
- ntfy fallback
|
||||
- Gotify fallback
|
||||
- Home Assistant persistent notification optional
|
||||
|
||||
### Cron Triggers
|
||||
|
||||
The app should not reimplement Hermes cron. It should wrap Hermes cronjob functions/CLI:
|
||||
- list jobs
|
||||
- run job
|
||||
- pause/resume/remove
|
||||
- create/edit later
|
||||
|
||||
### “Prompting itself” / Hermes test harness
|
||||
|
||||
Because Hermes Mobile runs next to Hermes Agent, the companion can test connectivity by sending a harmless prompt through:
|
||||
- Hermes CLI: `hermes chat -q "ping"`
|
||||
- API server: `/v1/chat/completions`
|
||||
- Direct Python bridge later
|
||||
|
||||
The Settings health page should include a `Test Hermes` button.
|
||||
|
||||
## MVP Acceptance Criteria
|
||||
|
||||
- Install companion server on same Linux machine as Hermes.
|
||||
- Open mobile PWA on Android, pair to companion.
|
||||
- Send text prompt to Hermes and receive answer.
|
||||
- Upload a PNG or ZIP and include it in prompt context.
|
||||
- Record a voice note and turn it into a prompt.
|
||||
- See at least coarse tool activity while Hermes runs.
|
||||
- Receive push/ntfy notification when task completes.
|
||||
- View cron jobs and trigger one manually.
|
||||
|
||||
## Risks
|
||||
|
||||
1. Hermes API server may not expose enough event detail.
|
||||
- Mitigation: start with CLI/direct Python bridge and add event callbacks.
|
||||
|
||||
2. PWA push notification quirks.
|
||||
- Mitigation: support ntfy/Gotify fallback.
|
||||
|
||||
3. Mobile browser audio quirks.
|
||||
- Mitigation: test Android Chrome first; Capacitor later if needed.
|
||||
|
||||
4. Tool-call streaming requires Hermes integration changes.
|
||||
- Mitigation: companion adapter interface; use available callbacks in AIAgent where possible.
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
# Roadmap
|
||||
|
||||
## Phase 0 — Planning Scaffold
|
||||
|
||||
Status: current.
|
||||
|
||||
Deliverables:
|
||||
- Repo created
|
||||
- Product plan
|
||||
- Architecture plan
|
||||
- Design direction
|
||||
- Install strategy
|
||||
- Project structure
|
||||
|
||||
## Phase 1 — Skeleton
|
||||
|
||||
Goal: empty but runnable monorepo.
|
||||
|
||||
Deliverables:
|
||||
- pnpm workspace
|
||||
- `apps/mobile` Vite React PWA
|
||||
- `apps/companion` Fastify server
|
||||
- shared TypeScript package for event/API schemas
|
||||
- basic app shell with bottom nav
|
||||
- `/api/health`
|
||||
- local dev scripts
|
||||
|
||||
## Phase 2 — Basic Hermes Prompting
|
||||
|
||||
Goal: phone can send text prompt and receive final answer.
|
||||
|
||||
Deliverables:
|
||||
- pairing/auth MVP
|
||||
- Settings connect flow
|
||||
- task creation endpoint
|
||||
- Hermes CLI/API adapter MVP
|
||||
- Ask screen sends prompt
|
||||
- response display
|
||||
- task history in SQLite
|
||||
|
||||
## Phase 3 — Realtime Activity + Tool Timeline
|
||||
|
||||
Goal: see Hermes work live.
|
||||
|
||||
Deliverables:
|
||||
- event bus
|
||||
- WebSocket/SSE stream
|
||||
- tool timeline UI
|
||||
- Python worker adapter using AIAgent callbacks if needed
|
||||
- coarse/fine tool event mapping
|
||||
|
||||
## Phase 4 — Uploads + Voice
|
||||
|
||||
Goal: prompt with files and voice notes.
|
||||
|
||||
Deliverables:
|
||||
- multipart upload endpoint
|
||||
- upload tray UI
|
||||
- image/archive/pdf metadata
|
||||
- voice recorder UI
|
||||
- local STT or Hermes STT bridge
|
||||
- attach files to task prompt
|
||||
|
||||
## Phase 5 — Notifications
|
||||
|
||||
Goal: close phone and get told when done.
|
||||
|
||||
Deliverables:
|
||||
- PWA service worker
|
||||
- Web Push subscription management
|
||||
- notification provider abstraction
|
||||
- ntfy fallback
|
||||
- completion/failure/approval notifications
|
||||
|
||||
## Phase 6 — Cron + Approvals
|
||||
|
||||
Goal: mobile control plane.
|
||||
|
||||
Deliverables:
|
||||
- cron list/run/pause/resume/remove
|
||||
- cron output viewer
|
||||
- approval queue UI
|
||||
- companion approval API
|
||||
- Hermes approval integration strategy
|
||||
|
||||
## Phase 7 — Installer + Release
|
||||
|
||||
Goal: one-liner install.
|
||||
|
||||
Deliverables:
|
||||
- production build
|
||||
- install.sh
|
||||
- systemd service
|
||||
- update/uninstall modes
|
||||
- release artifact
|
||||
- pairing URL output
|
||||
|
||||
## Phase 8 — Android Polish
|
||||
|
||||
Goal: feels native.
|
||||
|
||||
Deliverables:
|
||||
- refined Happy-inspired UI
|
||||
- animations/microinteractions
|
||||
- adaptive icons/splash
|
||||
- share target
|
||||
- haptics
|
||||
- offline shell polish
|
||||
- optional Capacitor wrapper evaluation
|
||||
|
||||
## Phase 9 — Deep Hermes Integration
|
||||
|
||||
Goal: full Hermes-native experience.
|
||||
|
||||
Deliverables:
|
||||
- structured event stream from Hermes core or stable Python adapter
|
||||
- richer session browsing
|
||||
- generated file detection
|
||||
- background process control
|
||||
- subagent visualization
|
||||
- memory/skills dashboards possibly
|
||||
|
||||
## Open Feature Ideas
|
||||
|
||||
- Quick action tiles: “Check homelab”, “Run DV status”, “Start coding task”.
|
||||
- Voice-to-voice mode.
|
||||
- Per-task model/toolset selection.
|
||||
- “Watch mode” for long-running coding tasks.
|
||||
- Home Assistant widgets/actions.
|
||||
- Secure notification actions: approve/deny from notification.
|
||||
- Android native share target.
|
||||
- QR pairing from terminal output.
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
# Testing Strategy
|
||||
|
||||
## Philosophy
|
||||
|
||||
Hermes Mobile must be tested as an app plus a local service plus a Hermes integration. Generic unit tests are not enough; the important thing is whether a phone can send work to Hermes, see activity, close the app, and get notified when done.
|
||||
|
||||
## Test Layers
|
||||
|
||||
### 1. Unit Tests
|
||||
|
||||
Frontend:
|
||||
- event reducer/idempotency
|
||||
- upload tray state
|
||||
- voice recorder state machine
|
||||
- API client error handling
|
||||
- cron job card states
|
||||
|
||||
Companion:
|
||||
- config loading
|
||||
- auth pairing tokens
|
||||
- notification provider interface
|
||||
- upload path normalization
|
||||
- event bus fanout
|
||||
- task state transitions
|
||||
|
||||
### 2. Integration Tests
|
||||
|
||||
Companion server with fake Hermes adapter:
|
||||
- create task
|
||||
- stream events
|
||||
- upload file + attach to task
|
||||
- completion notification queued
|
||||
- cron list/run mocks
|
||||
|
||||
Hermes adapter contract tests:
|
||||
- CLI adapter health
|
||||
- API adapter health
|
||||
- Python worker JSONL protocol
|
||||
|
||||
### 3. End-to-End Tests
|
||||
|
||||
Use Playwright against local dev server.
|
||||
|
||||
Scenarios:
|
||||
- first-time pairing
|
||||
- send text prompt
|
||||
- upload PNG and send prompt
|
||||
- record/upload fake audio blob
|
||||
- watch tool timeline update
|
||||
- cron run button
|
||||
- notification permission flow mocked
|
||||
|
||||
### 4. Real Hermes Smoke Tests
|
||||
|
||||
On a machine with Hermes installed:
|
||||
|
||||
```bash
|
||||
hermes chat -q "Reply with exactly: hermes-mobile-ok"
|
||||
```
|
||||
|
||||
Companion health test should do the equivalent and verify response.
|
||||
|
||||
More advanced smoke:
|
||||
- Ask Hermes to run a harmless terminal command: `pwd`.
|
||||
- Verify a tool-call event appears.
|
||||
- Upload a small text file and ask Hermes to summarize it.
|
||||
|
||||
## Manual Android QA
|
||||
|
||||
Test on Android Chrome installed PWA:
|
||||
- Install app to home screen.
|
||||
- Launch standalone mode.
|
||||
- Keyboard does not break composer layout.
|
||||
- Back button closes sheets/modals first.
|
||||
- Voice recording works.
|
||||
- File picker works for image/zip/pdf.
|
||||
- Push notification arrives after app is backgrounded.
|
||||
- App reconnects to running task after being killed/reopened.
|
||||
|
||||
## Compatibility Targets
|
||||
|
||||
- Android Chrome latest
|
||||
- Android Firefox optional
|
||||
- Desktop Chrome for dev
|
||||
- iOS Safari later, not MVP priority
|
||||
|
||||
## Observability for Testing
|
||||
|
||||
Companion should expose:
|
||||
|
||||
```http
|
||||
GET /api/health
|
||||
GET /api/debug/events/recent
|
||||
GET /api/debug/config/redacted
|
||||
POST /api/hermes/test
|
||||
POST /api/notifications/test
|
||||
```
|
||||
|
||||
Only expose debug endpoints in dev mode or authenticated admin mode.
|
||||
|
||||
## CI Plan
|
||||
|
||||
Later:
|
||||
- pnpm lint
|
||||
- pnpm typecheck
|
||||
- pnpm test
|
||||
- pnpm e2e with fake adapter
|
||||
- build release artifact
|
||||
|
||||
## Dogfooding Plan
|
||||
|
||||
The app should be used to prompt Hermes about its own repo.
|
||||
|
||||
Example dogfood tasks:
|
||||
- “Run the test suite for Hermes Mobile and summarize failures.”
|
||||
- “Inspect the companion logs and fix the upload bug.”
|
||||
- “Create a cron job that reminds me if companion is down.”
|
||||
|
||||
This is important because Hermes is both the target agent and the tool used to build the app.
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Placeholder installer. Real implementation will arrive after the skeleton server exists.
|
||||
# Target UX:
|
||||
# curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash
|
||||
|
||||
echo "Hermes Mobile installer placeholder"
|
||||
echo "This repo is currently a planning scaffold; no companion server is installed yet."
|
||||
echo
|
||||
if command -v hermes >/dev/null 2>&1; then
|
||||
echo "Detected Hermes CLI: $(command -v hermes)"
|
||||
else
|
||||
echo "Hermes CLI not found in PATH."
|
||||
fi
|
||||
|
||||
echo "Planned install docs: docs/INSTALL.md"
|
||||
Reference in New Issue
Block a user