@graineai/inapp-react-native 0.16.0 → 0.18.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/INTEGRATION.md
CHANGED
|
@@ -652,11 +652,37 @@ agent not enabled for apps — leaves the launcher rendering nothing, silently.
|
|
|
652
652
|
Pass it before you debug anything else.
|
|
653
653
|
|
|
654
654
|
**On `rtc`: every call reports a fallback to `ws`.**
|
|
655
|
-
`onTransport` gives a `reason
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
655
|
+
`onTransport` gives a `reason`, and `message` carries the server's own sentence.
|
|
656
|
+
|
|
657
|
+
| `reason` | Means | Retry helps? |
|
|
658
|
+
|---|---|---|
|
|
659
|
+
| `not_configured` | A setting is wrong — see below. Also raised through `onError`. | Never |
|
|
660
|
+
| `unreachable` | No SIP realm on the org, or the SBC is not reachable from this network. | Sometimes |
|
|
661
|
+
| `screen_channel` | Audio connected, the screen channel did not, so the call restarted rather than run blind. | Sometimes |
|
|
662
|
+
|
|
663
|
+
**`not_configured`: which setting?** The call still works — it is carried on the
|
|
664
|
+
WebSocket — so nothing is on fire, but WebRTC will never be used until this is
|
|
665
|
+
fixed. Ask the mint endpoint directly, without rebuilding anything:
|
|
666
|
+
|
|
667
|
+
```bash
|
|
668
|
+
curl -i -X POST https://www.graine.ai/api/embed/rtc-session \
|
|
669
|
+
-H 'Content-Type: application/json' \
|
|
670
|
+
-d '{"publishableKey":"pk_live_YOUR_KEY"}'
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
| Response | `code` | Fix |
|
|
674
|
+
|---|---|---|
|
|
675
|
+
| 401 | `key_missing` | No key reached the endpoint. Update the SDK — before 0.17.0 it did not put `pk` in the page URL. |
|
|
676
|
+
| 401 | `key_malformed` | Not a publishable key. It starts with `pk_live_`, from Agent → Embed & Widgets. |
|
|
677
|
+
| 401 | `key_unknown` | No agent registered to that key. |
|
|
678
|
+
| 403 | `no_origin` | **The common one for apps.** A native app sends no `Origin`, so add the literal entry `app://` to that agent's Allowed domains. |
|
|
679
|
+
| 403 | `no_domains_configured` | The allowlist is empty, which denies everything. |
|
|
680
|
+
| 403 | `origin_not_allowed` | Add your site to Allowed domains. |
|
|
681
|
+
| 503 | `not_configured` | WebRTC is not enabled on that deployment. Not fixable from the app. |
|
|
682
|
+
| 200 | — | The mint works; look at `onTransport` for `unreachable` instead. |
|
|
683
|
+
|
|
684
|
+
That curl sends **no** `Origin` header, exactly as a native app does — so a 403
|
|
685
|
+
from it is precisely the 403 your app is getting.
|
|
660
686
|
|
|
661
687
|
**On `rtc`: the greeting plays twice, occasionally.**
|
|
662
688
|
That is the `screen_channel` fallback restarting the call. It only happens when
|
|
@@ -4,6 +4,8 @@ export interface GraineVoiceLauncherProps {
|
|
|
4
4
|
onTransport?: (info: {
|
|
5
5
|
transport: "ws" | "rtc";
|
|
6
6
|
requested: "ws" | "rtc";
|
|
7
|
+
reason?: "not_configured" | "unreachable" | "screen_channel";
|
|
8
|
+
message?: string;
|
|
7
9
|
}) => void;
|
|
8
10
|
webView: React.ComponentType<any>;
|
|
9
11
|
autoStart?: boolean;
|
|
@@ -25,6 +27,7 @@ export interface GraineVoiceApi {
|
|
|
25
27
|
connected: boolean;
|
|
26
28
|
connecting: boolean;
|
|
27
29
|
muted: boolean;
|
|
30
|
+
ready: boolean;
|
|
28
31
|
start: () => void;
|
|
29
32
|
end: () => void;
|
|
30
33
|
setMuted: (m: boolean) => void;
|
|
@@ -106,12 +106,18 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, trans
|
|
|
106
106
|
onTransport?.({
|
|
107
107
|
transport: msg.transport === "rtc" ? "rtc" : "ws",
|
|
108
108
|
requested: msg.requested === "rtc" ? "rtc" : "ws",
|
|
109
|
+
reason: msg.reason,
|
|
110
|
+
message: msg.message,
|
|
109
111
|
});
|
|
112
|
+
if (msg.reason === "not_configured" && msg.message) {
|
|
113
|
+
onError?.(String(msg.message));
|
|
114
|
+
}
|
|
110
115
|
return;
|
|
111
116
|
case "graine:ready":
|
|
112
117
|
setReady(true);
|
|
113
118
|
pushContext();
|
|
114
|
-
if (autoStart && !startedRef.current) {
|
|
119
|
+
if ((autoStart || pendingStartRef.current) && !startedRef.current) {
|
|
120
|
+
pendingStartRef.current = false;
|
|
115
121
|
startedRef.current = true;
|
|
116
122
|
void beginCall();
|
|
117
123
|
}
|
|
@@ -150,15 +156,21 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, trans
|
|
|
150
156
|
return;
|
|
151
157
|
}
|
|
152
158
|
}
|
|
153
|
-
}, [autoStart, beginCall, client, onCaption, onCallState, onMicDenied, onTransport, onEnded, post, pushContext]);
|
|
159
|
+
}, [autoStart, beginCall, client, onCaption, onCallState, onMicDenied, onTransport, onError, onEnded, post, pushContext]);
|
|
160
|
+
const pendingStartRef = useRef(false);
|
|
154
161
|
const api = {
|
|
155
162
|
connected,
|
|
156
163
|
connecting,
|
|
157
164
|
muted,
|
|
165
|
+
ready: !!voiceAgentId && ready,
|
|
158
166
|
start: useCallback(() => {
|
|
159
167
|
setConnecting(true);
|
|
168
|
+
if (!ready) {
|
|
169
|
+
pendingStartRef.current = true;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
160
172
|
void beginCall();
|
|
161
|
-
}, [beginCall]),
|
|
173
|
+
}, [beginCall, ready]),
|
|
162
174
|
end: useCallback(() => post({ type: "graine:end-call" }), [post]),
|
|
163
175
|
setMuted: useCallback((m) => {
|
|
164
176
|
setMutedState(m);
|
|
@@ -196,6 +208,7 @@ export function GraineVoiceLauncher({ webView: WebView, autoStart = false, trans
|
|
|
196
208
|
const effectiveTransport = transport ?? (appearance?.audioTransport === "rtc" ? "rtc" : "ws");
|
|
197
209
|
const src = `${client.opts.baseUrl.replace(/\/$/, "")}/embed/${voiceAgentId}` +
|
|
198
210
|
`?mode=voice&branding=0&embed=1&transport=${effectiveTransport}` +
|
|
211
|
+
`&pk=${encodeURIComponent(client.opts.publishableKey)}` +
|
|
199
212
|
(appearance?.accent ? `&accent=${encodeURIComponent(String(appearance.accent).replace("#", ""))}` : "");
|
|
200
213
|
return (_jsxs(_Fragment, { children: [_jsx(View, { style: { position: "absolute", width: 1, height: 1, opacity: 0, bottom: 0, left: 0 }, pointerEvents: "none", children: _jsx(WebView, { ref: ref, source: { uri: src }, originWhitelist: ["*"], javaScriptEnabled: true, domStorageEnabled: true, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, mediaCapturePermissionGrantType: "grant", onMessage: onMessage, style: { width: 1, height: 1, backgroundColor: "transparent" } }) }), children?.(api)] }));
|
|
201
214
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graineai/inapp-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Graine in-app agent for React Native
|
|
5
|
+
"description": "Graine in-app agent for React Native \u2014 an agent that sees the screen your customer is on and can act on it.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"private": false,
|
|
8
8
|
"main": "dist/index.js",
|