@mushi-mushi/core 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -19,6 +19,8 @@ interface MushiConfig {
19
19
  integrations?: MushiIntegrationsConfig;
20
20
  offline?: MushiOfflineConfig;
21
21
  rewards?: MushiRewardsConfig;
22
+ /** Page-aware, user-data-aware in-SDK assistant (P5). */
23
+ assistant?: MushiAssistantConfig;
22
24
  debug?: boolean;
23
25
  enabled?: boolean;
24
26
  /** Hook called before a report is sent. Return null to cancel, or return the (possibly modified) report. */
@@ -1024,6 +1026,56 @@ interface MushiDiagnosticsResult {
1024
1026
  */
1025
1027
  bannerRendered: boolean;
1026
1028
  }
1029
+ /**
1030
+ * Page-aware assistant (P5). The host app publishes a structured snapshot of
1031
+ * what the current screen is showing; the SDK forwards only this narrow,
1032
+ * server-validated subset so a malicious page cannot inject arbitrary prompt
1033
+ * fields. Ported from the admin console's page-context registry.
1034
+ */
1035
+ interface MushiPageContext {
1036
+ /** Current route/path (e.g. '/activity'). */
1037
+ route: string;
1038
+ /** Human title of the screen. */
1039
+ title?: string;
1040
+ /** One-line description of what is on screen. */
1041
+ summary?: string;
1042
+ /** Active filters as a flat key/value map. */
1043
+ filters?: Record<string, string | number | boolean | null | undefined>;
1044
+ /** The focused/selected entity, if any. */
1045
+ selection?: {
1046
+ kind: string;
1047
+ id: string;
1048
+ label?: string;
1049
+ } | null;
1050
+ }
1051
+ interface MushiAssistantConfig {
1052
+ /**
1053
+ * Enable the in-SDK assistant tab. Defaults to false; the console can also
1054
+ * enable it via runtime config so it can be turned on without a rebuild.
1055
+ */
1056
+ enabled?: boolean;
1057
+ /** Tab label (default: "Ask"). */
1058
+ label?: string;
1059
+ /** Greeting shown on an empty assistant thread. */
1060
+ greeting?: string;
1061
+ /** Suggested starter questions shown on an empty thread. */
1062
+ suggestions?: string[];
1063
+ }
1064
+ interface MushiAssistantStep {
1065
+ label: string;
1066
+ detail?: string;
1067
+ }
1068
+ interface MushiAssistantReply {
1069
+ /** 'answer' resolves the turn; 'clarify' asks the user a follow-up. */
1070
+ kind: 'answer' | 'clarify';
1071
+ text?: string;
1072
+ steps?: MushiAssistantStep[];
1073
+ /** For 'clarify' replies. */
1074
+ question?: string;
1075
+ options?: string[];
1076
+ /** Server-assigned thread id so the next turn continues the conversation. */
1077
+ threadId?: string;
1078
+ }
1027
1079
  interface MushiSDKInstance {
1028
1080
  /**
1029
1081
  * Open the reporter widget. With no options, opens to the category
@@ -1100,6 +1152,26 @@ interface MushiSDKInstance {
1100
1152
  name?: string;
1101
1153
  [k: string]: unknown;
1102
1154
  }): void;
1155
+ /**
1156
+ * Identify the current end user with a signed Mushi identity JWT minted by
1157
+ * the host app's server (see `@mushi-mushi/node`'s `mintMushiIdentityToken`).
1158
+ * This is the trust anchor for secure "My Reports", rewards-to-membership
1159
+ * grants, and the per-user assistant data index — the backend re-verifies
1160
+ * the signature against the project's identity secret. Unlike `identify()`,
1161
+ * which is spoofable, claims from this token are trusted server-side.
1162
+ *
1163
+ * Pass `null` (or call on logout) to clear the identity and fall back to the
1164
+ * anonymous reporter token.
1165
+ */
1166
+ identifyWithToken(token: string | null): void;
1167
+ /**
1168
+ * Publish the current screen's context so the in-SDK assistant can answer
1169
+ * page-aware questions. Call on every route/selection change. Pass `null`
1170
+ * to clear (e.g. on unmount). Only a narrow subset is sent to the server.
1171
+ */
1172
+ publishPageContext(context: MushiPageContext | null): void;
1173
+ /** Open the widget on the assistant ("Ask") tab. */
1174
+ openAssistant(): void;
1103
1175
  /**
1104
1176
  * Sentry-grade observability surface (2026-05-07).
1105
1177
  *
@@ -1227,6 +1299,16 @@ interface MushiApiClient {
1227
1299
  status: MushiReportStatus;
1228
1300
  }>>;
1229
1301
  getSdkConfig(): Promise<MushiApiResponse<MushiRuntimeSdkConfig>>;
1302
+ /**
1303
+ * Send one assistant turn. Forwards the page context + thread id; the
1304
+ * end-user identity token (when set) travels on the X-Mushi-User-Token
1305
+ * header so the backend can scope retrieval to the verified user.
1306
+ */
1307
+ askAssistant(input: {
1308
+ message: string;
1309
+ threadId?: string | null;
1310
+ context?: MushiPageContext | null;
1311
+ }): Promise<MushiApiResponse<MushiAssistantReply>>;
1230
1312
  getLatestSdkVersion(packageName: string): Promise<MushiApiResponse<MushiSdkVersionInfo>>;
1231
1313
  /**
1232
1314
  * Mushi v2.1: ship a single passive-discovery observation (route +
@@ -1347,11 +1429,9 @@ interface MushiApiClient {
1347
1429
  /** Whether the caller has a mushi_testers row. */
1348
1430
  getTesterStatus(jwt: string): Promise<MushiApiResponse<{
1349
1431
  is_tester: boolean;
1350
- tester: {
1351
- id: string;
1352
- public_handle: string | null;
1353
- display_name: string | null;
1354
- } | null;
1432
+ tester_id: string | null;
1433
+ public_handle: string | null;
1434
+ display_name: string | null;
1355
1435
  }>>;
1356
1436
  }
1357
1437
  /**
@@ -1471,6 +1551,12 @@ interface ApiClientOptions {
1471
1551
  apiEndpoint?: string;
1472
1552
  timeout?: number;
1473
1553
  maxRetries?: number;
1554
+ /**
1555
+ * Returns the current signed end-user identity JWT, or null when the user is
1556
+ * anonymous. When present it is sent on the X-Mushi-User-Token header so the
1557
+ * backend can verify it and scope identity-bound features to that user.
1558
+ */
1559
+ getUserToken?: () => string | null | undefined;
1474
1560
  }
