mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(observability): Sentry release tracking, source maps, and end-to-end DSN plumbing (#734)
* ci(release): bake Sentry DSN into shipped tauri bundle
Released builds weren't reporting anything to Sentry. Root cause: the
tauri.conf.json `beforeBuildCommand` re-runs `vite build` inside
`cargo tauri build`. The prior `yarn build` step set `VITE_SENTRY_DSN`
for its own run, but the tauri step did not — so the rebuild produced
a DSN-less `dist/` that overwrote the good one, and the shipped web UI
initialized Sentry with an empty DSN (`initSentry` returns early when
`!SENTRY_DSN`).
Fix:
- `release.yml` / `build-desktop` — declare `VITE_SENTRY_DSN` and
`VITE_DEBUG` on the tauri-build step so the `beforeBuildCommand`
rebuild bakes them into the final bundle.
- `release-packages.yml` / `build-cli-linux-arm64` — guard against a
missing `vars.OPENHUMAN_SENTRY_DSN` so the Linux arm64 CLI tarball
cannot ship without error reporting baked in via `option_env!`.
The core sidecar's `option_env!("OPENHUMAN_SENTRY_DSN")` already gets
the value from the dedicated "Build sidecar core binary" step; the
tauri shell doesn't rebuild it (separate crate, not a workspace dep),
so the baked DSN survives into the bundled installer.
* feat(observability): Sentry release tracking + source maps (#405)
Tags every Sentry event with a canonical release identifier shared by
the frontend and Rust core, uploads source maps so stack traces are
symbolicated in the dashboard, and adds a CLI probe for repeatable
verification of any future release.
Release identifier
openhuman@<semver>[+<short_git_sha>]
- Frontend (`app/src/utils/config.ts::SENTRY_RELEASE`) builds the tag
from `VITE_BUILD_SHA`.
- Core sidecar (`src/main.rs::build_release_tag`) builds the same tag
from `option_env!("OPENHUMAN_BUILD_SHA")`, so events from both
surfaces group under one release. Cargo's fingerprint already tracks
`option_env!` changes.
Environment separation
- Frontend: new `APP_ENVIRONMENT` export (`development` | `staging` |
`production`) derived from `VITE_OPENHUMAN_APP_ENV`, passed to
`Sentry.init`.
- Core: `resolve_environment` honors `OPENHUMAN_APP_ENV` at runtime,
falling back to `debug_assertions` detection.
Source-map upload
- `@sentry/vite-plugin` added as an app devDependency.
- `vite.config.ts` emits source maps unconditionally and registers the
plugin only when `SENTRY_AUTH_TOKEN` is present, so local dev skips
silently. The plugin uploads `dist/**/*.js{,.map}` under the
canonical release name and then deletes the on-disk `.map` files so
they never ship to end users.
CI wiring (`release.yml` + `release-packages.yml`)
- `Build frontend` and `Build and package Tauri app` both receive
`VITE_BUILD_SHA`, `SENTRY_RELEASE`, `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`,
`SENTRY_PROJECT_FRONTEND`. The tauri step needs the same env because
its `beforeBuildCommand` re-runs `vite build`.
- `Build sidecar core binary` receives `OPENHUMAN_BUILD_SHA` so
`option_env!` bakes the short SHA into the release tag.
- `build-cli-linux-arm64` mirrors `OPENHUMAN_BUILD_SHA` and
`OPENHUMAN_APP_ENV` for the arm64 CLI tarball.
Verification support
- New `openhuman sentry-test` CLI subcommand captures an `Error`-level
event against the currently-initialized client, flushes, and prints
the event UUID. Optional `--panic` flag exercises the panic
integration. Requires a DSN resolvable at runtime or baked in at
compile time; exits non-zero otherwise so misconfiguration is loud.
- `src/main.rs` now loads `.env` before `sentry::init`, so a DSN
defined only in the repo-local dotenv file (common dev case) is
honored by the startup-time Sentry client.
Docs
- `docs/sentry.md` covers the release identifier, environment table,
source-map pipeline, required CI variables, and a verification
runbook with troubleshooting tips.
- `.env.example` + `app/.env.example` document the new build-time vars.
This commit is contained in:
@@ -163,6 +163,11 @@ OPENHUMAN_SKILLS_WORKING_MEMORY_ENABLED=true
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Sentry DSN for Rust core error reporting (no PII is sent)
|
||||
OPENHUMAN_SENTRY_DSN=
|
||||
# [optional] Short git SHA baked into the Sentry release tag
|
||||
# (`openhuman@<version>+<sha>`) via `option_env!("OPENHUMAN_BUILD_SHA")`.
|
||||
# CI sets this automatically; leave blank locally (release tag falls back
|
||||
# to `openhuman@<version>`).
|
||||
OPENHUMAN_BUILD_SHA=
|
||||
# [optional] Default: true — set to false to disable anonymized analytics & crash reports
|
||||
OPENHUMAN_ANALYTICS_ENABLED=true
|
||||
|
||||
|
||||
@@ -60,10 +60,29 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
pkg-config libssl-dev build-essential cmake
|
||||
|
||||
- name: Verify Sentry DSN is present
|
||||
shell: bash
|
||||
env:
|
||||
OPENHUMAN_SENTRY_DSN: ${{ vars.OPENHUMAN_SENTRY_DSN }}
|
||||
run: |
|
||||
# Sentry DSN is baked into the binary at compile time via
|
||||
# `option_env!`. Missing DSN here means the arm64 CLI silently
|
||||
# ships without error reporting — fail the job instead.
|
||||
if [ -z "${OPENHUMAN_SENTRY_DSN}" ]; then
|
||||
echo "::error::vars.OPENHUMAN_SENTRY_DSN is empty — the Linux arm64 CLI would ship without error reporting."
|
||||
echo "Configure the repository / environment variable before re-running the release."
|
||||
exit 1
|
||||
fi
|
||||
echo "OPENHUMAN_SENTRY_DSN is set (length=${#OPENHUMAN_SENTRY_DSN})"
|
||||
|
||||
- name: Build CLI binary and package tarball
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
OPENHUMAN_SENTRY_DSN: ${{ vars.OPENHUMAN_SENTRY_DSN }}
|
||||
# Sentry release tracking (#405): keep the arm64 CLI tag in sync
|
||||
# with the desktop build (`openhuman@<version>+<short_sha>`).
|
||||
OPENHUMAN_BUILD_SHA: ${{ github.sha }}
|
||||
OPENHUMAN_APP_ENV: production
|
||||
run: |
|
||||
cargo build --release --bin openhuman-core
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
|
||||
@@ -456,6 +456,14 @@ jobs:
|
||||
# OAuth guardrails (#365): block openhuman://oauth/success on outdated desktop builds.
|
||||
VITE_MINIMUM_SUPPORTED_APP_VERSION: ${{ vars.VITE_MINIMUM_SUPPORTED_APP_VERSION }}
|
||||
VITE_LATEST_APP_DOWNLOAD_URL: ${{ vars.VITE_LATEST_APP_DOWNLOAD_URL }}
|
||||
# Sentry release tracking (#405): baked into the bundle so every event
|
||||
# groups under the same release, and @sentry/vite-plugin uploads the
|
||||
# matching source maps when SENTRY_AUTH_TOKEN is present.
|
||||
VITE_BUILD_SHA: ${{ needs.prepare-build.outputs.sha }}
|
||||
SENTRY_RELEASE: openhuman@${{ needs.prepare-build.outputs.version }}+${{ needs.prepare-build.outputs.sha }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
|
||||
SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT_FRONTEND }}
|
||||
|
||||
- name: Resolve core manifest and binary names
|
||||
id: core-paths
|
||||
@@ -498,6 +506,10 @@ jobs:
|
||||
CORE_BIN_NAME: ${{ steps.core-paths.outputs.core_bin_name }}
|
||||
OPENHUMAN_APP_ENV: ${{ inputs.build_target == 'staging' && 'staging' || 'production' }}
|
||||
OPENHUMAN_SENTRY_DSN: ${{ vars.OPENHUMAN_SENTRY_DSN }}
|
||||
# Sentry release tracking (#405): `option_env!("OPENHUMAN_BUILD_SHA")`
|
||||
# in src/main.rs bakes the short SHA into the release tag
|
||||
# (`openhuman@<version>+<sha>`) so core events match the frontend.
|
||||
OPENHUMAN_BUILD_SHA: ${{ needs.prepare-build.outputs.sha }}
|
||||
|
||||
- name: Stage sidecar for Tauri bundler
|
||||
shell: bash
|
||||
@@ -538,6 +550,20 @@ jobs:
|
||||
OPENHUMAN_APP_ENV: ${{ inputs.build_target == 'staging' && 'staging' || 'production' }}
|
||||
VITE_OPENHUMAN_APP_ENV: ${{ inputs.build_target == 'staging' && 'staging' || 'production' }}
|
||||
VITE_BACKEND_URL: ${{ needs.prepare-build.outputs.base_url }}
|
||||
# Re-declare Vite env so tauri.conf.json's `beforeBuildCommand`
|
||||
# (`vite build`) bakes Sentry + debug flags into the final bundle.
|
||||
# The earlier explicit `yarn build` step ran with these, but the
|
||||
# rebuild triggered here would strip them and ship a DSN-less UI.
|
||||
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
|
||||
VITE_DEBUG: ${{ vars.VITE_DEBUG }}
|
||||
# Sentry release tracking (#405) — must match the first `yarn build`
|
||||
# so source maps uploaded there still resolve against the final bundle
|
||||
# produced by the tauri-driven rebuild.
|
||||
VITE_BUILD_SHA: ${{ needs.prepare-build.outputs.sha }}
|
||||
SENTRY_RELEASE: openhuman@${{ needs.prepare-build.outputs.version }}+${{ needs.prepare-build.outputs.sha }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
|
||||
SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT_FRONTEND }}
|
||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.settings.platform == 'macos-latest' && '10.15' || '' }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD || secrets.UPDATER_PRIVATE_KEY_PASSWORD }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY || secrets.UPDATER_PRIVATE_KEY }}
|
||||
|
||||
@@ -22,6 +22,20 @@ VITE_SKILLS_GITHUB_REPO=tinyhumansai/openhuman-skills
|
||||
# [optional] Sentry DSN for error reporting (leave blank to disable)
|
||||
VITE_SENTRY_DSN=
|
||||
|
||||
# [optional] Short git SHA baked into the frontend bundle for the canonical
|
||||
# Sentry release tag `openhuman@<version>+<sha>`. CI sets this automatically
|
||||
# from `needs.prepare-build.outputs.sha`; leave blank locally (release tag
|
||||
# falls back to `openhuman@<version>`).
|
||||
VITE_BUILD_SHA=
|
||||
|
||||
# [CI-only] Sentry source-map upload — set on CI to enable
|
||||
# `@sentry/vite-plugin`. Leave blank locally; the plugin skips when
|
||||
# `SENTRY_AUTH_TOKEN` is empty.
|
||||
# SENTRY_AUTH_TOKEN=
|
||||
# SENTRY_ORG=
|
||||
# SENTRY_PROJECT=
|
||||
# SENTRY_RELEASE=
|
||||
|
||||
# [optional] Dev-only: auto-inject JWT token to skip login flow
|
||||
VITE_DEV_JWT_TOKEN=
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"@types/redux-logger": "^3.0.13",
|
||||
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^8.54.0",
|
||||
"@sentry/vite-plugin": "^2.22.6",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"@wdio/appium-service": "^9.24.0",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
|
||||
import { getCoreStateSnapshot } from '../lib/coreState/store';
|
||||
import { IS_DEV, SENTRY_DSN } from '../utils/config';
|
||||
import { APP_ENVIRONMENT, IS_DEV, SENTRY_DSN, SENTRY_RELEASE } from '../utils/config';
|
||||
import { enqueueError, registerSentrySender, type SanitizedSentryEvent } from './errorReportQueue';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -87,7 +87,12 @@ export function initSentry(): void {
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
environment: IS_DEV ? 'development' : 'production',
|
||||
environment: APP_ENVIRONMENT,
|
||||
// Canonical release tag shared with the core sidecar and source-map
|
||||
// upload (see @sentry/vite-plugin in app/vite.config.ts). Lets events
|
||||
// from all surfaces group under a single Sentry release with
|
||||
// symbolicated stack traces.
|
||||
release: SENTRY_RELEASE,
|
||||
enabled: !IS_DEV, // disable in dev builds
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@@ -53,6 +53,35 @@ export const DEV_JWT_TOKEN = import.meta.env.DEV
|
||||
|
||||
export const APP_VERSION = packageJson.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`).
|
||||
*
|
||||
|
||||
Vendored
+1
@@ -6,6 +6,7 @@ interface ImportMetaEnv {
|
||||
readonly VITE_BACKEND_URL?: string;
|
||||
readonly VITE_SKILLS_GITHUB_REPO?: string;
|
||||
readonly VITE_SENTRY_DSN?: string;
|
||||
readonly VITE_BUILD_SHA?: string;
|
||||
readonly VITE_DEV_JWT_TOKEN?: string;
|
||||
readonly VITE_DEV_FORCE_ONBOARDING?: string;
|
||||
readonly DEV: boolean;
|
||||
|
||||
+54
-2
@@ -1,10 +1,57 @@
|
||||
import { defineConfig } from "vite";
|
||||
import { defineConfig, type PluginOption } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
||||
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const pkg = JSON.parse(
|
||||
readFileSync(resolve(__dirname, "package.json"), "utf8"),
|
||||
) as { version: string };
|
||||
|
||||
// Canonical Sentry release — must stay in sync with the string produced by
|
||||
// `SENTRY_RELEASE` in app/src/utils/config.ts and the core sidecar's
|
||||
// `sentry::init` in src/main.rs so events from every surface group together.
|
||||
function computeSentryRelease(): string {
|
||||
const raw = (process.env.SENTRY_RELEASE ?? "").trim();
|
||||
if (raw) return raw;
|
||||
const sha = (process.env.VITE_BUILD_SHA ?? "").trim().slice(0, 12);
|
||||
return sha
|
||||
? `openhuman@${pkg.version}+${sha}`
|
||||
: `openhuman@${pkg.version}`;
|
||||
}
|
||||
|
||||
// Gate source-map upload on the presence of SENTRY_AUTH_TOKEN so local dev
|
||||
// and CI jobs that don't ship to users skip the plugin silently. The
|
||||
// companion `SENTRY_ORG` / `SENTRY_PROJECT` come from CI env.
|
||||
function maybeSentryPlugin(): PluginOption | null {
|
||||
const authToken = process.env.SENTRY_AUTH_TOKEN;
|
||||
if (!authToken) return null;
|
||||
return sentryVitePlugin({
|
||||
authToken,
|
||||
org: process.env.SENTRY_ORG,
|
||||
project: process.env.SENTRY_PROJECT,
|
||||
release: { name: computeSentryRelease() },
|
||||
sourcemaps: {
|
||||
// Vite emits hashed asset files under dist/assets/ — upload every
|
||||
// .js / .map the build produces.
|
||||
assets: ["../dist/**/*.js", "../dist/**/*.map"],
|
||||
// Never ship raw .map files to end users; the upload keeps a copy
|
||||
// server-side for symbolication while the bundled app strips them.
|
||||
filesToDeleteAfterUpload: ["../dist/**/*.map"],
|
||||
},
|
||||
// Release tagging + commits are handled by sentry-cli / the plugin
|
||||
// itself when AUTH_TOKEN and CI env (GITHUB_SHA etc.) are present.
|
||||
telemetry: false,
|
||||
});
|
||||
}
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
root: "src",
|
||||
@@ -12,6 +59,10 @@ export default defineConfig(async () => ({
|
||||
build: {
|
||||
outDir: "../dist",
|
||||
emptyOutDir: true,
|
||||
// Emit source maps so @sentry/vite-plugin can upload them; the plugin
|
||||
// deletes the on-disk .map files after upload so users don't receive
|
||||
// them in the shipped bundle.
|
||||
sourcemap: true,
|
||||
},
|
||||
plugins: [
|
||||
nodePolyfills({
|
||||
@@ -23,7 +74,8 @@ export default defineConfig(async () => ({
|
||||
},
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
maybeSentryPlugin(),
|
||||
].filter(Boolean) as PluginOption[],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
# Sentry Release Tracking & Source Maps
|
||||
|
||||
_Tracks issue [#405](https://github.com/tinyhumansai/openhuman/issues/405)._
|
||||
|
||||
OpenHuman reports crashes and errors from two surfaces that must group under
|
||||
a **single Sentry release** so a new deploy's regressions are easy to see:
|
||||
|
||||
- **Frontend** — `@sentry/react` in `app/src/services/analytics.ts`.
|
||||
- **Rust core sidecar** — `sentry::init` in `src/main.rs`.
|
||||
|
||||
The Tauri shell binary (`app/src-tauri`) has no Sentry wiring today.
|
||||
|
||||
## Canonical release identifier
|
||||
|
||||
Both surfaces report the **same** release tag:
|
||||
|
||||
```
|
||||
openhuman@<semver>+<short_git_sha>
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- `<semver>` is `packageJson.version` / `env!("CARGO_PKG_VERSION")`.
|
||||
- `<short_git_sha>` is the first 12 chars of the commit that produced the
|
||||
build. When the SHA is absent (local dev), the tag collapses to
|
||||
`openhuman@<semver>` with no `+` suffix.
|
||||
|
||||
The frontend computes this in `app/src/utils/config.ts::SENTRY_RELEASE`
|
||||
from `VITE_BUILD_SHA`. The core does the same in
|
||||
`src/main.rs::build_release_tag()` from `option_env!("OPENHUMAN_BUILD_SHA")`.
|
||||
|
||||
## Environments
|
||||
|
||||
Reported as the Sentry `environment` tag:
|
||||
|
||||
| Value | When |
|
||||
| ------------- | --------------------------------------------------------------- |
|
||||
| `development` | Local `yarn tauri dev` / debug builds |
|
||||
| `staging` | `VITE_OPENHUMAN_APP_ENV=staging` or `OPENHUMAN_APP_ENV=staging` |
|
||||
| `production` | Release builds from `workflow_dispatch` with `build_target=production` |
|
||||
|
||||
Fallback precedence for the core:
|
||||
|
||||
1. `OPENHUMAN_APP_ENV` env var at runtime (override).
|
||||
2. Compile-time `debug_assertions` → `development`.
|
||||
3. Otherwise → `production`.
|
||||
|
||||
## Source-map upload
|
||||
|
||||
The frontend emits source maps (`vite.config.ts` sets `build.sourcemap =
|
||||
true`). When `SENTRY_AUTH_TOKEN` is present at build time
|
||||
`@sentry/vite-plugin`:
|
||||
|
||||
1. Uploads every `dist/**/*.js` and its `.map` sibling.
|
||||
2. Tags the upload with the canonical release name above.
|
||||
3. **Deletes the on-disk `.map` files after upload** so users never receive
|
||||
them in the shipped bundle.
|
||||
|
||||
If `SENTRY_AUTH_TOKEN` is empty (local dev, smoke CI, forks without
|
||||
secrets), the plugin registers as a no-op — the build still produces source
|
||||
maps on disk but nothing is uploaded. This keeps the local dev loop zero-
|
||||
config.
|
||||
|
||||
## CI configuration
|
||||
|
||||
`release.yml` + `release-packages.yml` thread the following through to the
|
||||
build steps. Any subset can be set on a per-environment basis in the
|
||||
`Production` / `Staging` GitHub Actions environment:
|
||||
|
||||
### Required for upload to work
|
||||
|
||||
| Name | Type | Scope | Purpose |
|
||||
| ------------------------------------- | -------- | --------------- | --------------------------------------------- |
|
||||
| `secrets.SENTRY_AUTH_TOKEN` | secret | build-desktop | Auth for `@sentry/vite-plugin` uploads |
|
||||
| `vars.SENTRY_ORG` | variable | build-desktop | Sentry org slug |
|
||||
| `vars.SENTRY_PROJECT_FRONTEND` | variable | build-desktop | Sentry project slug for the frontend bundle |
|
||||
| `vars.OPENHUMAN_SENTRY_DSN` | variable | build-desktop | Core sidecar DSN (baked via `option_env!`) |
|
||||
| `vars.VITE_SENTRY_DSN` | variable | build-desktop | Frontend DSN (baked by Vite define) |
|
||||
|
||||
### Provided automatically
|
||||
|
||||
| Name | Source |
|
||||
| ------------------------ | ------------------------------------------------ |
|
||||
| `VITE_BUILD_SHA` | `needs.prepare-build.outputs.sha` (tag commit) |
|
||||
| `OPENHUMAN_BUILD_SHA` | Same — passed to `cargo build` for the sidecar |
|
||||
| `SENTRY_RELEASE` | `openhuman@<version>+<sha>` — same on both steps |
|
||||
|
||||
### Personal Sentry DSN (local)
|
||||
|
||||
Drop the DSN into your repo-local `.env`:
|
||||
|
||||
```sh
|
||||
# .env
|
||||
OPENHUMAN_SENTRY_DSN=https://<key>@o<org>.ingest.sentry.io/<project>
|
||||
```
|
||||
|
||||
`src/main.rs` now loads `.env` **before** `sentry::init`, so the runtime
|
||||
env var is visible to the client at startup without needing a manual
|
||||
`source scripts/load-dotenv.sh`.
|
||||
|
||||
For the frontend, put `VITE_SENTRY_DSN` in `app/.env.local`.
|
||||
|
||||
## Verification runbook
|
||||
|
||||
1. **Event arrives**. Trigger a test event from the core CLI:
|
||||
```sh
|
||||
./target/release/openhuman-core sentry-test
|
||||
# or on an installed release (Windows):
|
||||
# "%LOCALAPPDATA%\Programs\OpenHuman\OpenHuman.exe" core sentry-test
|
||||
# or (macOS):
|
||||
# /Applications/OpenHuman.app/Contents/MacOS/openhuman-core-* sentry-test
|
||||
```
|
||||
The command prints an event UUID on success; search it in the Sentry
|
||||
dashboard.
|
||||
2. **Release tag is right**. On the event detail page, the `Release` field
|
||||
should read `openhuman@<version>+<short_sha>` (matching the tag that cut
|
||||
the release).
|
||||
3. **Environment tag is right**. Production CI dispatch → `production`.
|
||||
Staging dispatch → `staging`. Local `yarn tauri dev` → `development`.
|
||||
4. **Stack traces are symbolicated**. Force a frontend error from the
|
||||
installed app; the event's stack trace should show original
|
||||
TypeScript file names and line numbers (not hashed `assets/index-*.js`).
|
||||
5. **CI failure is loud when misconfigured**. If `SENTRY_AUTH_TOKEN` is
|
||||
missing and the release is supposed to upload source maps, the CI run
|
||||
will warn in the Vite build log rather than silently producing an
|
||||
un-symbolicated release.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Events arrive without a release tag** — check the Vite build log for
|
||||
`SENTRY_RELEASE`; if empty, the CI workflow didn't pass it through.
|
||||
- **Events arrive without symbolication** — open the release in Sentry →
|
||||
"Source Maps" tab. Missing artifacts mean either `SENTRY_AUTH_TOKEN` was
|
||||
empty, or the plugin ran but the `assets:` glob didn't match (inspect the
|
||||
upload summary printed during `yarn build`).
|
||||
- **Frontend and core show different releases** — verify
|
||||
`needs.prepare-build.outputs.sha` is identical between the core build
|
||||
step (`OPENHUMAN_BUILD_SHA`) and the frontend build step
|
||||
(`VITE_BUILD_SHA` / `SENTRY_RELEASE`).
|
||||
- **No events from a release build, only from local** — `vars.*` probably
|
||||
isn't defined on the `Production` environment. Set it and re-cut the
|
||||
release.
|
||||
@@ -76,11 +76,106 @@ pub fn run_from_cli_args(args: &[String]) -> Result<()> {
|
||||
);
|
||||
crate::core::agent_cli::run_agent_command(&args[1..])
|
||||
}
|
||||
"sentry-test" => run_sentry_test_command(&args[1..]),
|
||||
// Generic namespace dispatcher: `openhuman <namespace> <function> ...`
|
||||
namespace => run_namespace_command(namespace, &args[1..], &grouped),
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the `sentry-test` subcommand used to verify Sentry wiring end-to-end.
|
||||
///
|
||||
/// Captures an Error-level event against the currently initialized Sentry
|
||||
/// client (see `sentry::init` in the binary entry point), flushes the client,
|
||||
/// and prints the event UUID to stdout. Optional `--panic` flag additionally
|
||||
/// triggers a panic so the panic integration is exercised too.
|
||||
///
|
||||
/// Requires a DSN resolvable at runtime — either via the `OPENHUMAN_SENTRY_DSN`
|
||||
/// env var or baked into the binary at build time via `option_env!`. Absent a
|
||||
/// DSN, the command exits non-zero with a diagnostic instead of silently
|
||||
/// producing no telemetry.
|
||||
fn run_sentry_test_command(args: &[String]) -> Result<()> {
|
||||
let mut message: Option<String> = None;
|
||||
let mut do_panic = false;
|
||||
let mut i = 0usize;
|
||||
|
||||
while i < args.len() {
|
||||
match args[i].as_str() {
|
||||
"--message" => {
|
||||
message = Some(
|
||||
args.get(i + 1)
|
||||
.ok_or_else(|| anyhow::anyhow!("missing value for --message"))?
|
||||
.clone(),
|
||||
);
|
||||
i += 2;
|
||||
}
|
||||
"--panic" => {
|
||||
do_panic = true;
|
||||
i += 1;
|
||||
}
|
||||
"-h" | "--help" => {
|
||||
println!("Usage: openhuman sentry-test [--message <text>] [--panic]");
|
||||
println!();
|
||||
println!(" --message <text> Body of the Error-level event sent to Sentry");
|
||||
println!(" (default: \"openhuman sentry-test ping\")");
|
||||
println!(" --panic After capturing the event, trigger a panic so the");
|
||||
println!(" panic integration reports it as a separate event.");
|
||||
println!();
|
||||
println!("Requires OPENHUMAN_SENTRY_DSN at runtime, or baked into the binary at");
|
||||
println!(
|
||||
"build time via option_env!. On success, prints the event UUID to stdout."
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
other => return Err(anyhow::anyhow!("unknown sentry-test arg: {other}")),
|
||||
}
|
||||
}
|
||||
|
||||
let client = sentry::Hub::current().client();
|
||||
let dsn_host = client
|
||||
.as_deref()
|
||||
.and_then(|c| c.dsn())
|
||||
.map(|d| d.host().to_string());
|
||||
|
||||
match &dsn_host {
|
||||
Some(host) => eprintln!("[sentry-test] Sentry client active (dsn host: {host})"),
|
||||
None => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Sentry is not initialized in this binary — no DSN is resolvable. \
|
||||
Set OPENHUMAN_SENTRY_DSN in the environment (or rebuild with it defined \
|
||||
at compile time) and try again."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let msg = message.unwrap_or_else(|| "openhuman sentry-test ping".to_string());
|
||||
|
||||
sentry::configure_scope(|scope| {
|
||||
scope.set_tag("test", "true");
|
||||
scope.set_tag("source", "sentry-test-cli");
|
||||
});
|
||||
|
||||
let event_id = sentry::capture_message(&msg, sentry::Level::Error);
|
||||
|
||||
if let Some(c) = client {
|
||||
if !c.flush(Some(std::time::Duration::from_secs(5))) {
|
||||
eprintln!(
|
||||
"[sentry-test] WARNING: flush timed out after 5s — event may not have reached Sentry."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!("{event_id}");
|
||||
|
||||
if do_panic {
|
||||
eprintln!(
|
||||
"[sentry-test] Triggering panic as requested — the panic integration should capture it."
|
||||
);
|
||||
panic!("openhuman sentry-test intentional panic");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Loads key/value pairs from a `.env` file into the process environment.
|
||||
///
|
||||
/// This is used for all CLI entrypoints so direct namespace commands pick up
|
||||
@@ -515,6 +610,7 @@ fn print_general_help(grouped: &BTreeMap<String, Vec<ControllerSchema>>) {
|
||||
println!(" openhuman agent <subcommand> [options] (inspect agent definitions & prompts)");
|
||||
println!(" openhuman voice [--hotkey <combo>] [--mode <tap|push>] (voice dictation server)");
|
||||
println!(" openhuman tree-summarizer <subcommand> [options] (summary tree CLI)");
|
||||
println!(" openhuman sentry-test [--message <text>] [--panic] (verify Sentry wiring)");
|
||||
println!(" openhuman <namespace> <function> [--param value ...]\n");
|
||||
println!("Available namespaces:");
|
||||
for namespace in grouped.keys() {
|
||||
|
||||
+51
-6
@@ -14,6 +14,15 @@ use regex::Regex;
|
||||
/// information is redacted before being sent to the server. After setup, it
|
||||
/// delegates execution to the core library based on CLI arguments.
|
||||
fn main() {
|
||||
// Load `.env` before `sentry::init` so a DSN defined only in the dotenv
|
||||
// file is visible to the Sentry client at startup. `dotenvy::dotenv()` is
|
||||
// a no-op for variables already present in the process environment, and
|
||||
// the CLI dispatcher later calls `load_dotenv_for_cli` which honors
|
||||
// `OPENHUMAN_DOTENV_PATH`; this early call handles the common default
|
||||
// case (repo-local `.env`) so startup-time consumers (Sentry, config
|
||||
// overrides) see the same values as runtime RPC handlers.
|
||||
let _ = dotenvy::dotenv();
|
||||
|
||||
// Initialize Sentry as the very first operation so the guard outlives everything.
|
||||
// If OPENHUMAN_SENTRY_DSN is unset or empty, sentry::init returns a no-op guard.
|
||||
let _sentry_guard = sentry::init(sentry::ClientOptions {
|
||||
@@ -23,12 +32,8 @@ fn main() {
|
||||
.or_else(|| option_env!("OPENHUMAN_SENTRY_DSN").map(|s| s.to_string()))
|
||||
.filter(|s| !s.is_empty())
|
||||
.and_then(|s| s.parse().ok()),
|
||||
release: Some(std::borrow::Cow::Borrowed(env!("CARGO_PKG_VERSION"))),
|
||||
environment: Some(if cfg!(debug_assertions) {
|
||||
"development".into()
|
||||
} else {
|
||||
"production".into()
|
||||
}),
|
||||
release: Some(std::borrow::Cow::Owned(build_release_tag())),
|
||||
environment: Some(std::borrow::Cow::Owned(resolve_environment())),
|
||||
send_default_pii: false,
|
||||
before_send: Some(std::sync::Arc::new(|mut event| {
|
||||
// Strip server_name (hostname) to avoid leaking machine identity
|
||||
@@ -57,6 +62,46 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Release / environment resolution for Sentry
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Canonical release tag: `openhuman@<version>[+<short_sha>]`.
|
||||
///
|
||||
/// Matches the string the frontend reports (`SENTRY_RELEASE` in
|
||||
/// `app/src/utils/config.ts`) so events from every surface group under
|
||||
/// the same release in the Sentry dashboard and benefit from the same
|
||||
/// source-map upload.
|
||||
fn build_release_tag() -> String {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let sha = option_env!("OPENHUMAN_BUILD_SHA").unwrap_or("").trim();
|
||||
let sha_short: String = sha.chars().take(12).collect();
|
||||
if sha_short.is_empty() {
|
||||
format!("openhuman@{version}")
|
||||
} else {
|
||||
format!("openhuman@{version}+{sha_short}")
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the deployment environment reported to Sentry.
|
||||
///
|
||||
/// Honors `OPENHUMAN_APP_ENV` at runtime (`staging` / `production`) so the
|
||||
/// same binary could in principle be redeployed between environments; falls
|
||||
/// back to debug/release detection when unset.
|
||||
fn resolve_environment() -> String {
|
||||
if let Ok(value) = std::env::var("OPENHUMAN_APP_ENV") {
|
||||
let trimmed = value.trim().to_ascii_lowercase();
|
||||
if !trimmed.is_empty() {
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
if cfg!(debug_assertions) {
|
||||
"development".to_string()
|
||||
} else {
|
||||
"production".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Secret scrubbing
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz"
|
||||
integrity sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==
|
||||
|
||||
"@babel/core@^7.24.4":
|
||||
"@babel/core@^7.18.5", "@babel/core@^7.24.4":
|
||||
version "7.29.0"
|
||||
resolved "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz"
|
||||
integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==
|
||||
@@ -898,7 +898,7 @@
|
||||
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
|
||||
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5":
|
||||
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5":
|
||||
version "1.5.5"
|
||||
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz"
|
||||
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
|
||||
@@ -1412,6 +1412,11 @@
|
||||
"@sentry-internal/browser-utils" "10.38.0"
|
||||
"@sentry/core" "10.38.0"
|
||||
|
||||
"@sentry/babel-plugin-component-annotate@2.23.1":
|
||||
version "2.23.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.23.1.tgz#c3c87cf8ed776390c7cc9a3e4a14342debf7a793"
|
||||
integrity sha512-l1z8AvI6k9I+2z49OgvP3SlzB1M0Lw24KtceiJibNaSyQwxsItoT9/XftZ/8BBtkosVmNOTQhL1eUsSkuSv1LA==
|
||||
|
||||
"@sentry/browser@10.38.0":
|
||||
version "10.38.0"
|
||||
resolved "https://registry.npmjs.org/@sentry/browser/-/browser-10.38.0.tgz"
|
||||
@@ -1423,6 +1428,74 @@
|
||||
"@sentry-internal/replay-canvas" "10.38.0"
|
||||
"@sentry/core" "10.38.0"
|
||||
|
||||
"@sentry/bundler-plugin-core@2.23.1":
|
||||
version "2.23.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.23.1.tgz#74423d58159e709b59770bd870f068f171a7b470"
|
||||
integrity sha512-JA6utNiwMKv6Jfj0Hmk0DI/XUizSHg7HhhkFETKhRlYEhZAdkyz1atDBg0ncKNgRBKyHeSYWcMFtUyo26VB76w==
|
||||
dependencies:
|
||||
"@babel/core" "^7.18.5"
|
||||
"@sentry/babel-plugin-component-annotate" "2.23.1"
|
||||
"@sentry/cli" "2.39.1"
|
||||
dotenv "^16.3.1"
|
||||
find-up "^5.0.0"
|
||||
glob "^9.3.2"
|
||||
magic-string "0.30.8"
|
||||
unplugin "1.0.1"
|
||||
|
||||
"@sentry/cli-darwin@2.39.1":
|
||||
version "2.39.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.39.1.tgz#75c338a53834b4cf72f57599f4c72ffb36cf0781"
|
||||
integrity sha512-kiNGNSAkg46LNGatfNH5tfsmI/kCAaPA62KQuFZloZiemTNzhy9/6NJP8HZ/GxGs8GDMxic6wNrV9CkVEgFLJQ==
|
||||
|
||||
"@sentry/cli-linux-arm64@2.39.1":
|
||||
version "2.39.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.39.1.tgz#27db44700c33fcb1e8966257020b43f8494373e6"
|
||||
integrity sha512-5VbVJDatolDrWOgaffsEM7znjs0cR8bHt9Bq0mStM3tBolgAeSDHE89NgHggfZR+DJ2VWOy4vgCwkObrUD6NQw==
|
||||
|
||||
"@sentry/cli-linux-arm@2.39.1":
|
||||
version "2.39.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.39.1.tgz#451683fa9a5a60b1359d104ec71334ed16f4b63c"
|
||||
integrity sha512-DkENbxyRxUrfLnJLXTA4s5UL/GoctU5Cm4ER1eB7XN7p9WsamFJd/yf2KpltkjEyiTuplv0yAbdjl1KX3vKmEQ==
|
||||
|
||||
"@sentry/cli-linux-i686@2.39.1":
|
||||
version "2.39.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.39.1.tgz#9965a81f97a94e8b6d1d15589e43fee158e35201"
|
||||
integrity sha512-pXWVoKXCRrY7N8vc9H7mETiV9ZCz+zSnX65JQCzZxgYrayQPJTc+NPRnZTdYdk5RlAupXaFicBI2GwOCRqVRkg==
|
||||
|
||||
"@sentry/cli-linux-x64@2.39.1":
|
||||
version "2.39.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.39.1.tgz#31fe008b02f92769543dc9919e2a5cbc4cda7889"
|
||||
integrity sha512-IwayNZy+it7FWG4M9LayyUmG1a/8kT9+/IEm67sT5+7dkMIMcpmHDqL8rWcPojOXuTKaOBBjkVdNMBTXy0mXlA==
|
||||
|
||||
"@sentry/cli-win32-i686@2.39.1":
|
||||
version "2.39.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.39.1.tgz#609e8790c49414011445e397130560c777850b35"
|
||||
integrity sha512-NglnNoqHSmE+Dz/wHeIVRnV2bLMx7tIn3IQ8vXGO5HWA2f8zYJGktbkLq1Lg23PaQmeZLPGlja3gBQfZYSG10Q==
|
||||
|
||||
"@sentry/cli-win32-x64@2.39.1":
|
||||
version "2.39.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.39.1.tgz#1a874a5570c6d162b35d9d001c96e5389d07d2cb"
|
||||
integrity sha512-xv0R2CMf/X1Fte3cMWie1NXuHmUyQPDBfCyIt6k6RPFPxAYUgcqgMPznYwVMwWEA1W43PaOkSn3d8ZylsDaETw==
|
||||
|
||||
"@sentry/cli@2.39.1":
|
||||
version "2.39.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.39.1.tgz#916bb5b7567ccf7fdf94ef6cf8a2b9ab78370d29"
|
||||
integrity sha512-JIb3e9vh0+OmQ0KxmexMXg9oZsR/G7HMwxt5BUIKAXZ9m17Xll4ETXTRnRUBT3sf7EpNGAmlQk1xEmVN9pYZYQ==
|
||||
dependencies:
|
||||
https-proxy-agent "^5.0.0"
|
||||
node-fetch "^2.6.7"
|
||||
progress "^2.0.3"
|
||||
proxy-from-env "^1.1.0"
|
||||
which "^2.0.2"
|
||||
optionalDependencies:
|
||||
"@sentry/cli-darwin" "2.39.1"
|
||||
"@sentry/cli-linux-arm" "2.39.1"
|
||||
"@sentry/cli-linux-arm64" "2.39.1"
|
||||
"@sentry/cli-linux-i686" "2.39.1"
|
||||
"@sentry/cli-linux-x64" "2.39.1"
|
||||
"@sentry/cli-win32-i686" "2.39.1"
|
||||
"@sentry/cli-win32-x64" "2.39.1"
|
||||
|
||||
"@sentry/core@10.38.0":
|
||||
version "10.38.0"
|
||||
resolved "https://registry.npmjs.org/@sentry/core/-/core-10.38.0.tgz"
|
||||
@@ -1436,6 +1509,14 @@
|
||||
"@sentry/browser" "10.38.0"
|
||||
"@sentry/core" "10.38.0"
|
||||
|
||||
"@sentry/vite-plugin@^2.22.6":
|
||||
version "2.23.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.23.1.tgz#352bc2e2349f6a7f6c704f4f105382c4d33083e0"
|
||||
integrity sha512-avtjtIQ019sZW3FklpmNNsQOnYZjCHpnVxgDGElfZb+AaR4AvtHNlxXLJp+iqEfSK+Xok8MJarJqIgCaWcF40Q==
|
||||
dependencies:
|
||||
"@sentry/bundler-plugin-core" "2.23.1"
|
||||
unplugin "1.0.1"
|
||||
|
||||
"@sinclair/typebox@^0.34.0":
|
||||
version "0.34.48"
|
||||
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz"
|
||||
@@ -2313,6 +2394,18 @@ acorn@^8.15.0:
|
||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz"
|
||||
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
|
||||
|
||||
acorn@^8.8.1:
|
||||
version "8.16.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a"
|
||||
integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
dependencies:
|
||||
debug "4"
|
||||
|
||||
agent-base@^7.1.0, agent-base@^7.1.2:
|
||||
version "7.1.4"
|
||||
resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz"
|
||||
@@ -3483,6 +3576,11 @@ domutils@^3.0.1, domutils@^3.2.2:
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.3"
|
||||
|
||||
dotenv@^16.3.1:
|
||||
version "16.6.1"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020"
|
||||
integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==
|
||||
|
||||
dotenv@^17.2.0:
|
||||
version "17.2.4"
|
||||
resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz"
|
||||
@@ -4461,6 +4559,16 @@ glob@^8.1.0:
|
||||
minimatch "^5.0.1"
|
||||
once "^1.3.0"
|
||||
|
||||
glob@^9.3.2:
|
||||
version "9.3.5"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21"
|
||||
integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
minimatch "^8.0.2"
|
||||
minipass "^4.2.4"
|
||||
path-scurry "^1.6.1"
|
||||
|
||||
globals@^14.0.0:
|
||||
version "14.0.0"
|
||||
resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz"
|
||||
@@ -4671,6 +4779,14 @@ https-browserify@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz"
|
||||
integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==
|
||||
|
||||
https-proxy-agent@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
|
||||
dependencies:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
https-proxy-agent@^7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz"
|
||||
@@ -5520,6 +5636,13 @@ lz-string@^1.5.0:
|
||||
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz"
|
||||
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
|
||||
|
||||
magic-string@0.30.8:
|
||||
version "0.30.8"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.8.tgz#14e8624246d2bedba70d5462aa99ac9681844613"
|
||||
integrity sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||
|
||||
magic-string@^0.30.12, magic-string@^0.30.21, magic-string@^0.30.3:
|
||||
version "0.30.21"
|
||||
resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz"
|
||||
@@ -5921,6 +6044,13 @@ minimatch@^5.0.1, minimatch@^5.1.0, minimatch@^5.1.6:
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimatch@^8.0.2:
|
||||
version "8.0.7"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.7.tgz#954766e22da88a3e0a17ad93b58c15c9d8a579de"
|
||||
integrity sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimatch@^9.0.0, minimatch@^9.0.4, minimatch@^9.0.5:
|
||||
version "9.0.5"
|
||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz"
|
||||
@@ -5933,6 +6063,11 @@ minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8:
|
||||
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
minipass@^4.2.4:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a"
|
||||
integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==
|
||||
|
||||
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz"
|
||||
@@ -6008,6 +6143,13 @@ netmask@^2.0.2:
|
||||
resolved "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz"
|
||||
integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
|
||||
|
||||
node-fetch@^2.6.7:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-releases@^2.0.27:
|
||||
version "2.0.27"
|
||||
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz"
|
||||
@@ -6433,7 +6575,7 @@ path-parse@^1.0.7:
|
||||
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
path-scurry@^1.11.1:
|
||||
path-scurry@^1.11.1, path-scurry@^1.6.1:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz"
|
||||
integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
|
||||
@@ -7814,6 +7956,11 @@ tr46@^6.0.0:
|
||||
dependencies:
|
||||
punycode "^2.3.1"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||
|
||||
tree-kill@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz"
|
||||
@@ -8032,6 +8179,16 @@ unist-util-visit@^5.0.0:
|
||||
unist-util-is "^6.0.0"
|
||||
unist-util-visit-parents "^6.0.0"
|
||||
|
||||
unplugin@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-1.0.1.tgz#83b528b981cdcea1cad422a12cd02e695195ef3f"
|
||||
integrity sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==
|
||||
dependencies:
|
||||
acorn "^8.8.1"
|
||||
chokidar "^3.5.3"
|
||||
webpack-sources "^3.2.3"
|
||||
webpack-virtual-modules "^0.5.0"
|
||||
|
||||
update-browserslist-db@^1.2.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz"
|
||||
@@ -8239,11 +8396,26 @@ webdriverio@9.24.0:
|
||||
urlpattern-polyfill "^10.0.0"
|
||||
webdriver "9.24.0"
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
||||
|
||||
webidl-conversions@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz"
|
||||
integrity sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==
|
||||
|
||||
webpack-sources@^3.2.3:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.4.tgz#a338b95eb484ecc75fbb196cbe8a2890618b4891"
|
||||
integrity sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==
|
||||
|
||||
webpack-virtual-modules@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz#362f14738a56dae107937ab98ea7062e8bdd3b6c"
|
||||
integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==
|
||||
|
||||
whatwg-encoding@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz"
|
||||
@@ -8270,6 +8442,14 @@ whatwg-url@^16.0.0:
|
||||
tr46 "^6.0.0"
|
||||
webidl-conversions "^8.0.1"
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
|
||||
which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz"
|
||||
|
||||
Reference in New Issue
Block a user