mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
refactor(config): centralize env vars, add .env.example files, remove stale config
- Centralize all VITE_* env var reads in app/src/utils/config.ts (SENTRY_DSN, BACKEND_URL, DEV_JWT_TOKEN) - Update consumers (analytics.ts, backendUrl.ts, store/index.ts) to import from config.ts - Add TypeScript type declarations for ImportMetaEnv in vite-env.d.ts - Remove dead import.meta.env.OPENHUMAN_CORE_RPC_URL fallback (non-VITE prefix, never exposed by Vite) - Remove unused TELEGRAM_BOT_USERNAME/TELEGRAM_BOT_ID from test mocks - Create .env.example (root) documenting ~30 Rust/Tauri env vars with [required]/[optional] tags - Create app/.env.example documenting 6 frontend VITE_* vars - Add Configuration section to CLAUDE.md Closes #61
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
# Root environment variables — Rust core, Tauri shell, and shared settings.
|
||||
# Copy to .env and fill in values as needed.
|
||||
# Loaded via: source scripts/load-dotenv.sh
|
||||
#
|
||||
# Tags: [required] must be set, [optional] has a sensible default or can be blank
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Backend API
|
||||
# ---------------------------------------------------------------------------
|
||||
# [required] Primary backend URL (read by Rust core and QuickJS skills sandbox)
|
||||
BACKEND_URL=https://staging-api.alphahuman.xyz
|
||||
# [required] Also read by Vite frontend (VITE_ prefix required for browser exposure)
|
||||
VITE_BACKEND_URL=https://staging-api.alphahuman.xyz
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Core process
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Default: 7788
|
||||
OPENHUMAN_CORE_PORT=7788
|
||||
# [optional] Default: http://127.0.0.1:7788/rpc
|
||||
OPENHUMAN_CORE_RPC_URL=http://127.0.0.1:7788/rpc
|
||||
# [optional] Run mode: child (default, spawns sidecar) | inprocess
|
||||
OPENHUMAN_CORE_RUN_MODE=child
|
||||
# [optional] Override path to openhuman core binary (leave blank for auto-detection)
|
||||
OPENHUMAN_CORE_BIN=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Config overrides (override config.toml values at runtime)
|
||||
# ---------------------------------------------------------------------------
|
||||
# [required] API key for the LLM provider
|
||||
OPENHUMAN_API_KEY=
|
||||
# [optional] Default model to use
|
||||
OPENHUMAN_MODEL=
|
||||
# [optional] Workspace directory (default: ~/.openhuman)
|
||||
OPENHUMAN_WORKSPACE=
|
||||
# [optional] Default: 0.7
|
||||
OPENHUMAN_TEMPERATURE=0.7
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Runtime flags
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Default: 0
|
||||
OPENHUMAN_BROWSER_ALLOW_ALL=0
|
||||
# [optional] Default: 0
|
||||
OPENHUMAN_LOG_PROMPTS=0
|
||||
# [optional] Enable reasoning mode
|
||||
OPENHUMAN_REASONING_ENABLED=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Web search
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Default: false
|
||||
OPENHUMAN_WEB_SEARCH_ENABLED=false
|
||||
# [optional] Search provider name
|
||||
OPENHUMAN_WEB_SEARCH_PROVIDER=
|
||||
# [optional] Required if web search provider is Brave
|
||||
OPENHUMAN_BRAVE_API_KEY=
|
||||
# [optional] Default: 5
|
||||
OPENHUMAN_WEB_SEARCH_MAX_RESULTS=5
|
||||
# [optional] Default: 10
|
||||
OPENHUMAN_WEB_SEARCH_TIMEOUT_SECS=10
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Storage
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Storage backend (default: SQLite)
|
||||
OPENHUMAN_STORAGE_PROVIDER=
|
||||
# [optional] Database connection URL
|
||||
OPENHUMAN_STORAGE_DB_URL=
|
||||
# [optional] Connection timeout in seconds
|
||||
OPENHUMAN_STORAGE_CONNECT_TIMEOUT_SECS=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Proxy
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Default: false
|
||||
OPENHUMAN_PROXY_ENABLED=false
|
||||
# [optional] HTTP proxy URL
|
||||
OPENHUMAN_HTTP_PROXY=
|
||||
# [optional] HTTPS proxy URL
|
||||
OPENHUMAN_HTTPS_PROXY=
|
||||
# [optional] Catch-all proxy URL
|
||||
OPENHUMAN_ALL_PROXY=
|
||||
# [optional] Comma-separated hosts to bypass proxy
|
||||
OPENHUMAN_NO_PROXY=
|
||||
# [optional] Proxy scope
|
||||
OPENHUMAN_PROXY_SCOPE=
|
||||
# [optional] Comma-separated services to proxy
|
||||
OPENHUMAN_PROXY_SERVICES=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Local AI binary overrides
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Override path to whisper binary
|
||||
WHISPER_BIN=
|
||||
# [optional] Override path to piper binary
|
||||
PIPER_BIN=
|
||||
# [optional] Override path to ollama binary
|
||||
OLLAMA_BIN=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Skills
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Override skills registry URL
|
||||
SKILLS_REGISTRY_URL=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Logging
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Default: info
|
||||
RUST_LOG=info
|
||||
# [optional] Default: 0 (set to 1 for full backtraces)
|
||||
RUST_BACKTRACE=1
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Testing (do not set in production)
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Enable mock service mode
|
||||
# OPENHUMAN_SERVICE_MOCK=0
|
||||
# [optional] Path to mock state file
|
||||
# OPENHUMAN_SERVICE_MOCK_STATE_FILE=
|
||||
@@ -75,6 +75,19 @@ cargo check --manifest-path app/src-tauri/Cargo.toml
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Environment variables are documented in two `.env.example` files:
|
||||
|
||||
- **[`.env.example`](.env.example)** (repo root) — Rust core, Tauri shell, backend URL, logging, proxy, storage, web search, local AI binary overrides. Loaded via `source scripts/load-dotenv.sh`.
|
||||
- **[`app/.env.example`](app/.env.example)** — Frontend `VITE_*` vars (core RPC URL, backend URL, Sentry DSN, skills repo, dev helpers). Copy to `app/.env.local` for local overrides.
|
||||
|
||||
**Frontend config** is centralized in [`app/src/utils/config.ts`](app/src/utils/config.ts). All `VITE_*` env vars should be read there and re-exported — do not read `import.meta.env` directly in other files.
|
||||
|
||||
**Rust config** uses a TOML-based `Config` struct (`src/openhuman/config/schema/types.rs`) with env var overrides applied in `src/openhuman/config/schema/load.rs`. Env vars override config file values at runtime (e.g. `OPENHUMAN_API_KEY` overrides `config.api_key`).
|
||||
|
||||
---
|
||||
|
||||
## Testing Guide (Unit + E2E)
|
||||
|
||||
### Unit tests (Vitest)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Frontend (Vite) environment variables
|
||||
# Copy to app/.env.local and fill in values as needed.
|
||||
# Only VITE_-prefixed vars are exposed to the browser.
|
||||
#
|
||||
# Tags: [required] must be set, [optional] has a sensible default or can be blank
|
||||
|
||||
# [optional] Core RPC endpoint (Tauri sets this automatically; override for web-only dev)
|
||||
VITE_OPENHUMAN_CORE_RPC_URL=http://127.0.0.1:7788/rpc
|
||||
|
||||
# [required] Backend API URL (web fallback when core RPC is unavailable)
|
||||
VITE_BACKEND_URL=https://staging-api.alphahuman.xyz
|
||||
|
||||
# [optional] Skills GitHub repository slug (default: tinyhumansai/openhuman-skills)
|
||||
VITE_SKILLS_GITHUB_REPO=tinyhumansai/openhuman-skills
|
||||
|
||||
# [optional] Sentry DSN for error reporting (leave blank to disable)
|
||||
VITE_SENTRY_DSN=
|
||||
|
||||
# [optional] Dev-only: auto-inject JWT token to skip login flow
|
||||
VITE_DEV_JWT_TOKEN=
|
||||
|
||||
# [optional] Dev-only: force onboarding flow to always show
|
||||
VITE_DEV_FORCE_ONBOARDING=false
|
||||
@@ -21,11 +21,9 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
|
||||
import { store } from '../store';
|
||||
import { IS_DEV } from '../utils/config';
|
||||
import { IS_DEV, SENTRY_DSN } from '../utils/config';
|
||||
import { enqueueError, registerSentrySender, type SanitizedSentryEvent } from './errorReportQueue';
|
||||
|
||||
const SENTRY_DSN = import.meta.env.VITE_SENTRY_DSN as string | undefined;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { isTauri as coreIsTauri } from '@tauri-apps/api/core';
|
||||
|
||||
import { BACKEND_URL } from '../utils/config';
|
||||
import { callCoreRpc } from './coreRpcClient';
|
||||
|
||||
let resolvedBackendUrl: string | null = null;
|
||||
@@ -10,10 +11,7 @@ function normalizeBaseUrl(url: string): string {
|
||||
}
|
||||
|
||||
function webFallbackBackendUrl(): string {
|
||||
const fromVite =
|
||||
typeof import.meta.env.VITE_BACKEND_URL === 'string'
|
||||
? import.meta.env.VITE_BACKEND_URL.trim()
|
||||
: '';
|
||||
const fromVite = typeof BACKEND_URL === 'string' ? BACKEND_URL.trim() : '';
|
||||
if (fromVite) {
|
||||
return normalizeBaseUrl(fromVite);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from 'redux-persist';
|
||||
import storage from 'redux-persist/lib/storage';
|
||||
|
||||
import { IS_DEV } from '../utils/config';
|
||||
import { DEV_JWT_TOKEN, IS_DEV } from '../utils/config';
|
||||
import {
|
||||
logout as clearRustSession,
|
||||
storeSession,
|
||||
@@ -147,9 +147,8 @@ export const store = configureStore({
|
||||
|
||||
export const persistor = persistStore(store, null, () => {
|
||||
// Dev-only: auto-inject JWT token for local testing without login flow.
|
||||
const devToken = import.meta.env.VITE_DEV_JWT_TOKEN;
|
||||
if (devToken && !store.getState().auth.token) {
|
||||
store.dispatch(setToken(devToken));
|
||||
if (DEV_JWT_TOKEN && !store.getState().auth.token) {
|
||||
store.dispatch(setToken(DEV_JWT_TOKEN));
|
||||
console.log('[dev] Auto-injected JWT token from VITE_DEV_JWT_TOKEN');
|
||||
|
||||
// Auto-mark user as onboarded once their profile is fetched
|
||||
|
||||
@@ -57,10 +57,13 @@ vi.mock('../utils/tauriCommands', () => ({
|
||||
|
||||
// Mock the config module
|
||||
vi.mock('../utils/config', () => ({
|
||||
TELEGRAM_BOT_USERNAME: 'test_bot',
|
||||
TELEGRAM_BOT_ID: '12345',
|
||||
CORE_RPC_URL: 'http://127.0.0.1:7788/rpc',
|
||||
IS_DEV: true,
|
||||
DEV_FORCE_ONBOARDING: false,
|
||||
SKILLS_GITHUB_REPO: 'test/skills',
|
||||
SENTRY_DSN: undefined,
|
||||
BACKEND_URL: 'http://localhost:5005',
|
||||
DEV_JWT_TOKEN: undefined,
|
||||
}));
|
||||
|
||||
vi.mock('../services/backendUrl', () => ({
|
||||
|
||||
+12
-3
@@ -1,7 +1,5 @@
|
||||
export const CORE_RPC_URL =
|
||||
import.meta.env.OPENHUMAN_CORE_RPC_URL ||
|
||||
import.meta.env.VITE_OPENHUMAN_CORE_RPC_URL ||
|
||||
'http://127.0.0.1:7788/rpc';
|
||||
import.meta.env.VITE_OPENHUMAN_CORE_RPC_URL || 'http://127.0.0.1:7788/rpc';
|
||||
|
||||
export const IS_DEV = import.meta.env.DEV;
|
||||
|
||||
@@ -11,3 +9,14 @@ export const DEV_FORCE_ONBOARDING =
|
||||
|
||||
export const SKILLS_GITHUB_REPO =
|
||||
import.meta.env.VITE_SKILLS_GITHUB_REPO || 'tinyhumansai/openhuman-skills';
|
||||
|
||||
/** Sentry DSN for error reporting. Leave blank to disable. */
|
||||
export const SENTRY_DSN = import.meta.env.VITE_SENTRY_DSN as string | undefined;
|
||||
|
||||
/** Backend API URL (web fallback when core RPC is unavailable). */
|
||||
export const BACKEND_URL = import.meta.env.VITE_BACKEND_URL as string | undefined;
|
||||
|
||||
/** Dev only: auto-inject JWT token to skip login flow. */
|
||||
export const DEV_JWT_TOKEN = import.meta.env.DEV
|
||||
? (import.meta.env.VITE_DEV_JWT_TOKEN as string | undefined)
|
||||
: undefined;
|
||||
|
||||
Vendored
+15
@@ -1,5 +1,20 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_OPENHUMAN_CORE_RPC_URL?: string;
|
||||
readonly VITE_BACKEND_URL?: string;
|
||||
readonly VITE_SKILLS_GITHUB_REPO?: string;
|
||||
readonly VITE_SENTRY_DSN?: string;
|
||||
readonly VITE_DEV_JWT_TOKEN?: string;
|
||||
readonly VITE_DEV_FORCE_ONBOARDING?: string;
|
||||
readonly DEV: boolean;
|
||||
readonly MODE: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
||||
// Node.js polyfills for browser
|
||||
declare global {
|
||||
interface Window {
|
||||
|
||||
Reference in New Issue
Block a user