1475
1561
  declare const DEFAULT_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
1476
1562
  declare const MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
@@ -1510,6 +1596,43 @@ declare function resolveRegionEndpoint(opts: {
1510
1596
  fetcher?: typeof fetch;
1511
1597
  }): Promise<string>;
1512
1598
 
1599
+ /**
1600
+ * FILE: packages/core/src/env-config.ts
1601
+ * PURPOSE: Reads Mushi credentials from the environment when they are not
1602
+ * supplied explicitly. This lets every framework init work with
1603
+ * zero-config — just run `npx mushi-mushi` once, add the .env.local
1604
+ * line, and the provider/plugin/init works without props.
1605
+ *
1606
+ * Resolution order per field:
1607
+ * 1. Vite / SvelteKit / Angular (VITE_MUSHI_*)
1608
+ * 2. Next.js (NEXT_PUBLIC_MUSHI_*)
1609
+ * 3. Nuxt (NUXT_PUBLIC_MUSHI_*)
1610
+ * 4. Expo managed workflow (EXPO_PUBLIC_MUSHI_*)
1611
+ * 5. Node / React Native / server (bare MUSHI_*)
1612
+ *
1613
+ * Vite statically replaces `import.meta.env.VITE_*` at build time, so the
1614
+ * literal key references here are intentional — they make bundlers replace
1615
+ * the values even when accessed indirectly.
1616
+ *
1617
+ * Next.js and Expo, by contrast, do STATIC string replacement that only
1618
+ * inlines literal `process.env.NEXT_PUBLIC_*` / `process.env.EXPO_PUBLIC_*`
1619
+ * dot-notation references. A dynamic `process.env[key]` lookup is explicitly
1620
+ * unsupported and silently resolves to `undefined` in production client
1621
+ * bundles (Expo even ships an ESLint rule against it). Every `process.env`
1622
+ * read below is therefore a literal, statically-analyzable reference.
1623
+ */
1624
+ interface ResolvedEnvConfig {
1625
+ projectId?: string;
1626
+ apiKey?: string;
1627
+ apiEndpoint?: string;
1628
+ }
1629
+ /**
1630
+ * Reads Mushi projectId, apiKey, and (optionally) apiEndpoint from
1631
+ * the environment. Returns only the fields that are present so callers
1632
+ * can cleanly spread: `{ ...resolveEnvConfig(), ...explicitConfig }`.
1633
+ */
1634
+ declare function resolveEnvConfig(): ResolvedEnvConfig;
1635
+
1513
1636
  interface PreFilterResult {
1514
1637
  passed: boolean;
1515
1638
  reason?: string;
@@ -1802,4 +1925,208 @@ declare function createLogger(options: LoggerOptions): Logger;
1802
1925
  */
1803
1926
  declare const noopLogger: Logger;
1804
1927
 
1805
- export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiBannerConfig, type MushiBannerLink, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiCrossAppReport, type MushiCustomCategory, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiHallOfFameEntry, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiLeaderboardEntry, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, type MushiPerformanceMetrics, type MushiPreFilterConfig, type MushiPreset, type MushiPrivacyConfig, type MushiProactiveConfig, type MushiRegion, type MushiReport, type MushiReportBuilder, type MushiReportCategory, type MushiReportStatus, type MushiReporterComment, type MushiReporterReport, type MushiReputationResult, type MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiSentryContext, type MushiTesterReputation, type MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiTracePropagationConfig, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PayloadGuardResult, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, newUuid, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
1928
+ /**
1929
+ * FILE: packages/core/src/design-tokens.ts
1930
+ * PURPOSE: The single cross-platform source of truth for Mushi widget design.
1931
+ *
1932
+ * Every platform SDK (web Shadow-DOM CSS, React Native components, Capacitor
1933
+ * native sheet) reads its colours, spacing, radii, typography, z-index scale,
1934
+ * motion, launcher geometry, and default copy from this module so the widget
1935
+ * renders coherently everywhere. `@mushi-mushi/core` is already a dependency
1936
+ * of every platform package, which is why the tokens live here rather than in
1937
+ * a separate published package — one import path, no extra build wiring.
1938
+ *
1939
+ * Colour values mirror the historical washi/vermillion palette previously
1940
+ * inlined in `@mushi-mushi/web`'s styles.ts so the visual identity is
1941
+ * unchanged; the difference is that RN and Capacitor now share the exact
1942
+ * same constants instead of re-deriving their own.
1943
+ */
1944
+ type MushiThemeMode = 'light' | 'dark';
1945
+ interface MushiColorPalette {
1946
+ /** washi cream / dark wash — primary surface */
1947
+ paper: string;
1948
+ /** elevated surface (panels, sheets) */
1949
+ paperRaised: string;
1950
+ /** sumi black / cream type — primary text */
1951
+ ink: string;
1952
+ /** captions, descriptions */
1953
+ inkMuted: string;
1954
+ /** disabled, separators */
1955
+ inkFaint: string;
1956
+ /** dim text */
1957
+ inkDim: string;
1958
+ /** hairline rules */
1959
+ rule: string;
1960
+ /** stronger rules / borders */
1961
+ ruleStrong: string;
1962
+ /** 朱 hanko red — signature accent */
1963
+ accent: string;
1964
+ /** ~8–12% accent wash */
1965
+ accentWash: string;
1966
+ /** accessible text on the accent wash */
1967
+ accentInk: string;
1968
+ /** success */
1969
+ ok: string;
1970
+ /** danger / destructive */
1971
+ danger: string;
1972
+ }
1973
+ /** Light-mode palette. */
1974
+ declare const MUSHI_COLORS_LIGHT: MushiColorPalette;
1975
+ /** Dark-mode palette. */
1976
+ declare const MUSHI_COLORS_DARK: MushiColorPalette;
1977
+ /** Resolve the palette for a theme mode. */
1978
+ declare function mushiPalette(mode: MushiThemeMode): MushiColorPalette;
1979
+ /**
1980
+ * Spacing scale (px). Named by feel, matching the editorial design system so
1981
+ * the same rhythm reads across web and native.
1982
+ */
1983
+ declare const MUSHI_SPACING: {
1984
+ readonly hairline: 2;
1985
+ readonly tight: 4;
1986
+ readonly snug: 8;
1987
+ readonly comfy: 12;
1988
+ readonly roomy: 16;
1989
+ readonly lounge: 20;
1990
+ readonly open: 24;
1991
+ readonly wide: 32;
1992
+ };
1993
+ /** Corner radii (px). Tables/strips stay sharp; controls get soft corners. */
1994
+ declare const MUSHI_RADIUS: {
1995
+ readonly none: 0;
1996
+ readonly control: 4;
1997
+ readonly card: 10;
1998
+ readonly sheet: 16;
1999
+ readonly pill: 999;
2000
+ };
2001
+ /** Typography stacks shared by every platform (system-first, no web-font fetch). */
2002
+ declare const MUSHI_TYPE: {
2003
+ readonly fontDisplay: "'Iowan Old Style', 'Palatino Linotype', 'Palatino', 'Book Antiqua', 'Cambria', Georgia, 'Times New Roman', serif";
2004
+ readonly fontBody: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI Variable Display', 'Segoe UI', sans-serif";
2005
+ readonly fontMono: "ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, 'Liberation Mono', monospace";
2006
+ readonly sizeBody: 14;
2007
+ readonly sizeLabel: 12;
2008
+ readonly sizeTitle: 16;
2009
+ readonly lineBody: 1.55;
2010
+ };
2011
+ /**
2012
+ * z-index scale. The host element is pass-through; only interactive surfaces
2013
+ * opt back into pointer events. Banner sits just under the FAB; panel above.
2014
+ */
2015
+ declare const MUSHI_Z: {
2016
+ readonly base: 99999;
2017
+ /** banner sits one below the trigger so an open FAB overlaps it */
2018
+ readonly banner: 99998;
2019
+ /** report/assistant panel sits one above the trigger */
2020
+ readonly panel: 100000;
2021
+ /** element-selector overlay — always on top */
2022
+ readonly overlay: 2147483647;
2023
+ };
2024
+ /** Motion signature — a soft back-out "stamp press". */
2025
+ declare const MUSHI_MOTION: {
2026
+ readonly easeStamp: "cubic-bezier(0.22, 1, 0.36, 1)";
2027
+ readonly durationFast: 200;
2028
+ readonly durationPanel: 300;
2029
+ };
2030
+ /**
2031
+ * Launcher geometry — the numbers that previously diverged between platforms
2032
+ * (banner height, FAB size, default gutter, panel width). Centralising them
2033
+ * is what fixes the "different on every app" + safe-area bleed problems.
2034
+ */
2035
+ declare const MUSHI_GEOMETRY: {
2036
+ readonly bannerHeight: 36;
2037
+ readonly fabSize: 52;
2038
+ readonly edgeTabWidth: 28;
2039
+ /** default gutter from the screen edge (added on top of safe-area insets) */
2040
+ readonly gutter: 24;
2041
+ readonly panelWidth: 384;
2042
+ /** below this viewport width the panel becomes a full-width bottom sheet */
2043
+ readonly panelSheetBreakpoint: 480;
2044
+ };
2045
+ /**
2046
+ * Default user-facing copy. Centralised so apps stop drifting on the banner
2047
+ * label / CTA wording (seen as inconsistent across the four host apps).
2048
+ */
2049
+ declare const MUSHI_COPY: {
2050
+ readonly bannerLabel: "Beta";
2051
+ readonly bannerMessage: "Help us improve — your feedback shapes what we ship.";
2052
+ readonly bugCta: "Report a bug";
2053
+ readonly featureCta: "Feature request";
2054
+ readonly triggerText: "🐛";
2055
+ readonly panelTitle: "Send feedback";
2056
+ readonly myReportsTab: "My reports";
2057
+ readonly assistantTab: "Ask";
2058
+ };
2059
+ /** A flat, serialisable snapshot of every token for a given theme mode. */
2060
+ interface MushiTokenSnapshot {
2061
+ mode: MushiThemeMode;
2062
+ colors: MushiColorPalette;
2063
+ spacing: typeof MUSHI_SPACING;
2064
+ radius: typeof MUSHI_RADIUS;
2065
+ type: typeof MUSHI_TYPE;
2066
+ z: typeof MUSHI_Z;
2067
+ motion: typeof MUSHI_MOTION;
2068
+ geometry: typeof MUSHI_GEOMETRY;
2069
+ copy: typeof MUSHI_COPY;
2070
+ }
2071
+ /** Build a full token snapshot for a theme mode (used by RN/Capacitor). */
2072
+ declare function mushiTokens(mode: MushiThemeMode): MushiTokenSnapshot;
2073
+
2074
+ /**
2075
+ * FILE: packages/core/src/identity.ts
2076
+ * PURPOSE: The signed-JWT end-user identity contract shared between the SDK
2077
+ * and the Mushi backend.
2078
+ *
2079
+ * The host app's server mints a short-lived JWT (HS256, signed with the
2080
+ * project's identity secret obtained from the console) describing the
2081
+ * currently-logged-in end user, and hands it to the SDK via
2082
+ * `Mushi.identifyWithToken(jwt)`. The SDK never verifies the signature — it
2083
+ * only base64url-decodes the payload to show the user's name locally and
2084
+ * forwards the raw token to the backend, which verifies it against the
2085
+ * per-project secret before trusting any claim. This mirrors the Featurebase
2086
+ * `featurebaseJwt` model and is the trust anchor for "My Reports", rewards,
2087
+ * and the per-user assistant data index.
2088
+ *
2089
+ * Anonymous reporters (logged-out) keep using the random reporter token; the
2090
+ * signed JWT is purely additive.
2091
+ */
2092
+ /** Claims carried by a Mushi end-user identity token. */
2093
+ interface MushiIdentityClaims {
2094
+ /** Mushi external project id this token is scoped to. */
2095
+ projectId: string;
2096
+ /** The host app's stable id for this end user (becomes `external_user_id`). */
2097
+ sub: string;
2098
+ /** Optional display email (never stored raw server-side — hashed). */
2099
+ email?: string;
2100
+ /** Optional display name. */
2101
+ name?: string;
2102
+ /** Issued-at (seconds since epoch). */
2103
+ iat?: number;
2104
+ /** Expiry (seconds since epoch). Tokens should be short-lived (<= 1h). */
2105
+ exp?: number;
2106
+ }
2107
+ /**
2108
+ * Sentinel so callers can distinguish a Mushi identity token from the random
2109
+ * anonymous reporter token (`mushi_<uuid>`). Identity tokens are standard JWTs
2110
+ * (three dot-separated base64url segments), so this prefix is informational
2111
+ * only and not embedded in the wire format.
2112
+ */
2113
+ declare const MUSHI_IDENTITY_TOKEN_PREFIX = "mushi.identity";
2114
+ /**
2115
+ * Build the canonical claims object a host server should sign. Exposed so the
2116
+ * `@mushi-mushi/node` SDK and docs share one definition of the payload shape.
2117
+ */
2118
+ declare function buildIdentityClaims(input: {
2119
+ projectId: string;
2120
+ userId: string;
2121
+ email?: string;
2122
+ name?: string;
2123
+ ttlSeconds?: number;
2124
+ }): Required<Pick<MushiIdentityClaims, 'projectId' | 'sub' | 'iat' | 'exp'>> & Pick<MushiIdentityClaims, 'email' | 'name'>;
2125
+ /**
2126
+ * Decode (WITHOUT verifying) the claims of an identity JWT. Returns null if the
2127
+ * token is malformed. CLIENT-SIDE DISPLAY ONLY — never trust these claims for
2128
+ * authorisation; the server re-verifies the signature.
2129
+ */
2130
+ declare function parseIdentityToken(token: string): MushiIdentityClaims | null;
2131
+
2132
+ export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_COLORS_DARK, MUSHI_COLORS_LIGHT, MUSHI_COPY, MUSHI_GEOMETRY, MUSHI_IDENTITY_TOKEN_PREFIX, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, MUSHI_MOTION, MUSHI_RADIUS, MUSHI_SPACING, MUSHI_TYPE, MUSHI_Z, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiAssistantConfig, type MushiAssistantReply, type MushiAssistantStep, type MushiBannerConfig, type MushiBannerLink, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiColorPalette, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiCrossAppReport, type MushiCustomCategory, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiHallOfFameEntry, type MushiIdentityClaims, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiLeaderboardEntry, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, type MushiPageContext, type MushiPerformanceMetrics, type MushiPreFilterConfig, type MushiPreset, type MushiPrivacyConfig, type MushiProactiveConfig, type MushiRegion, type MushiReport, type MushiReportBuilder, type MushiReportCategory, type MushiReportStatus, type MushiReporterComment, type MushiReporterReport, type MushiReputationResult, type MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiSentryContext, type MushiTesterReputation, type MushiThemeMode, type MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiTokenSnapshot, type MushiTracePropagationConfig, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PayloadGuardResult, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, type ResolvedEnvConfig, buildIdentityClaims, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, parseIdentityToken, resolveEnvConfig, resolveRegionEndpoint, scrubPii };
package/dist/index.js CHANGED
@@ -41,6 +41,7 @@ function formatBytes(n) {
41
41
  }
