| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
Optimised images with calibre/image-actions | 1 年前 | |
Wire @rdi-ui/pipeline into the standalone v2 pipeline management page (#6506) * feat(rdi): wire @rdi-ui/pipeline into the standalone v2 page | 1 天前 | |
rework configs | 1 年前 | |
Initial commit | 4 年前 | |
RI-8398 Pass plugin visualization config via a JSON element (#6505) * refactor(ui): read plugin iframe config from a json element | 14 天前 | |
Initial commit | 4 年前 | |
Ri-7334: icons titles and link handlers | 11 个月前 | |
RI-8113 Add Sentry error tracking for the Electron app (#6073) * RI-7977: setup Sentry projects * remove sentry from api * update sentry env variables * link electron sentry project * remove docs * chore: reconcile yarn.lock after rebase onto main Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): report main-process test crash via captureException A synchronous throw inside the globalShortcut callback is swallowed by Electron's native dispatch and never reaches Sentry's uncaughtException handler, so Cmd/Ctrl+Shift+K reported nothing. Capture the error explicitly instead, matching the shortcut's non-crashing intent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add RI-8113 production-readiness design Two-tier consent model inside Sentry (anonymous Tier 1 / consented Tier 2), scrubbing security-review checklist, early-crash + consent-caching handling, and mapping to the RI-8113 scope. Intended for Legal/Security review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): record Legal/Security decisions in readiness doc Tier-1 allowlist accepted; IP not tracked; region confirmation pending (EU recommended); retention defaulted to 90 days. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): record Sentry org region as US (residency decision pending) Org is US-region (per DSN host). Region is fixed at org creation, so Legal should confirm US is acceptable for anonymized crash data, or an EU org must be created before launch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): confirm US region approved (residency resolved) Sentry is an org-approved vendor; all data stored in US. No EU org needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): two-tier consent gating + shared scrubbing (RI-8113) Implements the consent model from docs/sentry-production-readiness.md: - Shared, SDK-agnostic scrubbing module (ui/src/services/sentry) used by both the main process and renderer so the layers cannot drift: scrubEvent (redacts extra/contexts/request/breadcrumbs, normalizes stack-frame paths, strips server_name/IP) + minimizeEvent (Tier 1 anonymous allowlist under a shared sentinel id). - Main process: default-deny consent flag cached in electron-store for a synchronous boot read; beforeSend sends full (scrubbed) events with consent and minimized anonymous events without; breadcrumbs gated on consent; native crashReporter upload gated behind consent (start-once); setConsent() updates the tier at runtime. - Renderer: beforeSend self-gates per-event via checkIsAnalyticsGranted. - Consent plumbing: useSentryConsentSync pushes agreements.analytics to the main process over IPC (setSentryConsent) on startup and on toggle. - Unit tests for the scrubbing module. Note: autoSessionTracking/serverName options dropped where the Sentry v10 type no longer accepts them (renderer); server_name is cleared per-event. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(desktop): type-correct rollup external in vite.main.config The PoC added a predicate function into rollup's `external` array, which only accepts string | RegExp — a TS2769 overload error surfaced after the rebase pulled in newer vite/rollup types. Fold the module list, regex patterns, and the api-dist predicate into a single `external` function (behavior-preserving). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(test): preserve subpaths in @redislabsdev->@redis-ui jest mapping @redis-ui/components v44 (pulled in by the rebase) imports icons via subpaths, e.g. @redislabsdev/redis-ui-icons/multicolor. The moduleNameMapper matched only the bare package name and dropped the subpath, so the mapped module resolved to the package root and LoaderLargeIcon was undefined — styled(undefined) crashed every UI test suite at setup. Anchor each mapping and capture the optional subpath ($1) so deep imports survive the rename. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): redesign ErrorBoundary fallback (RI-8113) Make the title the visual focal point (was small/faded vs the button), mute the supporting copy, and replace the alarming brand-red action with the calm primary blue (recovery, not crash). Colors are theme-aware via the persisted document.body theme class and mirror redis-ui neutral/ primary tokens — inlined because the boundary renders above ThemeProvider, where useTheme()/themed CSS variables are unavailable when a crash is caught. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): adjust ErrorBOundary copy * refactor(sentry): split reporting from the error boundary Separate the generic ErrorBoundary (catch + fallback UI + onError callback) from SentryErrorBoundary (a thin wrapper that injects Sentry reporting). Fixes a real robustness gap: the Sentry capture call previously ran unguarded in componentDidCatch, so if it threw, the error escaped the boundary and the fallback UI never rendered. ErrorBoundary now invokes onError inside try/catch, so a reporting failure can never prevent the fallback from showing. The generic boundary is also reusable/testable without any Sentry dependency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): adjust ErrorBoundary copy * refactor(sentry): split ErrorBoundary into types/styles/constants/utils Follow the component-folder convention: extract ErrorBoundaryProps/State and Palette to .types.ts, the inline CSSProperties to .styles.ts, the palettes + copy to .constants.ts, and getPalette to .utils.ts. The component file is now just the boundary logic + markup. Styles stay plain CSSProperties (not styled-components) because the boundary renders above ThemeProvider; the rationale lives in the constants/styles file headers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(sentry): add ErrorBoundary tests Cover the fallback rendering, custom fallback, onError forwarding, and the robustness guarantee that the fallback still renders when onError throws. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add source-maps decision (version-only release + debug IDs) Record §9: source maps are required for real debugging value; decision is release=pkg.version (no per-build SHA) + debug IDs for bundle/map matching, SHA as metadata only. Captures current build state, implementation outline, and the build-determinism open question. Also mark ErrorBoundary refinement done. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): upload source maps via bundler plugins (RI-8113) Add @sentry/vite-plugin (renderer) and @sentry/webpack-plugin (main), gated on SENTRY_AUTH_TOKEN so they are a no-op for local/dev builds. When the token is present (CI) they generate hidden source maps, inject debug IDs into bundle+map, upload to Sentry, and delete the maps so they never ship in the app. Release name = pkg.version (matches the runtime release); debug IDs handle bundle/map matching, so no per-build SHA is needed. Wire the env vars into all four build pipelines, and add the missing RI_SENTRY_UI_DSN (the renderer Sentry layer reads it but it was never passed). New CI config required (see readiness doc): SENTRY_AUTH_TOKEN (secret), SENTRY_ORG / SENTRY_PROJECT_UI / SENTRY_PROJECT_ELECTRON (vars), and the RI_SENTRY_UI_DSN secret. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): mark source maps implemented; list required CI secrets/vars Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): RI_ prefix source-map env vars + report from web build 1. Prefix the source-map env vars with RI_ for consistency: RI_SENTRY_AUTH_TOKEN / RI_SENTRY_ORG / RI_SENTRY_PROJECT_UI / RI_SENTRY_PROJECT_ELECTRON (vite + webpack configs and all four pipelines). Note: the auth token is build-time only and must never be read via import.meta.env in client code. 2. Initialize Sentry in the web entry (index.tsx) via a new services/sentryWeb.ts using @sentry/react, mirroring the Electron renderer (consent-gated two-tier reporting, shared scrubbing). All three targets now report: Electron main, Electron renderer, and web/docker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): UI config uses RI_SENTRY_UI_DSN, not the electron DSN A PoC commit (link electron sentry project) repointed the UI sentry.dsn at RI_SENTRY_ELECTRON_DSN, so the renderer and web reported to the Electron project and RI_SENTRY_UI_DSN was never read. Point it back at the UI DSN. Also document that this sentry block is runtime-overridable per host via the domainConfig merge in config/index.ts — the channel the cloud deployment will use to point the web build at its own Sentry project (no refactor of the init code needed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add cloud (RedisInsight-Cloud) compatibility note Capture that this PR is forward-compatible with the cloud runtime-config approach (getConfig deep-merges per-host domainConfig; init reads config not env; sync resolution = no re-init), the additive cloud work, and the exact integration point. Records that cloud serves a prebuilt S3 web artifact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): single RI_SENTRY_DSN for all layers Consolidate the split RI_SENTRY_ELECTRON_DSN / RI_SENTRY_UI_DSN into one RI_SENTRY_DSN shared by the Electron main process, renderer, and web — one Sentry project, filtered by platform/environment within it (no second project to maintain). Update the main init, UI config, webpack EnvironmentPlugin, all four pipelines, and the readiness doc. Also drop the redundant source-map comment lines from the workflow env blocks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): single RI_SENTRY_PROJECT for source-map uploads With one Sentry project, the renderer and main source-map uploads target the same project. Collapse RI_SENTRY_PROJECT_UI / RI_SENTRY_PROJECT_ELECTRON into a single RI_SENTRY_PROJECT (vite + webpack configs, all four pipelines, doc). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): default RI_SENTRY_ENVIRONMENT to development in main bundle The webpack EnvironmentPlugin defaulted RI_SENTRY_ENVIRONMENT to 'production' while the runtime code and UI config default to 'development'. Align it to 'development' and fix the stale doc comment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): stop tracking the production-readiness doc Keep the readiness/planning doc local-only (added to .git/info/exclude), like docs/pr-plan-RI-7682-decouple-fe-be.md. It should not land in the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * revert(test): drop jest mapper change; pin @redis-ui to main's 44.0.2 The jest.config.cjs subpath-mapping change wasn't actually needed — it was masking an unintended lockfile drift. package.json specs match main (^44.0.2 etc.), but the lockfile had drifted @redis-ui to 44.1.0/6.11.0/ 15.1.0/3.8.0 (44.1.0 introduced the @redislabsdev/*/subpath imports that broke the old mapper). main pins 44.0.2, where the original mapper works. Pin the four @redis-ui packages back to main's resolved versions (Sentry SDKs untouched at 10.39.0/7.8.0) and revert jest.config.cjs to match main. Tests pass and type-check is clean at 44.0.2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: restore api/yarn.lock to match main The rebase resolved the api/yarn.lock conflict with --theirs (the month-old PoC lockfile) and it was never reconciled, leaving ~1300 lines of stale churn against main even though redisinsight/api/package.json is identical to main. The PoC removed its API Sentry deps, so there is no API dependency change on this branch — restore main's api/yarn.lock (frozen-lockfile consistent with the unchanged package.json). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: rebuild root yarn.lock as main + Sentry subtree only The rebase-era reinstall had drifted hundreds of unrelated transitive deps (babel, jest, inquirer, colordx, @electron/get, rollup, ...) within their caret ranges, bloating the diff vs main. Rebuild from main's lockfile and re-add only the Sentry deps, pinning @sentry/react@10.39.0 and @sentry/electron@7.8.0 (the pair that dedupes @sentry/core — newer 10.57/ 7.13 resolve a duplicate core and fail type-check). @redis-ui stays at main's 44.0.2. Net result: diff vs main is now just the Sentry/OpenTelemetry subtree (+ magic-string 0.30.19->0.30.21, pulled transitively). frozen-lockfile consistent, type-check clean, tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): drop verbose comments Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): make source-map upload failures non-fatal Add an errorHandler to both bundler plugins so a failed upload (Sentry outage, bad token, network blip) warns and continues instead of failing the build — matching the cloud-ui convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): gate source-map generation+upload on RI_SENTRY_ENABLED Previously the upload was gated only on RI_SENTRY_AUTH_TOKEN, so a build with the token but RI_SENTRY_ENABLED=false would still generate and upload maps — and since generation was token-only while the deleting plugin was gated separately, those maps could ship inside the asar. Couple both map generation and upload to (auth token AND RI_SENTRY_ENABLED === 'true') in the vite and webpack configs, so a disabled build produces no maps at all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): read RI_SENTRY_ENABLED from vars, not secrets RI_SENTRY_ENABLED is a non-sensitive flag and is configured as a Repository variable, but the pipelines read it from secrets.* — which silently resolved to empty, disabling Sentry everywhere. Read it from vars.* so the repository variable is actually picked up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): correct error-boundary theme detection + tidy logging - getPalette read lowercase 'theme_light/dark' but themeService writes the Theme enum value uppercase ('theme_LIGHT'/'theme_DARK'), so it never matched and always fell back to OS/dark. Use the Theme enum to build the class name. - Replace console.log with console.warn in the renderer Sentry services and drop the eslint-disable (no console.log in production code); remove the noisy success-init logs. - Fix stale setConsent JSDoc (revoke does not close the client). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style(sentry): prettier-format scrubbing spec Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: refresh desktop tscheck baseline (-6 from vite.main.config fix) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): tag events with app.layer (electron-main/renderer/web) Distinguishes which layer a crash came from — a dimension not otherwise captured (renderer and web are both javascript-platform). OS stays in the auto-captured os context. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): label source-map bundles by build OS via dist Set the upload dist to the build OS (macos/windows/linux from process.platform on each per-OS CI runner) so the per-platform source-map bundles are distinguishable in Sentry's Source Maps view. inject: false keeps dist on the uploaded bundle only (release is already set in Sentry.init) — events are untouched and still match via debug IDs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(build): raise vite build heap to 8192 (source-map OOM on macOS CI) Enabling hidden source maps + the Sentry upload plugin pushed the renderer vite build over the V8 heap limit, OOM-crashing build:renderer (SIGABRT) only on the macOS runner (~7GB RAM vs more on linux/windows). Bump NODE_OPTIONS=--max-old-space-size=8192 on the vite build, matching the repo's existing stats script and the cloud-ui convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): use process.platform directly for source-map dist Drop the cosmetic darwin->macos map; process.platform (darwin/win32/linux) is fine as the dist label. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): remove PoC crash-trigger code Remove the test crash triggers added during the PoC: the Cmd+Shift+K/C global shortcuts and triggerTestCrash/triggerNativeCrash imports in app.ts, the Test Helpers block (triggerTestCrash/triggerNativeCrash) in sentry.ts, and the Crash Handler / Crash React buttons in HelpMenu.tsx. HelpMenu.tsx returns to 0 diff vs main; app.ts keeps only initSentry(). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): wire renderer IPC bridge via @sentry/electron/preload The renderer SDK (@sentry/electron/renderer) relays events to the main process over an IPC bridge that the preload script must establish. Without it the renderer falls back to the unsupported `sentry-ipc://` protocol ("URL scheme not supported" / "failed to establish connection with the Electron main process"), so renderer-side errors (React errors, the error boundary) were never reported — only main-process crashes were. Importing `@sentry/electron/preload` sets up the bridge. The package is a root dependency and not externalized by webpack.config.base, so it is bundled into preload.js (prod webpack and dev vite alike). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): correct web source-map deletion path and release Address two Cursor Bugbot findings on the web/Docker build path: - filesToDeleteAfterUpload was hardcoded to the Electron renderer outDir (`../dist/renderer`), so web builds (outDir `./dist`) uploaded hidden source maps but never deleted them — they could ship in the artifact. Use the build's actual `outDir` glob instead. - sentryWeb.ts set `release` from redisinsight/ui/package.json (2.66.0) while the Vite upload plugin uses defaultConfig.app.version (redisinsight/package.json, 3.6.0). Web events and uploaded maps would disagree on release and fail to symbolicate. Use riConfig.app.version, the same source the upload plugin uses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): gate native crash minidumps on consent; init renderer early Addresses AI review findings on the Sentry PR: - Disable @sentry/electron's default minidump integration. It starts Electron's crashReporter at init and uploads minidumps (process memory) via the SDK transport regardless of analytics consent — bypassing the Tier-1 gate (beforeSend cannot scrub a minidump attachment) and conflicting with our own consent-gated crashReporter.start. Native crashes now flow only through the consent-gated initCrashReporter. - Honour consent revocation/grant at runtime: setConsent now toggles crashReporter.setUploadToServer so minidump uploads stop immediately on revoke and resume on re-grant, instead of only taking effect next launch. - Initialize the renderer Sentry before the windowId IPC round-trip so renderer errors during early startup are captured (matching the web entrypoint). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): remove unused main-process exports captureException, setUser and isSentryInitialized are no longer referenced in production code (the PoC crash triggers that used them were removed). Dropping them keeps the main-process Sentry surface to initSentry/setConsent. Note: anonymity for non-consenting users does not depend on setUser — the Tier-1 path (minimizeEvent) already stamps the shared NON_TRACKING_ANONYMOUS_ID on every no-consent event. This commit is isolated so the sentry-tracking-crash branch can restore the crash-test helpers by reverting just this commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): redact secrets embedded in free-text event fields scrubSensitiveData only redacts by key name, so a secret living inside a string value — most notably a Redis connection URI in a thrown error (redis://user:pass@host) — passed through untouched on the consented path (beforeSend returns scrubEvent's result directly; only Tier 1 minimized it). Add scrubSecretsInText, applied in scrubEvent to the free-text fields: event.message, exception.values[].value, breadcrumbs[].message, request.url/query_string, and stack-frame source context. It redacts URI userinfo credentials and password/token/secret/apiKey assignments. Runs on both tiers (defense-in-depth); best-effort/heuristic, not a guarantee. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): accept RI_SENTRY_ENABLED=1 in main; cover Windows file URLs - Main process now enables Sentry on 'true' OR '1', matching the renderer/web booleanEnv semantics, so the two layers no longer diverge when RI_SENTRY_ENABLED=1 (main was strict-'true' only). - Add normalizePath regression tests for C:/Users/... and file:///C:/Users/... — the /Users/ branch already normalizes these (prefix-agnostic); the tests lock it in against regex regressions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): align source-map gates with runtime; widen free-text token scrubbing - Source-map upload gates (vite + webpack) now accept RI_SENTRY_ENABLED='1' as well as 'true', matching the runtime/booleanEnv semantics so a build configured with '1' still symbolicates (previously: events on, maps off). - Free-text scrubber now redacts Bearer/Basic auth scheme credentials and OAuth-prefixed token names (access_token, refreshToken, ...), which the previous assignment pattern missed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): disable release-health session tracking (consent bypass) Default session integrations emit release-health envelopes on load/navigation that are NOT events, so they bypass the beforeSend consent gate and would send usage telemetry for opted-out users. Disable them in all three layers: - web + Electron renderer: BrowserSession - main: MainProcessSession Also align the Electron renderer's `release` to riConfig.app.version (same source as the web init and the Vite upload plugin) for consistency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): delete hidden source maps even when upload fails filesToDeleteAfterUpload only runs after a successful upload, and the upload errorHandler is intentionally non-fatal — so a failed upload (bad token, 5xx, network) left the emitted .js.map files in the output dir to be packaged. Delete them in the errorHandler too (both vite + webpack) so maps never ship regardless of upload outcome. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): keep debug_meta in Tier-1 so anonymous crashes symbolicate minimizeEvent dropped debug_meta and minimizeFrame dropped abs_path, so no-consent (Tier-1) events could not be matched to uploaded source maps and showed minified frames. Keep debug_meta (code_file + debug_id) and abs_path, normalizing any user path defensively. These carry no PII — build paths + debug-id UUIDs — so anonymous stacks become readable without weakening the Tier-1 redaction. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): scrub secrets in structured-bag string values; normalize request url scrubSensitiveData only redacts by key name, so a secret inside a string value (e.g. a token/credential in a breadcrumb's data.url) passed through on the consented path. Add scrubSecretsDeep, applied to extra/contexts/request/ breadcrumb data, so string values are also free-text scrubbed. Also run normalizePath over request.url to strip an OS account name from a Windows file:// page URL (matching how stack-frame paths are handled). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): skip native crashReporter in Mac App Store builds Electron's crashReporter is unsupported under the MAS App Sandbox. Guard process.mas in initCrashReporter so MAS builds skip native minidumps while JS-level Sentry reporting continues to work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): error boundary message wording * refactor(sentry): trim comments; guard renderer/web init with try/catch Addresses review feedback on the PR: - Trim comments across the Sentry modules to short why-only notes (per the comments rule in #6091); drop how/story comments and stale doc references. - Wrap Sentry.init in try/catch in the web and Electron-renderer initSentry (matching the main process) so a Sentry init failure can't break app startup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> | 3 个月前 | |
RI-8113 Add Sentry error tracking for the Electron app (#6073) * RI-7977: setup Sentry projects * remove sentry from api * update sentry env variables * link electron sentry project * remove docs * chore: reconcile yarn.lock after rebase onto main Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): report main-process test crash via captureException A synchronous throw inside the globalShortcut callback is swallowed by Electron's native dispatch and never reaches Sentry's uncaughtException handler, so Cmd/Ctrl+Shift+K reported nothing. Capture the error explicitly instead, matching the shortcut's non-crashing intent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add RI-8113 production-readiness design Two-tier consent model inside Sentry (anonymous Tier 1 / consented Tier 2), scrubbing security-review checklist, early-crash + consent-caching handling, and mapping to the RI-8113 scope. Intended for Legal/Security review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): record Legal/Security decisions in readiness doc Tier-1 allowlist accepted; IP not tracked; region confirmation pending (EU recommended); retention defaulted to 90 days. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): record Sentry org region as US (residency decision pending) Org is US-region (per DSN host). Region is fixed at org creation, so Legal should confirm US is acceptable for anonymized crash data, or an EU org must be created before launch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): confirm US region approved (residency resolved) Sentry is an org-approved vendor; all data stored in US. No EU org needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): two-tier consent gating + shared scrubbing (RI-8113) Implements the consent model from docs/sentry-production-readiness.md: - Shared, SDK-agnostic scrubbing module (ui/src/services/sentry) used by both the main process and renderer so the layers cannot drift: scrubEvent (redacts extra/contexts/request/breadcrumbs, normalizes stack-frame paths, strips server_name/IP) + minimizeEvent (Tier 1 anonymous allowlist under a shared sentinel id). - Main process: default-deny consent flag cached in electron-store for a synchronous boot read; beforeSend sends full (scrubbed) events with consent and minimized anonymous events without; breadcrumbs gated on consent; native crashReporter upload gated behind consent (start-once); setConsent() updates the tier at runtime. - Renderer: beforeSend self-gates per-event via checkIsAnalyticsGranted. - Consent plumbing: useSentryConsentSync pushes agreements.analytics to the main process over IPC (setSentryConsent) on startup and on toggle. - Unit tests for the scrubbing module. Note: autoSessionTracking/serverName options dropped where the Sentry v10 type no longer accepts them (renderer); server_name is cleared per-event. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(desktop): type-correct rollup external in vite.main.config The PoC added a predicate function into rollup's `external` array, which only accepts string | RegExp — a TS2769 overload error surfaced after the rebase pulled in newer vite/rollup types. Fold the module list, regex patterns, and the api-dist predicate into a single `external` function (behavior-preserving). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(test): preserve subpaths in @redislabsdev->@redis-ui jest mapping @redis-ui/components v44 (pulled in by the rebase) imports icons via subpaths, e.g. @redislabsdev/redis-ui-icons/multicolor. The moduleNameMapper matched only the bare package name and dropped the subpath, so the mapped module resolved to the package root and LoaderLargeIcon was undefined — styled(undefined) crashed every UI test suite at setup. Anchor each mapping and capture the optional subpath ($1) so deep imports survive the rename. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): redesign ErrorBoundary fallback (RI-8113) Make the title the visual focal point (was small/faded vs the button), mute the supporting copy, and replace the alarming brand-red action with the calm primary blue (recovery, not crash). Colors are theme-aware via the persisted document.body theme class and mirror redis-ui neutral/ primary tokens — inlined because the boundary renders above ThemeProvider, where useTheme()/themed CSS variables are unavailable when a crash is caught. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): adjust ErrorBOundary copy * refactor(sentry): split reporting from the error boundary Separate the generic ErrorBoundary (catch + fallback UI + onError callback) from SentryErrorBoundary (a thin wrapper that injects Sentry reporting). Fixes a real robustness gap: the Sentry capture call previously ran unguarded in componentDidCatch, so if it threw, the error escaped the boundary and the fallback UI never rendered. ErrorBoundary now invokes onError inside try/catch, so a reporting failure can never prevent the fallback from showing. The generic boundary is also reusable/testable without any Sentry dependency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): adjust ErrorBoundary copy * refactor(sentry): split ErrorBoundary into types/styles/constants/utils Follow the component-folder convention: extract ErrorBoundaryProps/State and Palette to .types.ts, the inline CSSProperties to .styles.ts, the palettes + copy to .constants.ts, and getPalette to .utils.ts. The component file is now just the boundary logic + markup. Styles stay plain CSSProperties (not styled-components) because the boundary renders above ThemeProvider; the rationale lives in the constants/styles file headers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(sentry): add ErrorBoundary tests Cover the fallback rendering, custom fallback, onError forwarding, and the robustness guarantee that the fallback still renders when onError throws. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add source-maps decision (version-only release + debug IDs) Record §9: source maps are required for real debugging value; decision is release=pkg.version (no per-build SHA) + debug IDs for bundle/map matching, SHA as metadata only. Captures current build state, implementation outline, and the build-determinism open question. Also mark ErrorBoundary refinement done. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): upload source maps via bundler plugins (RI-8113) Add @sentry/vite-plugin (renderer) and @sentry/webpack-plugin (main), gated on SENTRY_AUTH_TOKEN so they are a no-op for local/dev builds. When the token is present (CI) they generate hidden source maps, inject debug IDs into bundle+map, upload to Sentry, and delete the maps so they never ship in the app. Release name = pkg.version (matches the runtime release); debug IDs handle bundle/map matching, so no per-build SHA is needed. Wire the env vars into all four build pipelines, and add the missing RI_SENTRY_UI_DSN (the renderer Sentry layer reads it but it was never passed). New CI config required (see readiness doc): SENTRY_AUTH_TOKEN (secret), SENTRY_ORG / SENTRY_PROJECT_UI / SENTRY_PROJECT_ELECTRON (vars), and the RI_SENTRY_UI_DSN secret. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): mark source maps implemented; list required CI secrets/vars Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): RI_ prefix source-map env vars + report from web build 1. Prefix the source-map env vars with RI_ for consistency: RI_SENTRY_AUTH_TOKEN / RI_SENTRY_ORG / RI_SENTRY_PROJECT_UI / RI_SENTRY_PROJECT_ELECTRON (vite + webpack configs and all four pipelines). Note: the auth token is build-time only and must never be read via import.meta.env in client code. 2. Initialize Sentry in the web entry (index.tsx) via a new services/sentryWeb.ts using @sentry/react, mirroring the Electron renderer (consent-gated two-tier reporting, shared scrubbing). All three targets now report: Electron main, Electron renderer, and web/docker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): UI config uses RI_SENTRY_UI_DSN, not the electron DSN A PoC commit (link electron sentry project) repointed the UI sentry.dsn at RI_SENTRY_ELECTRON_DSN, so the renderer and web reported to the Electron project and RI_SENTRY_UI_DSN was never read. Point it back at the UI DSN. Also document that this sentry block is runtime-overridable per host via the domainConfig merge in config/index.ts — the channel the cloud deployment will use to point the web build at its own Sentry project (no refactor of the init code needed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add cloud (RedisInsight-Cloud) compatibility note Capture that this PR is forward-compatible with the cloud runtime-config approach (getConfig deep-merges per-host domainConfig; init reads config not env; sync resolution = no re-init), the additive cloud work, and the exact integration point. Records that cloud serves a prebuilt S3 web artifact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): single RI_SENTRY_DSN for all layers Consolidate the split RI_SENTRY_ELECTRON_DSN / RI_SENTRY_UI_DSN into one RI_SENTRY_DSN shared by the Electron main process, renderer, and web — one Sentry project, filtered by platform/environment within it (no second project to maintain). Update the main init, UI config, webpack EnvironmentPlugin, all four pipelines, and the readiness doc. Also drop the redundant source-map comment lines from the workflow env blocks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): single RI_SENTRY_PROJECT for source-map uploads With one Sentry project, the renderer and main source-map uploads target the same project. Collapse RI_SENTRY_PROJECT_UI / RI_SENTRY_PROJECT_ELECTRON into a single RI_SENTRY_PROJECT (vite + webpack configs, all four pipelines, doc). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): default RI_SENTRY_ENVIRONMENT to development in main bundle The webpack EnvironmentPlugin defaulted RI_SENTRY_ENVIRONMENT to 'production' while the runtime code and UI config default to 'development'. Align it to 'development' and fix the stale doc comment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): stop tracking the production-readiness doc Keep the readiness/planning doc local-only (added to .git/info/exclude), like docs/pr-plan-RI-7682-decouple-fe-be.md. It should not land in the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * revert(test): drop jest mapper change; pin @redis-ui to main's 44.0.2 The jest.config.cjs subpath-mapping change wasn't actually needed — it was masking an unintended lockfile drift. package.json specs match main (^44.0.2 etc.), but the lockfile had drifted @redis-ui to 44.1.0/6.11.0/ 15.1.0/3.8.0 (44.1.0 introduced the @redislabsdev/*/subpath imports that broke the old mapper). main pins 44.0.2, where the original mapper works. Pin the four @redis-ui packages back to main's resolved versions (Sentry SDKs untouched at 10.39.0/7.8.0) and revert jest.config.cjs to match main. Tests pass and type-check is clean at 44.0.2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: restore api/yarn.lock to match main The rebase resolved the api/yarn.lock conflict with --theirs (the month-old PoC lockfile) and it was never reconciled, leaving ~1300 lines of stale churn against main even though redisinsight/api/package.json is identical to main. The PoC removed its API Sentry deps, so there is no API dependency change on this branch — restore main's api/yarn.lock (frozen-lockfile consistent with the unchanged package.json). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: rebuild root yarn.lock as main + Sentry subtree only The rebase-era reinstall had drifted hundreds of unrelated transitive deps (babel, jest, inquirer, colordx, @electron/get, rollup, ...) within their caret ranges, bloating the diff vs main. Rebuild from main's lockfile and re-add only the Sentry deps, pinning @sentry/react@10.39.0 and @sentry/electron@7.8.0 (the pair that dedupes @sentry/core — newer 10.57/ 7.13 resolve a duplicate core and fail type-check). @redis-ui stays at main's 44.0.2. Net result: diff vs main is now just the Sentry/OpenTelemetry subtree (+ magic-string 0.30.19->0.30.21, pulled transitively). frozen-lockfile consistent, type-check clean, tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): drop verbose comments Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): make source-map upload failures non-fatal Add an errorHandler to both bundler plugins so a failed upload (Sentry outage, bad token, network blip) warns and continues instead of failing the build — matching the cloud-ui convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): gate source-map generation+upload on RI_SENTRY_ENABLED Previously the upload was gated only on RI_SENTRY_AUTH_TOKEN, so a build with the token but RI_SENTRY_ENABLED=false would still generate and upload maps — and since generation was token-only while the deleting plugin was gated separately, those maps could ship inside the asar. Couple both map generation and upload to (auth token AND RI_SENTRY_ENABLED === 'true') in the vite and webpack configs, so a disabled build produces no maps at all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): read RI_SENTRY_ENABLED from vars, not secrets RI_SENTRY_ENABLED is a non-sensitive flag and is configured as a Repository variable, but the pipelines read it from secrets.* — which silently resolved to empty, disabling Sentry everywhere. Read it from vars.* so the repository variable is actually picked up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): correct error-boundary theme detection + tidy logging - getPalette read lowercase 'theme_light/dark' but themeService writes the Theme enum value uppercase ('theme_LIGHT'/'theme_DARK'), so it never matched and always fell back to OS/dark. Use the Theme enum to build the class name. - Replace console.log with console.warn in the renderer Sentry services and drop the eslint-disable (no console.log in production code); remove the noisy success-init logs. - Fix stale setConsent JSDoc (revoke does not close the client). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style(sentry): prettier-format scrubbing spec Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: refresh desktop tscheck baseline (-6 from vite.main.config fix) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): tag events with app.layer (electron-main/renderer/web) Distinguishes which layer a crash came from — a dimension not otherwise captured (renderer and web are both javascript-platform). OS stays in the auto-captured os context. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): label source-map bundles by build OS via dist Set the upload dist to the build OS (macos/windows/linux from process.platform on each per-OS CI runner) so the per-platform source-map bundles are distinguishable in Sentry's Source Maps view. inject: false keeps dist on the uploaded bundle only (release is already set in Sentry.init) — events are untouched and still match via debug IDs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(build): raise vite build heap to 8192 (source-map OOM on macOS CI) Enabling hidden source maps + the Sentry upload plugin pushed the renderer vite build over the V8 heap limit, OOM-crashing build:renderer (SIGABRT) only on the macOS runner (~7GB RAM vs more on linux/windows). Bump NODE_OPTIONS=--max-old-space-size=8192 on the vite build, matching the repo's existing stats script and the cloud-ui convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): use process.platform directly for source-map dist Drop the cosmetic darwin->macos map; process.platform (darwin/win32/linux) is fine as the dist label. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): remove PoC crash-trigger code Remove the test crash triggers added during the PoC: the Cmd+Shift+K/C global shortcuts and triggerTestCrash/triggerNativeCrash imports in app.ts, the Test Helpers block (triggerTestCrash/triggerNativeCrash) in sentry.ts, and the Crash Handler / Crash React buttons in HelpMenu.tsx. HelpMenu.tsx returns to 0 diff vs main; app.ts keeps only initSentry(). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): wire renderer IPC bridge via @sentry/electron/preload The renderer SDK (@sentry/electron/renderer) relays events to the main process over an IPC bridge that the preload script must establish. Without it the renderer falls back to the unsupported `sentry-ipc://` protocol ("URL scheme not supported" / "failed to establish connection with the Electron main process"), so renderer-side errors (React errors, the error boundary) were never reported — only main-process crashes were. Importing `@sentry/electron/preload` sets up the bridge. The package is a root dependency and not externalized by webpack.config.base, so it is bundled into preload.js (prod webpack and dev vite alike). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): correct web source-map deletion path and release Address two Cursor Bugbot findings on the web/Docker build path: - filesToDeleteAfterUpload was hardcoded to the Electron renderer outDir (`../dist/renderer`), so web builds (outDir `./dist`) uploaded hidden source maps but never deleted them — they could ship in the artifact. Use the build's actual `outDir` glob instead. - sentryWeb.ts set `release` from redisinsight/ui/package.json (2.66.0) while the Vite upload plugin uses defaultConfig.app.version (redisinsight/package.json, 3.6.0). Web events and uploaded maps would disagree on release and fail to symbolicate. Use riConfig.app.version, the same source the upload plugin uses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): gate native crash minidumps on consent; init renderer early Addresses AI review findings on the Sentry PR: - Disable @sentry/electron's default minidump integration. It starts Electron's crashReporter at init and uploads minidumps (process memory) via the SDK transport regardless of analytics consent — bypassing the Tier-1 gate (beforeSend cannot scrub a minidump attachment) and conflicting with our own consent-gated crashReporter.start. Native crashes now flow only through the consent-gated initCrashReporter. - Honour consent revocation/grant at runtime: setConsent now toggles crashReporter.setUploadToServer so minidump uploads stop immediately on revoke and resume on re-grant, instead of only taking effect next launch. - Initialize the renderer Sentry before the windowId IPC round-trip so renderer errors during early startup are captured (matching the web entrypoint). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): remove unused main-process exports captureException, setUser and isSentryInitialized are no longer referenced in production code (the PoC crash triggers that used them were removed). Dropping them keeps the main-process Sentry surface to initSentry/setConsent. Note: anonymity for non-consenting users does not depend on setUser — the Tier-1 path (minimizeEvent) already stamps the shared NON_TRACKING_ANONYMOUS_ID on every no-consent event. This commit is isolated so the sentry-tracking-crash branch can restore the crash-test helpers by reverting just this commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): redact secrets embedded in free-text event fields scrubSensitiveData only redacts by key name, so a secret living inside a string value — most notably a Redis connection URI in a thrown error (redis://user:pass@host) — passed through untouched on the consented path (beforeSend returns scrubEvent's result directly; only Tier 1 minimized it). Add scrubSecretsInText, applied in scrubEvent to the free-text fields: event.message, exception.values[].value, breadcrumbs[].message, request.url/query_string, and stack-frame source context. It redacts URI userinfo credentials and password/token/secret/apiKey assignments. Runs on both tiers (defense-in-depth); best-effort/heuristic, not a guarantee. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): accept RI_SENTRY_ENABLED=1 in main; cover Windows file URLs - Main process now enables Sentry on 'true' OR '1', matching the renderer/web booleanEnv semantics, so the two layers no longer diverge when RI_SENTRY_ENABLED=1 (main was strict-'true' only). - Add normalizePath regression tests for C:/Users/... and file:///C:/Users/... — the /Users/ branch already normalizes these (prefix-agnostic); the tests lock it in against regex regressions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): align source-map gates with runtime; widen free-text token scrubbing - Source-map upload gates (vite + webpack) now accept RI_SENTRY_ENABLED='1' as well as 'true', matching the runtime/booleanEnv semantics so a build configured with '1' still symbolicates (previously: events on, maps off). - Free-text scrubber now redacts Bearer/Basic auth scheme credentials and OAuth-prefixed token names (access_token, refreshToken, ...), which the previous assignment pattern missed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): disable release-health session tracking (consent bypass) Default session integrations emit release-health envelopes on load/navigation that are NOT events, so they bypass the beforeSend consent gate and would send usage telemetry for opted-out users. Disable them in all three layers: - web + Electron renderer: BrowserSession - main: MainProcessSession Also align the Electron renderer's `release` to riConfig.app.version (same source as the web init and the Vite upload plugin) for consistency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): delete hidden source maps even when upload fails filesToDeleteAfterUpload only runs after a successful upload, and the upload errorHandler is intentionally non-fatal — so a failed upload (bad token, 5xx, network) left the emitted .js.map files in the output dir to be packaged. Delete them in the errorHandler too (both vite + webpack) so maps never ship regardless of upload outcome. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): keep debug_meta in Tier-1 so anonymous crashes symbolicate minimizeEvent dropped debug_meta and minimizeFrame dropped abs_path, so no-consent (Tier-1) events could not be matched to uploaded source maps and showed minified frames. Keep debug_meta (code_file + debug_id) and abs_path, normalizing any user path defensively. These carry no PII — build paths + debug-id UUIDs — so anonymous stacks become readable without weakening the Tier-1 redaction. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): scrub secrets in structured-bag string values; normalize request url scrubSensitiveData only redacts by key name, so a secret inside a string value (e.g. a token/credential in a breadcrumb's data.url) passed through on the consented path. Add scrubSecretsDeep, applied to extra/contexts/request/ breadcrumb data, so string values are also free-text scrubbed. Also run normalizePath over request.url to strip an OS account name from a Windows file:// page URL (matching how stack-frame paths are handled). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): skip native crashReporter in Mac App Store builds Electron's crashReporter is unsupported under the MAS App Sandbox. Guard process.mas in initCrashReporter so MAS builds skip native minidumps while JS-level Sentry reporting continues to work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): error boundary message wording * refactor(sentry): trim comments; guard renderer/web init with try/catch Addresses review feedback on the PR: - Trim comments across the Sentry modules to short why-only notes (per the comments rule in #6091); drop how/story comments and stale doc references. - Wrap Sentry.init in try/catch in the web and Electron-renderer initSentry (matching the main process) so a Sentry init failure can't break app startup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> | 3 个月前 | |
fix(scripts): fail the TS baseline check when tsc crashes (#6434) * fix(scripts): fail loudly when tsc dies instead of reporting zero errors The baseline check read tsc's diagnostics off a pipe, so tsc's exit status was discarded. An out-of-memory abort produces exit 134 and no stdout, which the parser turns into an empty error list, so the check concluded every recorded error had been fixed and told the developer to refresh the baseline. Doing that wrote an empty baseline and dropped 1287 recorded UI errors, exiting 0. `tscheck:force` skipped the comparison entirely and wrote the empty baseline outright. Run tsc from the script instead of piping into it, and treat any exit status other than 0, 1 or 2 (or death by signal) as a run that never reported its diagnostics. All three commands now refuse to touch the baseline and say that tsc ran out of memory. * fix(scripts): reject compiler crashes that exit 1 with no diagnostics A tsc run killed by an uncaught exception exits 1 with an empty stdout and a stack trace on stderr. Exit 1 is also a legitimate diagnostics status, so the status check let those runs through, the parser turned the empty output into zero errors, and both `tscheck` and `tscheck:force` went on to replace the baseline with an empty snapshot. Require a non-zero exit to carry at least one parsed diagnostic. A compiler that failed but reported nothing says nothing about the real error count, whatever its status. * refactor(scripts): move tsc crash detection into a separate wrapper Revert ts-error-check.ts to its original form and move the crash-detection logic (spawn tsc, check exit status/signal, refuse to touch the baseline on a crash) into a new tsc-safe-run.ts, which forwards parsed diagnostics to the unmodified ts-error-check.ts over stdin, same as the old shell pipe did. Keeps ts-error-check.ts byte-identical to main so it stays easy to sync with the copy of this script kept in another repo. * fix(scripts): resolve tsx as a JS entry instead of a bare spawn spawnSync('tsx', ...) relies on PATH/shell lookup, which fails on Windows where npm exposes tsx through a .cmd shim. Resolve tsx/cli the same way tsc is already resolved here and launch it via process.execPath, so no shell is involved. | 29 天前 | |
Wire @rdi-ui/pipeline into the standalone v2 pipeline management page (#6506) * feat(rdi): wire @rdi-ui/pipeline into the standalone v2 page | 1 天前 | |
#CR-10 - Move to vite | 2 年前 | |
RI-8113 Add Sentry error tracking for the Electron app (#6073) * RI-7977: setup Sentry projects * remove sentry from api * update sentry env variables * link electron sentry project * remove docs * chore: reconcile yarn.lock after rebase onto main Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): report main-process test crash via captureException A synchronous throw inside the globalShortcut callback is swallowed by Electron's native dispatch and never reaches Sentry's uncaughtException handler, so Cmd/Ctrl+Shift+K reported nothing. Capture the error explicitly instead, matching the shortcut's non-crashing intent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add RI-8113 production-readiness design Two-tier consent model inside Sentry (anonymous Tier 1 / consented Tier 2), scrubbing security-review checklist, early-crash + consent-caching handling, and mapping to the RI-8113 scope. Intended for Legal/Security review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): record Legal/Security decisions in readiness doc Tier-1 allowlist accepted; IP not tracked; region confirmation pending (EU recommended); retention defaulted to 90 days. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): record Sentry org region as US (residency decision pending) Org is US-region (per DSN host). Region is fixed at org creation, so Legal should confirm US is acceptable for anonymized crash data, or an EU org must be created before launch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): confirm US region approved (residency resolved) Sentry is an org-approved vendor; all data stored in US. No EU org needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): two-tier consent gating + shared scrubbing (RI-8113) Implements the consent model from docs/sentry-production-readiness.md: - Shared, SDK-agnostic scrubbing module (ui/src/services/sentry) used by both the main process and renderer so the layers cannot drift: scrubEvent (redacts extra/contexts/request/breadcrumbs, normalizes stack-frame paths, strips server_name/IP) + minimizeEvent (Tier 1 anonymous allowlist under a shared sentinel id). - Main process: default-deny consent flag cached in electron-store for a synchronous boot read; beforeSend sends full (scrubbed) events with consent and minimized anonymous events without; breadcrumbs gated on consent; native crashReporter upload gated behind consent (start-once); setConsent() updates the tier at runtime. - Renderer: beforeSend self-gates per-event via checkIsAnalyticsGranted. - Consent plumbing: useSentryConsentSync pushes agreements.analytics to the main process over IPC (setSentryConsent) on startup and on toggle. - Unit tests for the scrubbing module. Note: autoSessionTracking/serverName options dropped where the Sentry v10 type no longer accepts them (renderer); server_name is cleared per-event. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(desktop): type-correct rollup external in vite.main.config The PoC added a predicate function into rollup's `external` array, which only accepts string | RegExp — a TS2769 overload error surfaced after the rebase pulled in newer vite/rollup types. Fold the module list, regex patterns, and the api-dist predicate into a single `external` function (behavior-preserving). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(test): preserve subpaths in @redislabsdev->@redis-ui jest mapping @redis-ui/components v44 (pulled in by the rebase) imports icons via subpaths, e.g. @redislabsdev/redis-ui-icons/multicolor. The moduleNameMapper matched only the bare package name and dropped the subpath, so the mapped module resolved to the package root and LoaderLargeIcon was undefined — styled(undefined) crashed every UI test suite at setup. Anchor each mapping and capture the optional subpath ($1) so deep imports survive the rename. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): redesign ErrorBoundary fallback (RI-8113) Make the title the visual focal point (was small/faded vs the button), mute the supporting copy, and replace the alarming brand-red action with the calm primary blue (recovery, not crash). Colors are theme-aware via the persisted document.body theme class and mirror redis-ui neutral/ primary tokens — inlined because the boundary renders above ThemeProvider, where useTheme()/themed CSS variables are unavailable when a crash is caught. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): adjust ErrorBOundary copy * refactor(sentry): split reporting from the error boundary Separate the generic ErrorBoundary (catch + fallback UI + onError callback) from SentryErrorBoundary (a thin wrapper that injects Sentry reporting). Fixes a real robustness gap: the Sentry capture call previously ran unguarded in componentDidCatch, so if it threw, the error escaped the boundary and the fallback UI never rendered. ErrorBoundary now invokes onError inside try/catch, so a reporting failure can never prevent the fallback from showing. The generic boundary is also reusable/testable without any Sentry dependency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): adjust ErrorBoundary copy * refactor(sentry): split ErrorBoundary into types/styles/constants/utils Follow the component-folder convention: extract ErrorBoundaryProps/State and Palette to .types.ts, the inline CSSProperties to .styles.ts, the palettes + copy to .constants.ts, and getPalette to .utils.ts. The component file is now just the boundary logic + markup. Styles stay plain CSSProperties (not styled-components) because the boundary renders above ThemeProvider; the rationale lives in the constants/styles file headers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(sentry): add ErrorBoundary tests Cover the fallback rendering, custom fallback, onError forwarding, and the robustness guarantee that the fallback still renders when onError throws. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add source-maps decision (version-only release + debug IDs) Record §9: source maps are required for real debugging value; decision is release=pkg.version (no per-build SHA) + debug IDs for bundle/map matching, SHA as metadata only. Captures current build state, implementation outline, and the build-determinism open question. Also mark ErrorBoundary refinement done. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): upload source maps via bundler plugins (RI-8113) Add @sentry/vite-plugin (renderer) and @sentry/webpack-plugin (main), gated on SENTRY_AUTH_TOKEN so they are a no-op for local/dev builds. When the token is present (CI) they generate hidden source maps, inject debug IDs into bundle+map, upload to Sentry, and delete the maps so they never ship in the app. Release name = pkg.version (matches the runtime release); debug IDs handle bundle/map matching, so no per-build SHA is needed. Wire the env vars into all four build pipelines, and add the missing RI_SENTRY_UI_DSN (the renderer Sentry layer reads it but it was never passed). New CI config required (see readiness doc): SENTRY_AUTH_TOKEN (secret), SENTRY_ORG / SENTRY_PROJECT_UI / SENTRY_PROJECT_ELECTRON (vars), and the RI_SENTRY_UI_DSN secret. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): mark source maps implemented; list required CI secrets/vars Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): RI_ prefix source-map env vars + report from web build 1. Prefix the source-map env vars with RI_ for consistency: RI_SENTRY_AUTH_TOKEN / RI_SENTRY_ORG / RI_SENTRY_PROJECT_UI / RI_SENTRY_PROJECT_ELECTRON (vite + webpack configs and all four pipelines). Note: the auth token is build-time only and must never be read via import.meta.env in client code. 2. Initialize Sentry in the web entry (index.tsx) via a new services/sentryWeb.ts using @sentry/react, mirroring the Electron renderer (consent-gated two-tier reporting, shared scrubbing). All three targets now report: Electron main, Electron renderer, and web/docker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): UI config uses RI_SENTRY_UI_DSN, not the electron DSN A PoC commit (link electron sentry project) repointed the UI sentry.dsn at RI_SENTRY_ELECTRON_DSN, so the renderer and web reported to the Electron project and RI_SENTRY_UI_DSN was never read. Point it back at the UI DSN. Also document that this sentry block is runtime-overridable per host via the domainConfig merge in config/index.ts — the channel the cloud deployment will use to point the web build at its own Sentry project (no refactor of the init code needed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sentry): add cloud (RedisInsight-Cloud) compatibility note Capture that this PR is forward-compatible with the cloud runtime-config approach (getConfig deep-merges per-host domainConfig; init reads config not env; sync resolution = no re-init), the additive cloud work, and the exact integration point. Records that cloud serves a prebuilt S3 web artifact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): single RI_SENTRY_DSN for all layers Consolidate the split RI_SENTRY_ELECTRON_DSN / RI_SENTRY_UI_DSN into one RI_SENTRY_DSN shared by the Electron main process, renderer, and web — one Sentry project, filtered by platform/environment within it (no second project to maintain). Update the main init, UI config, webpack EnvironmentPlugin, all four pipelines, and the readiness doc. Also drop the redundant source-map comment lines from the workflow env blocks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): single RI_SENTRY_PROJECT for source-map uploads With one Sentry project, the renderer and main source-map uploads target the same project. Collapse RI_SENTRY_PROJECT_UI / RI_SENTRY_PROJECT_ELECTRON into a single RI_SENTRY_PROJECT (vite + webpack configs, all four pipelines, doc). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): default RI_SENTRY_ENVIRONMENT to development in main bundle The webpack EnvironmentPlugin defaulted RI_SENTRY_ENVIRONMENT to 'production' while the runtime code and UI config default to 'development'. Align it to 'development' and fix the stale doc comment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): stop tracking the production-readiness doc Keep the readiness/planning doc local-only (added to .git/info/exclude), like docs/pr-plan-RI-7682-decouple-fe-be.md. It should not land in the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * revert(test): drop jest mapper change; pin @redis-ui to main's 44.0.2 The jest.config.cjs subpath-mapping change wasn't actually needed — it was masking an unintended lockfile drift. package.json specs match main (^44.0.2 etc.), but the lockfile had drifted @redis-ui to 44.1.0/6.11.0/ 15.1.0/3.8.0 (44.1.0 introduced the @redislabsdev/*/subpath imports that broke the old mapper). main pins 44.0.2, where the original mapper works. Pin the four @redis-ui packages back to main's resolved versions (Sentry SDKs untouched at 10.39.0/7.8.0) and revert jest.config.cjs to match main. Tests pass and type-check is clean at 44.0.2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: restore api/yarn.lock to match main The rebase resolved the api/yarn.lock conflict with --theirs (the month-old PoC lockfile) and it was never reconciled, leaving ~1300 lines of stale churn against main even though redisinsight/api/package.json is identical to main. The PoC removed its API Sentry deps, so there is no API dependency change on this branch — restore main's api/yarn.lock (frozen-lockfile consistent with the unchanged package.json). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: rebuild root yarn.lock as main + Sentry subtree only The rebase-era reinstall had drifted hundreds of unrelated transitive deps (babel, jest, inquirer, colordx, @electron/get, rollup, ...) within their caret ranges, bloating the diff vs main. Rebuild from main's lockfile and re-add only the Sentry deps, pinning @sentry/react@10.39.0 and @sentry/electron@7.8.0 (the pair that dedupes @sentry/core — newer 10.57/ 7.13 resolve a duplicate core and fail type-check). @redis-ui stays at main's 44.0.2. Net result: diff vs main is now just the Sentry/OpenTelemetry subtree (+ magic-string 0.30.19->0.30.21, pulled transitively). frozen-lockfile consistent, type-check clean, tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): drop verbose comments Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): make source-map upload failures non-fatal Add an errorHandler to both bundler plugins so a failed upload (Sentry outage, bad token, network blip) warns and continues instead of failing the build — matching the cloud-ui convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): gate source-map generation+upload on RI_SENTRY_ENABLED Previously the upload was gated only on RI_SENTRY_AUTH_TOKEN, so a build with the token but RI_SENTRY_ENABLED=false would still generate and upload maps — and since generation was token-only while the deleting plugin was gated separately, those maps could ship inside the asar. Couple both map generation and upload to (auth token AND RI_SENTRY_ENABLED === 'true') in the vite and webpack configs, so a disabled build produces no maps at all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): read RI_SENTRY_ENABLED from vars, not secrets RI_SENTRY_ENABLED is a non-sensitive flag and is configured as a Repository variable, but the pipelines read it from secrets.* — which silently resolved to empty, disabling Sentry everywhere. Read it from vars.* so the repository variable is actually picked up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): correct error-boundary theme detection + tidy logging - getPalette read lowercase 'theme_light/dark' but themeService writes the Theme enum value uppercase ('theme_LIGHT'/'theme_DARK'), so it never matched and always fell back to OS/dark. Use the Theme enum to build the class name. - Replace console.log with console.warn in the renderer Sentry services and drop the eslint-disable (no console.log in production code); remove the noisy success-init logs. - Fix stale setConsent JSDoc (revoke does not close the client). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style(sentry): prettier-format scrubbing spec Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: refresh desktop tscheck baseline (-6 from vite.main.config fix) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): tag events with app.layer (electron-main/renderer/web) Distinguishes which layer a crash came from — a dimension not otherwise captured (renderer and web are both javascript-platform). OS stays in the auto-captured os context. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sentry): label source-map bundles by build OS via dist Set the upload dist to the build OS (macos/windows/linux from process.platform on each per-OS CI runner) so the per-platform source-map bundles are distinguishable in Sentry's Source Maps view. inject: false keeps dist on the uploaded bundle only (release is already set in Sentry.init) — events are untouched and still match via debug IDs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(build): raise vite build heap to 8192 (source-map OOM on macOS CI) Enabling hidden source maps + the Sentry upload plugin pushed the renderer vite build over the V8 heap limit, OOM-crashing build:renderer (SIGABRT) only on the macOS runner (~7GB RAM vs more on linux/windows). Bump NODE_OPTIONS=--max-old-space-size=8192 on the vite build, matching the repo's existing stats script and the cloud-ui convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): use process.platform directly for source-map dist Drop the cosmetic darwin->macos map; process.platform (darwin/win32/linux) is fine as the dist label. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(sentry): remove PoC crash-trigger code Remove the test crash triggers added during the PoC: the Cmd+Shift+K/C global shortcuts and triggerTestCrash/triggerNativeCrash imports in app.ts, the Test Helpers block (triggerTestCrash/triggerNativeCrash) in sentry.ts, and the Crash Handler / Crash React buttons in HelpMenu.tsx. HelpMenu.tsx returns to 0 diff vs main; app.ts keeps only initSentry(). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): wire renderer IPC bridge via @sentry/electron/preload The renderer SDK (@sentry/electron/renderer) relays events to the main process over an IPC bridge that the preload script must establish. Without it the renderer falls back to the unsupported `sentry-ipc://` protocol ("URL scheme not supported" / "failed to establish connection with the Electron main process"), so renderer-side errors (React errors, the error boundary) were never reported — only main-process crashes were. Importing `@sentry/electron/preload` sets up the bridge. The package is a root dependency and not externalized by webpack.config.base, so it is bundled into preload.js (prod webpack and dev vite alike). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): correct web source-map deletion path and release Address two Cursor Bugbot findings on the web/Docker build path: - filesToDeleteAfterUpload was hardcoded to the Electron renderer outDir (`../dist/renderer`), so web builds (outDir `./dist`) uploaded hidden source maps but never deleted them — they could ship in the artifact. Use the build's actual `outDir` glob instead. - sentryWeb.ts set `release` from redisinsight/ui/package.json (2.66.0) while the Vite upload plugin uses defaultConfig.app.version (redisinsight/package.json, 3.6.0). Web events and uploaded maps would disagree on release and fail to symbolicate. Use riConfig.app.version, the same source the upload plugin uses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): gate native crash minidumps on consent; init renderer early Addresses AI review findings on the Sentry PR: - Disable @sentry/electron's default minidump integration. It starts Electron's crashReporter at init and uploads minidumps (process memory) via the SDK transport regardless of analytics consent — bypassing the Tier-1 gate (beforeSend cannot scrub a minidump attachment) and conflicting with our own consent-gated crashReporter.start. Native crashes now flow only through the consent-gated initCrashReporter. - Honour consent revocation/grant at runtime: setConsent now toggles crashReporter.setUploadToServer so minidump uploads stop immediately on revoke and resume on re-grant, instead of only taking effect next launch. - Initialize the renderer Sentry before the windowId IPC round-trip so renderer errors during early startup are captured (matching the web entrypoint). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(sentry): remove unused main-process exports captureException, setUser and isSentryInitialized are no longer referenced in production code (the PoC crash triggers that used them were removed). Dropping them keeps the main-process Sentry surface to initSentry/setConsent. Note: anonymity for non-consenting users does not depend on setUser — the Tier-1 path (minimizeEvent) already stamps the shared NON_TRACKING_ANONYMOUS_ID on every no-consent event. This commit is isolated so the sentry-tracking-crash branch can restore the crash-test helpers by reverting just this commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): redact secrets embedded in free-text event fields scrubSensitiveData only redacts by key name, so a secret living inside a string value — most notably a Redis connection URI in a thrown error (redis://user:pass@host) — passed through untouched on the consented path (beforeSend returns scrubEvent's result directly; only Tier 1 minimized it). Add scrubSecretsInText, applied in scrubEvent to the free-text fields: event.message, exception.values[].value, breadcrumbs[].message, request.url/query_string, and stack-frame source context. It redacts URI userinfo credentials and password/token/secret/apiKey assignments. Runs on both tiers (defense-in-depth); best-effort/heuristic, not a guarantee. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): accept RI_SENTRY_ENABLED=1 in main; cover Windows file URLs - Main process now enables Sentry on 'true' OR '1', matching the renderer/web booleanEnv semantics, so the two layers no longer diverge when RI_SENTRY_ENABLED=1 (main was strict-'true' only). - Add normalizePath regression tests for C:/Users/... and file:///C:/Users/... — the /Users/ branch already normalizes these (prefix-agnostic); the tests lock it in against regex regressions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): align source-map gates with runtime; widen free-text token scrubbing - Source-map upload gates (vite + webpack) now accept RI_SENTRY_ENABLED='1' as well as 'true', matching the runtime/booleanEnv semantics so a build configured with '1' still symbolicates (previously: events on, maps off). - Free-text scrubber now redacts Bearer/Basic auth scheme credentials and OAuth-prefixed token names (access_token, refreshToken, ...), which the previous assignment pattern missed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): disable release-health session tracking (consent bypass) Default session integrations emit release-health envelopes on load/navigation that are NOT events, so they bypass the beforeSend consent gate and would send usage telemetry for opted-out users. Disable them in all three layers: - web + Electron renderer: BrowserSession - main: MainProcessSession Also align the Electron renderer's `release` to riConfig.app.version (same source as the web init and the Vite upload plugin) for consistency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): delete hidden source maps even when upload fails filesToDeleteAfterUpload only runs after a successful upload, and the upload errorHandler is intentionally non-fatal — so a failed upload (bad token, 5xx, network) left the emitted .js.map files in the output dir to be packaged. Delete them in the errorHandler too (both vite + webpack) so maps never ship regardless of upload outcome. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): keep debug_meta in Tier-1 so anonymous crashes symbolicate minimizeEvent dropped debug_meta and minimizeFrame dropped abs_path, so no-consent (Tier-1) events could not be matched to uploaded source maps and showed minified frames. Keep debug_meta (code_file + debug_id) and abs_path, normalizing any user path defensively. These carry no PII — build paths + debug-id UUIDs — so anonymous stacks become readable without weakening the Tier-1 redaction. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): scrub secrets in structured-bag string values; normalize request url scrubSensitiveData only redacts by key name, so a secret inside a string value (e.g. a token/credential in a breadcrumb's data.url) passed through on the consented path. Add scrubSecretsDeep, applied to extra/contexts/request/ breadcrumb data, so string values are also free-text scrubbed. Also run normalizePath over request.url to strip an OS account name from a Windows file:// page URL (matching how stack-frame paths are handled). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): skip native crashReporter in Mac App Store builds Electron's crashReporter is unsupported under the MAS App Sandbox. Guard process.mas in initCrashReporter so MAS builds skip native minidumps while JS-level Sentry reporting continues to work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sentry): error boundary message wording * refactor(sentry): trim comments; guard renderer/web init with try/catch Addresses review feedback on the PR: - Trim comments across the Sentry modules to short why-only notes (per the comments rule in #6091); drop how/story comments and stale doc references. - Wrap Sentry.init in try/catch in the web and Electron-renderer initSentry (matching the main process) so a Sentry init failure can't break app startup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> | 3 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 年前 | ||
| 1 天前 | ||
| 1 年前 | ||
| 4 年前 | ||
| 14 天前 | ||
| 4 年前 | ||
| 11 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 29 天前 | ||
| 1 天前 | ||
| 2 年前 | ||
| 3 个月前 |