-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Expand file tree
/
Copy pathconfig.ts
More file actions
330 lines (288 loc) · 15 KB
/
Copy pathconfig.ts
File metadata and controls
330 lines (288 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
import packageJson from '../../package.json';
const APP_ENV = (import.meta.env.VITE_OPENHUMAN_APP_ENV as string | undefined)
?.trim()
.toLowerCase();
const DEFAULT_BACKEND_URL =
APP_ENV === 'staging' ? 'https://staging-api.tinyhumans.ai' : 'https://api.tinyhumans.ai';
/**
* Build-time fallback for the Core JSON-RPC endpoint URL.
*
* **Not runtime-authoritative.** At runtime `getCoreRpcUrl()` (in
* `services/coreRpcClient.ts`) is the source of truth: it first checks for a
* URL stored by the user via the Welcome screen (`configPersistence`), then
* falls back to this constant. Never read this constant directly from product
* code that needs the live endpoint — call `getCoreRpcUrl()` instead.
*
* Override at build time via `VITE_OPENHUMAN_CORE_RPC_URL`.
*/
export const CORE_RPC_URL =
import.meta.env.VITE_OPENHUMAN_CORE_RPC_URL || 'http://127.0.0.1:7788/rpc';
/** Matches core `OPENHUMAN_TOOL_TIMEOUT_SECS` (default 120s, max 3600s). */
const DEFAULT_TOOL_TIMEOUT_SECS = 120;
const MAX_TOOL_TIMEOUT_SECS = 3600;
function parseToolTimeoutSecs(): number {
const raw = import.meta.env.VITE_TOOL_TIMEOUT_SECS as string | undefined;
if (raw === undefined || raw === '') return DEFAULT_TOOL_TIMEOUT_SECS;
const n = Number(raw);
if (!Number.isFinite(n) || n <= 0 || n > MAX_TOOL_TIMEOUT_SECS) {
return DEFAULT_TOOL_TIMEOUT_SECS;
}
return Math.round(n);
}
export const TOOL_TIMEOUT_SECS = parseToolTimeoutSecs();
/**
* Per-request timeout for Core JSON-RPC `fetch()` calls, in milliseconds.
* Without this the UI can hang indefinitely if the core sidecar stops
* responding mid-flight. Bounded to [1s, 10min]; default 30s. Override with
* `VITE_CORE_RPC_TIMEOUT_MS`.
*/
const DEFAULT_CORE_RPC_TIMEOUT_MS = 30_000;
const MIN_CORE_RPC_TIMEOUT_MS = 1_000;
const MAX_CORE_RPC_TIMEOUT_MS = 10 * 60 * 1_000;
function parseCoreRpcTimeoutMs(): number {
const raw = import.meta.env.VITE_CORE_RPC_TIMEOUT_MS as string | undefined;
if (raw === undefined || raw === '') return DEFAULT_CORE_RPC_TIMEOUT_MS;
const n = Number(raw);
if (!Number.isFinite(n) || n < MIN_CORE_RPC_TIMEOUT_MS || n > MAX_CORE_RPC_TIMEOUT_MS) {
return DEFAULT_CORE_RPC_TIMEOUT_MS;
}
return Math.round(n);
}
export const CORE_RPC_TIMEOUT_MS = parseCoreRpcTimeoutMs();
export const IS_DEV = import.meta.env.DEV;
export const IS_PROD = import.meta.env.PROD;
export const E2E_RESTART_APP_AS_RELOAD =
import.meta.env.VITE_OPENHUMAN_E2E_RESTART_APP_AS_RELOAD === 'true';
export const E2E_DEFAULT_CORE_MODE =
(import.meta.env.VITE_OPENHUMAN_E2E_DEFAULT_CORE_MODE as string | undefined) || '';
/**
* True when the build behaves like a dev build for runtime purposes — either
* a real `vite dev` (DEV=true) or a `vite build --mode development` (the E2E
* harness — DEV=false but MODE='development'). `IS_DEV` alone is insufficient
* for the E2E case because `vite build` always sets PROD=true / DEV=false
* regardless of `--mode`. Consumers gating behavior that should NOT happen in
* shipped binaries (e.g. the `restartApp` reload-instead-of-restart path)
* should read this flag rather than touch `import.meta.env` directly.
*/
export const IS_DEV_LIKE = IS_DEV || import.meta.env.MODE === 'development';
/** Dev only: skip `.skip_onboarding` workspace check and ignore onboarded state so `/onboarding` always shows. Set `VITE_DEV_FORCE_ONBOARDING=true` in `.env.local`. */
export const DEV_FORCE_ONBOARDING =
import.meta.env.DEV && import.meta.env.VITE_DEV_FORCE_ONBOARDING === 'true';
/**
* Consumer-first-session UX (intent picker, home IA, trust affordances).
* **Default off** so `main` stays unchanged until slices ship behind this flag.
* Opt in locally or in staging: `VITE_CONSUMER_FIRST_SESSION=true` in `app/.env.local`.
* Spec: `docs/plans/consumer-first-session-spec.md`.
*/
export const CONSUMER_FIRST_SESSION_ENABLED =
import.meta.env.VITE_CONSUMER_FIRST_SESSION === 'true';
/**
* Master kill-switch for chat multimodal attachments (image / video / document).
* Enabled by default; the actual affordance is gated on the resolved model's
* capability tier at the call site (`Conversations.tsx`) — images and video need
* a vision-capable tier, documents flow on any model. Set
* `VITE_CHAT_ATTACHMENTS=false` to hard-disable the whole feature for a build.
*/
export const CHAT_ATTACHMENTS_ENABLED = import.meta.env.VITE_CHAT_ATTACHMENTS !== 'false';
export const SKILLS_GITHUB_REPO =
import.meta.env.VITE_SKILLS_GITHUB_REPO || 'tinyhumansai/openhuman-skills';
/**
* Transcript-derived restore path (Phase C, `docs/plans/transcript-derived-view.md`).
*
* When **on** (default), the settled-turn process trails on thread open are
* hydrated from the `openhuman.threads_transcript_get` projection of the
* append-only `session_raw/*.jsonl` source of truth, instead of the legacy
* `turn_state_history` snapshot ring. Live token streaming is untouched either
* way — in-flight turns still render from socket-fed `chatRuntimeSlice` state.
*
* Automatic fallback to the legacy `turn_state_history` hydration when the RPC
* errors or reports `hasTranscript: false` (legacy threads), so the old path
* stays fully working. Hard-disable the whole derived path for a build with
* `VITE_DERIVED_TRANSCRIPT=false`.
*/
export const DERIVED_TRANSCRIPT_ENABLED = import.meta.env.VITE_DERIVED_TRANSCRIPT !== 'false';
/** Google Analytics 4 Measurement ID. Leave blank to disable GA. */
export const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID as string | undefined;
/** When true, allow GA in dev builds (for local debugging). Set `VITE_GA_FORCE_DEV=true` in `.env.local`. */
export const GA_FORCE_DEV = import.meta.env.VITE_GA_FORCE_DEV === 'true';
/** OpenPanel project client id. Leave blank to disable OpenPanel analytics. */
export const OPENPANEL_CLIENT_ID = (
(import.meta.env.VITE_OPENPANEL_CLIENT_ID as string | undefined) ??
'e9c996d5-497f-4eec-9bde-630019ad525b'
).trim();
/** OpenPanel API base URL. */
export const OPENPANEL_API_URL = (
(import.meta.env.VITE_OPENPANEL_API_URL as string | undefined) ??
'https://panel.tinyhumans.ai/api'
).trim();
/** Sentry DSN for error reporting. Leave blank to disable. */
export const SENTRY_DSN = import.meta.env.VITE_SENTRY_DSN as string | undefined;
/**
* Build-time fallback for the backend API base URL.
*
* **Not runtime-authoritative in Tauri.** In the desktop app, `getBackendUrl()`
* (in `services/backendUrl.ts`) asks the core sidecar for the live API URL via
* `openhuman.config_resolve_api_url`. If that call fails or returns an empty
* URL, `getBackendUrl()` **throws** — it does not fall back to this constant.
* This constant is only used in web/non-Tauri mode (where the sidecar is not
* present).
*
* Override at build time via `VITE_BACKEND_URL`.
*/
export const BACKEND_URL =
(import.meta.env.VITE_BACKEND_URL as string | undefined)?.trim() || DEFAULT_BACKEND_URL;
/** Telegram bot username used for managed DM linking when backend does not return a launch URL. */
export const TELEGRAM_BOT_USERNAME =
(import.meta.env.VITE_TELEGRAM_BOT_USERNAME as string | undefined) || 'openhuman_bot';
/** 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;
export const APP_VERSION = packageJson.version;
/** Desktop binary/package version reported with analytics events. */
export const APP_BINARY_VERSION =
(import.meta.env.VITE_OPENHUMAN_BINARY_VERSION as string | undefined)?.trim() || APP_VERSION;
/** Root Rust core crate version reported with analytics events. */
export const CORE_CARGO_VERSION =
(import.meta.env.VITE_OPENHUMAN_CORE_CARGO_VERSION as string | undefined)?.trim() || APP_VERSION;
/** Tauri shell Cargo crate version reported with analytics events. */
export const TAURI_CARGO_VERSION =
(import.meta.env.VITE_OPENHUMAN_TAURI_CARGO_VERSION as string | undefined)?.trim() ||
APP_BINARY_VERSION;
/**
* Deployment environment reported to Sentry and other observability surfaces.
*
* Derived from `VITE_OPENHUMAN_APP_ENV` (set by CI for production / staging
* bundles). Falls back to `development` in non-production builds so local
* debugging never mingles with real user events.
*/
export const APP_ENVIRONMENT: 'production' | 'staging' | 'development' = IS_DEV
? 'development'
: APP_ENV === 'staging'
? 'staging'
: 'production';
/** Short git SHA baked in at build time (`VITE_BUILD_SHA`). Empty locally. */
export const BUILD_SHA = ((import.meta.env.VITE_BUILD_SHA as string | undefined) ?? '')
.trim()
.slice(0, 12);
/**
* Canonical Sentry release identifier: `openhuman@<version>[+<short_sha>]`.
*
* Matches the tag the Rust core sidecar reports (see `src/main.rs`) so events
* from the frontend, the core, and source-map uploads all group under the
* same release in the Sentry dashboard.
*/
export const SENTRY_RELEASE = BUILD_SHA
? `openhuman@${APP_VERSION}+${BUILD_SHA}`
: `openhuman@${APP_VERSION}`;
/**
* Minimum **desktop app** semver required for OAuth deep-link completion (`openhuman://oauth/success`).
*
* **Build-time embedding:** This value is baked into each shipped installer. Raising the floor for
* users already on an older build requires them to install a **new** release (or use in-app update
* when available)—changing CI vars alone does not retrofit existing binaries. For a fleet-wide
* minimum that can move without a new app build, add a runtime policy endpoint later and consult it
* here with this constant as fallback only.
*
* Set in production builds (e.g. GitHub Actions `vars`). Empty = no gate (default for local dev).
*/
export const MINIMUM_SUPPORTED_APP_VERSION =
(import.meta.env.VITE_MINIMUM_SUPPORTED_APP_VERSION as string | undefined)?.trim() ?? '';
/** URL for the latest app release download page. Used for OAuth version-gate recovery and crash-recovery prompts. Override via VITE_LATEST_APP_DOWNLOAD_URL for deployment-specific download pages. */
export const LATEST_APP_DOWNLOAD_URL =
(import.meta.env.VITE_LATEST_APP_DOWNLOAD_URL as string | undefined)?.trim() ||
'https://github.com/tinyhumansai/openhuman/releases/latest';
/**
* Public GitHub repository URL. Target of the in-app "Star us on GitHub" CTA
* (#5005). Override via VITE_OPENHUMAN_GITHUB_REPO_URL for forks.
*
* The override is accepted only when it parses as an `https:` URL. This value is
* handed straight to `openUrl` by the CTA, so a malformed string or a
* custom-scheme override could break the button or invoke an unintended
* protocol handler; anything that is not valid HTTPS falls back to the default.
*/
export const OPENHUMAN_GITHUB_REPO_URL = ((): string => {
const fallback = 'https://github.com/tinyhumansai/openhuman';
const override = (import.meta.env.VITE_OPENHUMAN_GITHUB_REPO_URL as string | undefined)?.trim();
if (!override) return fallback;
try {
return new URL(override).protocol === 'https:' ? override : fallback;
} catch {
return fallback;
}
})();
/** Support page base URL. The crash screen appends `?ref=<sentryEventId>` so support can correlate a user's pasted Error ID to the exact Sentry event. Override via VITE_SUPPORT_URL for deployment-specific support endpoints. */
export const SUPPORT_URL =
(import.meta.env.VITE_SUPPORT_URL as string | undefined)?.trim() ||
'https://tinyhumans.ai/support';
/**
* Set `VITE_SENTRY_SMOKE_TEST=true` in one build (or in `.env.local`) to
* fire a one-shot diagnostic event at `initSentry()` time and verify the
* Sentry pipeline end-to-end. Has no effect in normal builds.
*/
export const SENTRY_SMOKE_TEST = import.meta.env.VITE_SENTRY_SMOKE_TEST === 'true';
/**
* ElevenLabs voice ID used for the mascot's reply speech. `JBFqnCBsd6RMkjVDRZzb`
* is "George" — a warm multilingual voice that pairs cleanly with the
* `eleven_multilingual_v2` model (`MASCOT_VOICE_MODEL_ID` below) so the
* mascot can speak any locale we ship without a voice swap. Override with
* `VITE_MASCOT_VOICE_ID` to A/B alternatives without a code change.
*/
export const MASCOT_VOICE_ID =
(import.meta.env.VITE_MASCOT_VOICE_ID as string | undefined)?.trim() || 'JBFqnCBsd6RMkjVDRZzb';
/**
* ElevenLabs model used for mascot reply speech. `eleven_multilingual_v2`
* speaks every locale we ship; the older `eleven_monolingual_v1` would
* choke on non-Latin scripts. Override with `VITE_MASCOT_VOICE_MODEL_ID`
* to pin a different model (e.g. `eleven_turbo_v2_5` for lower latency
* at the cost of accent fidelity).
*/
export const MASCOT_VOICE_MODEL_ID =
(import.meta.env.VITE_MASCOT_VOICE_MODEL_ID as string | undefined)?.trim() ||
'eleven_multilingual_v2';
/**
* Gates the realtime ElevenLabs Agents voice mode (#5399). On by default in
* every build (local, staging, production) so the Settings toggle is exposed
* without any build-time env wiring; set `VITE_VOICE_MODE=false` to hide it
* (kill switch). This gates only the UI switch — the realtime code paths
* additionally check the persisted `mascot.voiceMode`, so the feature still
* ships dark until the user opts in via the toggle.
*/
export const VOICE_MODE_FLAG_ENABLED =
(import.meta.env.VITE_VOICE_MODE as string | undefined)?.trim() !== 'false';
/**
* Which voice entry point the Human tab offers (#5399).
*
* On by default in every build: the tab shows the realtime "Start voice chat"
* control where the push-to-talk mic used to sit. Set
* `VITE_HUMAN_VOICE_REALTIME=false` to fall back to the classic tap-and-speak
* composer — the kill switch for the realtime path on this surface.
*
* Distinct from {@link VOICE_MODE_FLAG_ENABLED}, which gates the *chat* tab's
* mascot stage against the persisted `mascot.voiceMode`. Keep them separate:
* one surface's rollback must not silently change the other's.
*/
export const HUMAN_VOICE_REALTIME_ENABLED =
(import.meta.env.VITE_HUMAN_VOICE_REALTIME as string | undefined)?.trim() !== 'false';
/**
* Show BOTH voice entry points on the Human tab — the realtime control and the
* classic push-to-talk composer, stacked. Off by default: the two are alternative
* ways to say the same thing, so shipping both at once is a comparison aid (A/B a
* regression, demo the difference), not the intended product surface. Set
* `VITE_HUMAN_VOICE_SHOW_BOTH=true` to enable. Takes precedence over
* {@link HUMAN_VOICE_REALTIME_ENABLED}.
*/
export const HUMAN_VOICE_SHOW_BOTH =
(import.meta.env.VITE_HUMAN_VOICE_SHOW_BOTH as string | undefined)?.trim() === 'true';
/**
* URL of the published mascot manifest (`dist/mascots.json` from the
* `tinyhumansai/mascots` repo). This is the authoritative source for the
* in-app mascot library — each entry names a Rive `.riv` runtime file plus its
* `stateEngine` (poses, viseme codes, channels). Fetched directly over HTTPS
* (the `raw.githubusercontent.com` host is CORS-open and allowed by the
* webview CSP's `connect-src https:`). Override with `VITE_MASCOT_MANIFEST_URL`
* to point at a fork or a locally-served manifest during development.
*/
export const MASCOT_MANIFEST_URL =
(import.meta.env.VITE_MASCOT_MANIFEST_URL as string | undefined)?.trim() ||
'https://raw.githubusercontent.com/tinyhumansai/mascots/main/dist/mascots.json';