42
42
 
43
43
  // src/api-client.ts
44
+ var MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
44
45
  var DEFAULT_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
45
46
  var MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
46
47
  var MUSHI_INTERNAL_INIT_MARKER = "__mushiInternal";
@@ -52,13 +53,15 @@ function createApiClient(options) {
52
53
  apiKey,
53
54
  apiEndpoint = DEFAULT_API_ENDPOINT,
54
55
  timeout = DEFAULT_TIMEOUT,
55
- maxRetries = DEFAULT_MAX_RETRIES
56
+ maxRetries = DEFAULT_MAX_RETRIES,
57
+ getUserToken
56
58
  } = options;
57
59
  let baseUrl = apiEndpoint.replace(/\/$/, "");
58
60
  async function request(method, path, body, retries = maxRetries, internalKind, extraHeaders) {
59
61
  const url = `${baseUrl}${path}`;
60
62
  const controller = new AbortController();
61
63
  const timer = setTimeout(() => controller.abort(), timeout);
64
+ const userToken = getUserToken?.() ?? null;
62
65
  try {
63
66
  const response = await fetch(url, {
64
67
  method,
@@ -66,6 +69,7 @@ function createApiClient(options) {
66
69
  "Content-Type": "application/json",
67
70
  "X-Mushi-Api-Key": apiKey,
68
71
  "X-Mushi-Project": projectId,
72
+ ...userToken ? { [MUSHI_USER_TOKEN_HEADER]: userToken } : {},
69
73
  ...internalKind ? { [MUSHI_INTERNAL_HEADER]: internalKind } : {},
70
74
  ...extraHeaders
71
75
  },
@@ -168,6 +172,19 @@ function createApiClient(options) {
168
172
  async getSdkConfig() {
169
173
  return request("GET", "/v1/sdk/config", void 0, maxRetries, "sdk-config");
170
174
  },
175
+ async askAssistant(input) {
176
+ return request(
177
+ "POST",
178
+ "/v1/sdk/assistant",
179
+ {
180
+ message: input.message,
181
+ threadId: input.threadId ?? null,
182
+ context: input.context ?? null
183
+ },
184
+ 1,
185
+ "community"
186
+ );
187
+ },
171
188
  async getLatestSdkVersion(packageName) {
172
189
  const query = new URLSearchParams({ package: packageName }).toString();
173
190
  return request("GET", `/v1/sdk/latest-version?${query}`, void 0, maxRetries, "sdk-config");
@@ -445,6 +462,60 @@ function writeCache(storage, projectId, entry) {
445
462
  }
446
463
  }
447
464
 
465
+ // src/env-config.ts
466
+ function first(...values) {
467
+ return values.find((v) => v !== void 0 && v !== "");
468
+ }
469
+ function tryImportMetaEnv(key) {
470
+ try {
471
+ const env = import.meta.env;
472
+ return typeof env === "object" && env !== null ? env[key] : void 0;
473
+ } catch {
474
+ return void 0;
475
+ }
476
+ }
477
+ function readProcessEnv(read) {
478
+ try {
479
+ return read();
480
+ } catch {
481
+ return void 0;
482
+ }
483
+ }
484
+ function resolveEnvConfig() {
485
+ const projectId = first(
486
+ tryImportMetaEnv("VITE_MUSHI_PROJECT_ID"),
487
+ tryImportMetaEnv("NEXT_PUBLIC_MUSHI_PROJECT_ID"),
488
+ tryImportMetaEnv("NUXT_PUBLIC_MUSHI_PROJECT_ID"),
489
+ tryImportMetaEnv("EXPO_PUBLIC_MUSHI_PROJECT_ID"),
490
+ readProcessEnv(() => process.env.NEXT_PUBLIC_MUSHI_PROJECT_ID),
491
+ readProcessEnv(() => process.env.EXPO_PUBLIC_MUSHI_PROJECT_ID),
492
+ readProcessEnv(() => process.env.MUSHI_PROJECT_ID)
493
+ );
494
+ const apiKey = first(
495
+ tryImportMetaEnv("VITE_MUSHI_API_KEY"),
496
+ tryImportMetaEnv("NEXT_PUBLIC_MUSHI_API_KEY"),
497
+ tryImportMetaEnv("NUXT_PUBLIC_MUSHI_API_KEY"),
498
+ tryImportMetaEnv("EXPO_PUBLIC_MUSHI_API_KEY"),
499
+ readProcessEnv(() => process.env.NEXT_PUBLIC_MUSHI_API_KEY),
500
+ readProcessEnv(() => process.env.EXPO_PUBLIC_MUSHI_API_KEY),
501
+ readProcessEnv(() => process.env.MUSHI_API_KEY)
502
+ );
503
+ const apiEndpoint = first(
504
+ tryImportMetaEnv("VITE_MUSHI_API_ENDPOINT"),
505
+ tryImportMetaEnv("NEXT_PUBLIC_MUSHI_API_ENDPOINT"),
506
+ tryImportMetaEnv("NUXT_PUBLIC_MUSHI_API_ENDPOINT"),
507
+ tryImportMetaEnv("EXPO_PUBLIC_MUSHI_API_ENDPOINT"),
508
+ readProcessEnv(() => process.env.NEXT_PUBLIC_MUSHI_API_ENDPOINT),
509
+ readProcessEnv(() => process.env.EXPO_PUBLIC_MUSHI_API_ENDPOINT),
510
+ readProcessEnv(() => process.env.MUSHI_API_ENDPOINT)
511
+ );
512
+ const result = {};
513
+ if (projectId) result.projectId = projectId;
514
+ if (apiKey) result.apiKey = apiKey;
515
+ if (apiEndpoint) result.apiEndpoint = apiEndpoint;
516
+ return result;
517
+ }
518
+
448
519
  // src/pre-filter.ts
449
520
  var DEFAULT_MIN_LENGTH = 10;
450
521
  var DEFAULT_MAX_LENGTH = 2e3;
@@ -1500,6 +1571,159 @@ function normaliseThrown(thrown) {
1500
1571
  return { name: "Error", message: String(thrown) };
1501
1572
  }
1502
1573
 
1503
- export { DEFAULT_API_ENDPOINT, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, REGION_ENDPOINTS, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, newUuid, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
1574
+ // src/design-tokens.ts
1575
+ var MUSHI_COLORS_LIGHT = {
1576
+ paper: "#F8F4ED",
1577
+ paperRaised: "#FFFFFF",
1578
+ ink: "#0E0D0B",
1579
+ inkMuted: "#5C5852",
1580
+ inkFaint: "#9A9489",
1581
+ inkDim: "#6E6760",
1582
+ rule: "rgba(14,13,11,0.10)",
1583
+ ruleStrong: "rgba(14,13,11,0.16)",
1584
+ accent: "#E03C2C",
1585
+ accentWash: "rgba(224,60,44,0.08)",
1586
+ accentInk: "#7A1F15",
1587
+ ok: "#16A34A",
1588
+ danger: "#DC2626"
1589
+ };
1590
+ var MUSHI_COLORS_DARK = {
1591
+ paper: "#0F0E0C",
1592
+ paperRaised: "#1A1815",
1593
+ ink: "#F2EBDD",
1594
+ inkMuted: "#928B7E",
1595
+ inkFaint: "#5A5650",
1596
+ inkDim: "#7A7268",
1597
+ rule: "rgba(242,235,221,0.10)",
1598
+ ruleStrong: "rgba(242,235,221,0.18)",
1599
+ accent: "#FF5A47",
1600
+ accentWash: "rgba(255,90,71,0.12)",
1601
+ accentInk: "#FFE5E0",
1602
+ ok: "#4ADE80",
1603
+ danger: "#F87171"
1604
+ };
1605
+ function mushiPalette(mode) {
1606
+ return mode === "dark" ? MUSHI_COLORS_DARK : MUSHI_COLORS_LIGHT;
1607
+ }
1608
+ var MUSHI_SPACING = {
1609
+ hairline: 2,
1610
+ tight: 4,
1611
+ snug: 8,
1612
+ comfy: 12,
1613
+ roomy: 16,
1614
+ lounge: 20,
1615
+ open: 24,
1616
+ wide: 32
1617
+ };
1618
+ var MUSHI_RADIUS = {
1619
+ none: 0,
1620
+ control: 4,
1621
+ card: 10,
1622
+ sheet: 16,
1623
+ pill: 999
1624
+ };
1625
+ var MUSHI_TYPE = {
1626
+ fontDisplay: `'Iowan Old Style', 'Palatino Linotype', 'Palatino', 'Book Antiqua', 'Cambria', Georgia, 'Times New Roman', serif`,
1627
+ fontBody: `system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI Variable Display', 'Segoe UI', sans-serif`,
1628
+ fontMono: `ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, 'Liberation Mono', monospace`,
1629
+ sizeBody: 14,
1630
+ sizeLabel: 12,
1631
+ sizeTitle: 16,
1632
+ lineBody: 1.55
1633
+ };
1634
+ var MUSHI_Z = {
1635
+ base: 99999,
1636
+ /** banner sits one below the trigger so an open FAB overlaps it */
1637
+ banner: 99998,
1638
+ /** report/assistant panel sits one above the trigger */
1639
+ panel: 1e5,
1640
+ /** element-selector overlay — always on top */
1641
+ overlay: 2147483647
1642
+ };
1643
+ var MUSHI_MOTION = {
1644
+ easeStamp: "cubic-bezier(0.22, 1, 0.36, 1)",
1645
+ durationFast: 200,
1646
+ durationPanel: 300
1647
+ };
1648
+ var MUSHI_GEOMETRY = {
1649
+ bannerHeight: 36,
1650
+ fabSize: 52,
1651
+ edgeTabWidth: 28,
1652
+ /** default gutter from the screen edge (added on top of safe-area insets) */
1653
+ gutter: 24,
1654
+ panelWidth: 384,
1655
+ /** below this viewport width the panel becomes a full-width bottom sheet */
1656
+ panelSheetBreakpoint: 480
1657
+ };
1658
+ var MUSHI_COPY = {
1659
+ bannerLabel: "Beta",
1660
+ bannerMessage: "Help us improve \u2014 your feedback shapes what we ship.",
1661
+ bugCta: "Report a bug",
1662
+ featureCta: "Feature request",
1663
+ triggerText: "\u{1F41B}",
1664
+ panelTitle: "Send feedback",
1665
+ myReportsTab: "My reports",
1666
+ assistantTab: "Ask"
1667
+ };
1668
+ function mushiTokens(mode) {
1669
+ return {
1670
+ mode,
1671
+ colors: mushiPalette(mode),
1672
+ spacing: MUSHI_SPACING,
1673
+ radius: MUSHI_RADIUS,
1674
+ type: MUSHI_TYPE,
1675
+ z: MUSHI_Z,
1676
+ motion: MUSHI_MOTION,
1677
+ geometry: MUSHI_GEOMETRY,
1678
+ copy: MUSHI_COPY
1679
+ };
1680
+ }
1681
+
1682
+ // src/identity.ts
1683
+ var MUSHI_IDENTITY_TOKEN_PREFIX = "mushi.identity";
1684
+ function buildIdentityClaims(input) {
1685
+ const now = Math.floor(Date.now() / 1e3);
1686
+ const ttl = input.ttlSeconds && input.ttlSeconds > 0 ? Math.min(input.ttlSeconds, 3600) : 600;
1687
+ return {
1688
+ projectId: input.projectId,
1689
+ sub: input.userId,
1690
+ ...input.email ? { email: input.email } : {},
1691
+ ...input.name ? { name: input.name } : {},
1692
+ iat: now,
1693
+ exp: now + ttl
1694
+ };
1695
+ }
1696
+ function base64UrlDecode(segment) {
1697
+ const pad = segment.length % 4 === 0 ? "" : "=".repeat(4 - segment.length % 4);
1698
+ const base64 = segment.replace(/-/g, "+").replace(/_/g, "/") + pad;
1699
+ if (typeof atob === "function") return atob(base64);
1700
+ const B = globalThis.Buffer;
1701
+ if (B) return B.from(base64, "base64").toString("binary");
1702
+ return "";
1703
+ }
1704
+ function parseIdentityToken(token) {
1705
+ if (typeof token !== "string") return null;
1706
+ const parts = token.split(".");
1707
+ if (parts.length !== 3) return null;
1708
+ try {
1709
+ const json = base64UrlDecode(parts[1]);
1710
+ const payload = JSON.parse(json);
1711
+ const projectId = typeof payload.projectId === "string" ? payload.projectId : void 0;
1712
+ const sub = typeof payload.sub === "string" ? payload.sub : void 0;
1713
+ if (!projectId || !sub) return null;
1714
+ return {
1715
+ projectId,
1716
+ sub,
1717
+ ...typeof payload.email === "string" ? { email: payload.email } : {},
1718
+ ...typeof payload.name === "string" ? { name: payload.name } : {},
1719
+ ...typeof payload.iat === "number" ? { iat: payload.iat } : {},
1720
+ ...typeof payload.exp === "number" ? { exp: payload.exp } : {}
1721
+ };
1722
+ } catch {
1723
+ return null;
1724
+ }
1725
+ }
1726
+
1727
+ export { DEFAULT_API_ENDPOINT, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_COLORS_DARK, MUSHI_COLORS_LIGHT, MUSHI_COPY, MUSHI_GEOMETRY, MUSHI_IDENTITY_TOKEN_PREFIX, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, MUSHI_MOTION, MUSHI_RADIUS, MUSHI_SPACING, MUSHI_TYPE, MUSHI_Z, REGION_ENDPOINTS, buildIdentityClaims, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, parseIdentityToken, resolveEnvConfig, resolveRegionEndpoint, scrubPii };
1504
1728
  //# sourceMappingURL=index.js.map
1505
1729
  //# sourceMappingURL=index.js.map