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:
CodeGhost21
2026-04-21 22:48:20 -07:00
committed by GitHub
parent b45e0ed262
commit bdbb83772e
13 changed files with 628 additions and 13 deletions
+5
View File
@@ -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
+19
View File
@@ -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 }}"
+26
View File
@@ -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 }}
+14
View File
@@ -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=
+1
View File
@@ -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",
+7 -2
View File
@@ -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
// -----------------------------------------------------------------------
+29
View File
@@ -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`).
*
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+96
View File
@@ -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
View File
@@ -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
// ---------------------------------------------------------------------------
+183 -3
View File
@@ -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"