Adds a production-ready Dockerfile based on node:24-alpine:
- Installs only runtime dependencies (docker-cli, curl)
- Runs as non-root `node` user
- Includes HEALTHCHECK for container orchestration
- Adds .dockerignore to keep image lean
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Document user-scope service support fixes
- Record PR #17 creation process
- Include test output examples
- Note GitHub token usage (expires 90 days)
- Sort blocks by latest timestamp ascending (oldest → newest)
- Newest logs appear at bottom (user expectation)
- Clear header indicates chronological order
- Each block internally chronological (oldest → newest)
Final UX: user scrolls down to see current logs, can see historical logs above.
- Add regex validation for pgrep patterns and systemctl unit names
- Fix agent-dashboard process pattern (was dashboard-v2, now matches our structure)
- Prevents potential shell injection even though values are hardcoded
Clean addition. Placed correctly before the auth middleware so it stays unauthenticated. Only exposes process uptime which is safe for monitoring and load balancer health checks. No sensitive data leaked.
Replace the hardcoded opus/sonnet pricing with a generic,
file-backed, multi-provider cost estimation system.
Server-side:
- Add DEFAULT_MODEL_PRICING table covering OpenAI, Anthropic,
Google, xAI, and Nvidia models
- Add estimateMsgCost() that uses explicit cost when available,
falling back to rate-table calculation
- Add normalizeProvider/normalizeModel helpers for consistent
model key formatting (provider/model)
- Support optional data/model_pricing_usd_per_million.json file
to override default rates
- Track cacheRead/cacheWrite tokens separately in usage windows
- Use estimateMsgCost() across getSessionCost, getCostData,
getUsageWindows, and session detail endpoint
Client-side:
- Remove hardcoded pricing object from updateLimits()
- Read perModelCost from server response instead
- Display cache costs when present
- Use var and string concatenation for iOS Safari compatibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /api/logs endpoint only tried a single system-level journalctl
unit, so user-level services and compound services (e.g.
openclaw-gateway) returned "No logs available".
Now iterates over candidate unit names per service and both system
and user journal scopes, returning the first non-empty result.
Prefixes output with [source scope:unit] for debugging, and shows
a helpful message listing what was tried when no logs are found.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The service status panel only checked system-level systemctl, so
services running as user-level systemd units (systemctl --user)
or standalone processes showed as inactive.
Now checks system systemctl first, then user systemctl, then falls
back to pgrep pattern matching. Also checks alternative unit names
(e.g. openclaw-gateway, openclaw-webhooks) for compound services.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow credential files (credentials.json, mfa-secret.txt, audit.log)
to be stored in a separate directory from the workspace data folder
by setting the DASHBOARD_AUTH_DIR environment variable.
Defaults to the existing dataDir when unset, so no behavior change
for current deployments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap res.json() in try/catch in the register, login, and recovery
handlers so a 502 proxy error or HTML response doesn't throw an
unhandled exception. Falls back to an empty object and shows the
HTTP status code in the error message instead.
Uses var for iOS Safari compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the dashboard is accessed via an explicit /index.html path
(e.g. from a bookmark or direct link), API_BASE was set to a path
ending in /index.html, causing all API calls to 404.
Extract a getApiBasePath() helper that strips a trailing /index.html
segment before deriving the base path. Uses var for iOS Safari compat.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a lightweight health check endpoint that returns JSON
{"status":"ok","uptime":...} without requiring authentication.
Placed before the auth gate so load balancers, uptime monitors,
and Tailscale Funnel health probes can verify the server is running.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix registration endpoint bugs causing 400 errors
- Fix CORS headers not being set when origin doesn't match host
- Add missing 'ip' variable definition in registration handler
- Fix hardcoded /root/ paths to use configurable dataDir
- Add error logging for better debugging
These changes resolve the "Bad request" error when creating new accounts.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Fix logs endpoint authentication
The fetchLogs() function was making unauthenticated API calls,
causing 401 Unauthorized errors. Added Bearer token authentication
header to the fetch request.
Also improved error handling to show HTTP status codes.
Fixes logs viewer and ensures all API calls include proper authentication.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Dave Lutz <dave@lutzfamily.net>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>