yaver-feedback-react-native 0.2.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/README.md +690 -0
- package/app.plugin.js +70 -0
- package/package.json +39 -0
- package/src/BlackBox.ts +317 -0
- package/src/ConnectionScreen.tsx +400 -0
- package/src/Discovery.ts +223 -0
- package/src/FeedbackModal.tsx +678 -0
- package/src/FixReport.tsx +313 -0
- package/src/FloatingButton.tsx +860 -0
- package/src/P2PClient.ts +303 -0
- package/src/ShakeDetector.ts +57 -0
- package/src/YaverFeedback.ts +345 -0
- package/src/__tests__/Discovery.test.ts +187 -0
- package/src/__tests__/P2PClient.test.ts +218 -0
- package/src/__tests__/SDKToken.test.ts +268 -0
- package/src/__tests__/YaverFeedback.test.ts +189 -0
- package/src/__tests__/types.test.ts +247 -0
- package/src/capture.ts +84 -0
- package/src/expo.ts +62 -0
- package/src/index.ts +54 -0
- package/src/types.ts +251 -0
- package/src/upload.ts +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
# @yaver/feedback-react-native
|
|
2
|
+
|
|
3
|
+
Visual feedback SDK for Yaver. Lets testers and developers shake their phone (or tap a floating button) to capture screenshots, record voice notes, and send bug reports directly to a Yaver agent running on a dev machine. Built for vibe coding workflows where feedback needs to flow fast.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @yaver/feedback-react-native
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Peer dependencies
|
|
12
|
+
|
|
13
|
+
For full functionality, install these optional peer dependencies:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Device discovery (stored connections)
|
|
17
|
+
npm install @react-native-async-storage/async-storage
|
|
18
|
+
|
|
19
|
+
# Screenshots
|
|
20
|
+
npm install react-native-view-shot
|
|
21
|
+
|
|
22
|
+
# Voice notes
|
|
23
|
+
npm install react-native-audio-recorder-player
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { YaverFeedback, BlackBox, FeedbackModal } from '@yaver/feedback-react-native';
|
|
30
|
+
|
|
31
|
+
// Initialize once at app startup
|
|
32
|
+
YaverFeedback.init({
|
|
33
|
+
agentUrl: 'http://192.168.1.10:18080',
|
|
34
|
+
authToken: 'your-sdk-token', // SDK token (recommended) or CLI token
|
|
35
|
+
trigger: 'shake',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Start Black Box flight recorder
|
|
39
|
+
BlackBox.start();
|
|
40
|
+
BlackBox.wrapConsole(); // opt-in: stream console.log/warn/error to the agent
|
|
41
|
+
|
|
42
|
+
// Add FeedbackModal to your root component
|
|
43
|
+
function App() {
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
<YourApp />
|
|
47
|
+
<FeedbackModal />
|
|
48
|
+
</>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Shake your phone to open the feedback modal. Take screenshots, record voice notes, and send everything to your Yaver agent in one tap.
|
|
54
|
+
|
|
55
|
+
## Authentication
|
|
56
|
+
|
|
57
|
+
The SDK uses **bearer token auth** for all requests to the agent. Two token types are supported:
|
|
58
|
+
|
|
59
|
+
### SDK Tokens (recommended)
|
|
60
|
+
|
|
61
|
+
SDK tokens are **long-lived (1 year)** and **independent from CLI session tokens**. CLI reauth (`yaver auth`) does NOT invalidate SDK tokens.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Create an SDK token
|
|
65
|
+
yaver sdk-token create --label "AcmeStore dev"
|
|
66
|
+
# prints: 4a8f...b3c2
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Use in your SDK config:
|
|
70
|
+
```tsx
|
|
71
|
+
YaverFeedback.init({
|
|
72
|
+
authToken: '4a8f...b3c2', // SDK token
|
|
73
|
+
agentUrl: 'http://192.168.1.10:18080',
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Or via env var for build-time injection:
|
|
78
|
+
```bash
|
|
79
|
+
# .env.yaver (gitignored)
|
|
80
|
+
YAVER_SDK_TOKEN=4a8f...b3c2
|
|
81
|
+
YAVER_AGENT_URL=http://192.168.1.10:18080
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### CLI Token (fallback)
|
|
85
|
+
|
|
86
|
+
The SDK can share the CLI session token directly. This works because each `yaver auth` creates a **new session** without invalidating old ones. The old token stays valid until it expires (1 year) or is explicitly revoked via "logout everywhere".
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# .env.yaver (fallback)
|
|
90
|
+
YAVER_AUTH_TOKEN=abc123...
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Token Validation Flow
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
SDK request → Agent HTTP server
|
|
97
|
+
1. Exact match with agent's own token → Allow (fast path)
|
|
98
|
+
2. Token in cache → userId match → Allow/Deny
|
|
99
|
+
3. Try Convex /auth/validate (session token)
|
|
100
|
+
4. Try Convex /sdk/token/validate (SDK token)
|
|
101
|
+
→ Cache result → userId match → Allow/Deny
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Both token types resolve to the same `userId`. The agent checks `userId` equality, not token equality.
|
|
105
|
+
|
|
106
|
+
### Token Lifecycle
|
|
107
|
+
|
|
108
|
+
| Event | CLI Token | SDK Token |
|
|
109
|
+
|-------|-----------|-----------|
|
|
110
|
+
| `yaver auth` (reauth) | New token created, old stays valid | Unaffected |
|
|
111
|
+
| `yaver signout` | Current session deleted | Unaffected |
|
|
112
|
+
| `yaver signout --all` | ALL sessions deleted | Unaffected |
|
|
113
|
+
| SDK token revoke | Unaffected | Revoked |
|
|
114
|
+
| Token expiry | 1 year from last refresh | 1 year from creation |
|
|
115
|
+
|
|
116
|
+
### Security
|
|
117
|
+
|
|
118
|
+
The SDK implements defense-in-depth with 6 security layers:
|
|
119
|
+
|
|
120
|
+
**1. Scope Restriction** — SDK tokens can only access feedback/blackbox/voice/builds endpoints. They CANNOT execute tasks, run commands, access the vault, or shut down the agent.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Default scopes (safe for embedding in app builds)
|
|
124
|
+
yaver sdk-token create --label "AcmeStore"
|
|
125
|
+
# → scopes: feedback, blackbox, voice, builds
|
|
126
|
+
|
|
127
|
+
# Narrow scopes (feedback only)
|
|
128
|
+
yaver sdk-token create --scopes feedback,blackbox
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**2. IP Binding** — Restrict tokens to specific networks:
|
|
132
|
+
```bash
|
|
133
|
+
yaver sdk-token create --allowed-ips 192.168.1.0/24
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**3. Agent-side IP Allowlist** — Block all requests from outside your network:
|
|
137
|
+
```bash
|
|
138
|
+
yaver serve --allow-ips 192.168.1.0/24,10.0.0.0/8
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**4. Token Rotation** — Rotate tokens without downtime (5-minute grace period):
|
|
142
|
+
```typescript
|
|
143
|
+
const { token } = await client.rotateToken();
|
|
144
|
+
// Old token valid for 5 more minutes
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Short-lived tokens for CI/CD
|
|
149
|
+
yaver sdk-token create --expires 24h
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**5. New Device Alerts** — When an SDK token is used from a new IP, a security event is logged to Convex. Query via `GET /security/events`.
|
|
153
|
+
|
|
154
|
+
**6. HTTPS on LAN** — Agent auto-generates a self-signed TLS cert and serves HTTPS on port 18443. The cert fingerprint is exposed via `/health` and the LAN beacon for cert pinning.
|
|
155
|
+
|
|
156
|
+
**General rules:**
|
|
157
|
+
- **Never commit tokens to source control** — use `.env.yaver` (gitignored) or env vars
|
|
158
|
+
- SDK tokens can be revoked independently: `POST /sdk/token/revoke`
|
|
159
|
+
- Even if an SDK token is stolen, it cannot run code on your machine (scope restriction)
|
|
160
|
+
- The Convex backend validates tokens against your account only
|
|
161
|
+
|
|
162
|
+
## Black Box (Flight Recorder)
|
|
163
|
+
|
|
164
|
+
Continuous streaming of app events to the agent. The agent keeps a ring buffer (last 1000 events per device) and injects context into fix prompts — so the AI agent already knows what the app was doing when you ask for a fix.
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { BlackBox } from '@yaver/feedback-react-native';
|
|
168
|
+
|
|
169
|
+
// Start streaming (call after YaverFeedback.init)
|
|
170
|
+
BlackBox.start({
|
|
171
|
+
flushInterval: 2000, // send buffered events every 2s (default)
|
|
172
|
+
maxBufferSize: 50, // flush immediately at 50 events (default)
|
|
173
|
+
appName: 'AcmeStore',
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Logging
|
|
177
|
+
BlackBox.log('Cart updated', 'CartScreen');
|
|
178
|
+
BlackBox.warn('Low inventory', 'ProductCard');
|
|
179
|
+
BlackBox.error('Payment failed', 'Checkout', { orderId: '123' });
|
|
180
|
+
|
|
181
|
+
// Navigation tracking
|
|
182
|
+
BlackBox.navigation('ProductDetail', 'Home');
|
|
183
|
+
|
|
184
|
+
// Network monitoring
|
|
185
|
+
BlackBox.networkRequest('GET', '/api/products', 200, 142);
|
|
186
|
+
BlackBox.networkRequest('POST', '/api/order', 500, 3200);
|
|
187
|
+
|
|
188
|
+
// State changes (Redux actions, context updates, etc.)
|
|
189
|
+
BlackBox.stateChange('Cart cleared', { itemCount: 0 });
|
|
190
|
+
|
|
191
|
+
// Render performance
|
|
192
|
+
BlackBox.render('ProductList', 16.5);
|
|
193
|
+
|
|
194
|
+
// Error capture (also feeds into YaverFeedback error buffer)
|
|
195
|
+
BlackBox.captureError(new Error('Null ref'), false, { component: 'CartIcon' });
|
|
196
|
+
|
|
197
|
+
// Console wrapping (opt-in — SDK never auto-hooks)
|
|
198
|
+
BlackBox.wrapConsole(); // intercept console.log/warn/error
|
|
199
|
+
BlackBox.unwrapConsole(); // restore originals
|
|
200
|
+
|
|
201
|
+
// Error handler wrapper (pass-through, streams in real-time)
|
|
202
|
+
const existing = ErrorUtils.getGlobalHandler();
|
|
203
|
+
ErrorUtils.setGlobalHandler(BlackBox.wrapErrorHandler(existing));
|
|
204
|
+
|
|
205
|
+
// Control
|
|
206
|
+
BlackBox.stop(); // flush remaining events + stop timer
|
|
207
|
+
BlackBox.isStreaming; // check if active
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Event Types
|
|
211
|
+
|
|
212
|
+
| Type | Description | Example |
|
|
213
|
+
|------|-------------|---------|
|
|
214
|
+
| `log` | Console output (info/warn/error) | `BlackBox.log('User signed in')` |
|
|
215
|
+
| `error` | Caught exceptions with stack traces | `BlackBox.captureError(err)` |
|
|
216
|
+
| `navigation` | Screen transitions | `BlackBox.navigation('Cart', 'Home')` |
|
|
217
|
+
| `lifecycle` | App state (mount, background, foreground) | `BlackBox.lifecycle('app_background')` |
|
|
218
|
+
| `network` | HTTP requests/responses | `BlackBox.networkRequest('POST', '/api/pay', 500)` |
|
|
219
|
+
| `state` | State mutations | `BlackBox.stateChange('theme toggled')` |
|
|
220
|
+
| `render` | Component render with duration | `BlackBox.render('FlatList', 32.1)` |
|
|
221
|
+
|
|
222
|
+
### Resilience
|
|
223
|
+
|
|
224
|
+
- Failed flushes re-add events to the buffer (capped at 2x maxBufferSize)
|
|
225
|
+
- `YaverFeedback.setEnabled(false)` pauses BlackBox; `setEnabled(true)` resumes it
|
|
226
|
+
- BlackBox shares auth config with YaverFeedback (no separate init needed)
|
|
227
|
+
|
|
228
|
+
## Device Discovery
|
|
229
|
+
|
|
230
|
+
Three strategies (tried in order):
|
|
231
|
+
|
|
232
|
+
1. **Convex cloud** — fetch agent IP from Convex device registry (for cloud machines or cross-network)
|
|
233
|
+
2. **Stored connection** — try cached URL from last successful connection (AsyncStorage)
|
|
234
|
+
3. **LAN scan** — probe common LAN subnets (`192.168.1.*`, `192.168.0.*`, `10.0.0.*`, `10.0.1.*`)
|
|
235
|
+
|
|
236
|
+
### Convex discovery (recommended for teams)
|
|
237
|
+
|
|
238
|
+
No hardcoded IP needed — the SDK fetches the agent's IP from Convex:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
YaverFeedback.init({
|
|
242
|
+
convexUrl: 'https://your-app.convex.site',
|
|
243
|
+
authToken: 'your-token',
|
|
244
|
+
preferredDeviceId: 'abc123', // optional — first online device if omitted
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Auto-discovery (no agentUrl needed)
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
YaverFeedback.init({
|
|
252
|
+
authToken: 'your-token',
|
|
253
|
+
trigger: 'shake',
|
|
254
|
+
// No agentUrl — SDK discovers it automatically on first report
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Manual discovery
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { YaverDiscovery } from '@yaver/feedback-react-native';
|
|
262
|
+
|
|
263
|
+
// Full discovery (Convex → stored → LAN scan)
|
|
264
|
+
const result = await YaverDiscovery.discover({
|
|
265
|
+
convexUrl: 'https://your-app.convex.site',
|
|
266
|
+
authToken: 'your-token',
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Probe a specific URL
|
|
270
|
+
const agent = await YaverDiscovery.probe('http://192.168.1.42:18080');
|
|
271
|
+
|
|
272
|
+
// Connect and store for future sessions
|
|
273
|
+
await YaverDiscovery.connect('http://192.168.1.42:18080');
|
|
274
|
+
|
|
275
|
+
// Clear stored connection
|
|
276
|
+
await YaverDiscovery.clear();
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Connection Screen
|
|
280
|
+
|
|
281
|
+
A full-screen UI for discovering and connecting to a Yaver agent. Shows connection status, URL/token inputs, auto-discover button, and a Start/Stop testing toggle with recording timer.
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
import { YaverConnectionScreen } from '@yaver/feedback-react-native';
|
|
285
|
+
|
|
286
|
+
function App() {
|
|
287
|
+
return (
|
|
288
|
+
<>
|
|
289
|
+
<YourApp />
|
|
290
|
+
{__DEV__ && <YaverConnectionScreen />}
|
|
291
|
+
</>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The connection screen auto-discovers agents on mount and provides:
|
|
297
|
+
- Green/red connection status indicator
|
|
298
|
+
- Text inputs for agent URL (pre-filled from discovery) and auth token
|
|
299
|
+
- "Auto-discover" button to scan the network
|
|
300
|
+
- "Connect" button for manual connection
|
|
301
|
+
- "Start Testing" / "Stop & Send" toggle with recording timer
|
|
302
|
+
|
|
303
|
+
## Three Feedback Modes
|
|
304
|
+
|
|
305
|
+
### Live
|
|
306
|
+
|
|
307
|
+
Events are streamed to the agent as they happen. The agent can respond with commentary in real-time.
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
YaverFeedback.init({
|
|
311
|
+
agentUrl: 'http://192.168.1.10:18080',
|
|
312
|
+
authToken: 'your-token',
|
|
313
|
+
feedbackMode: 'live',
|
|
314
|
+
agentCommentaryLevel: 5, // Agent responds to what it sees
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Narrated
|
|
319
|
+
|
|
320
|
+
Record everything (screenshots, voice notes), then send the full bundle when you tap "Stop & Send". Good for walkthrough-style bug reports.
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
YaverFeedback.init({
|
|
324
|
+
agentUrl: 'http://192.168.1.10:18080',
|
|
325
|
+
authToken: 'your-token',
|
|
326
|
+
feedbackMode: 'narrated',
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Batch (default)
|
|
331
|
+
|
|
332
|
+
Collect everything and dump it all at the end when you tap "Send Report". The classic bug report flow.
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
YaverFeedback.init({
|
|
336
|
+
agentUrl: 'http://192.168.1.10:18080',
|
|
337
|
+
authToken: 'your-token',
|
|
338
|
+
feedbackMode: 'batch',
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Agent Commentary Levels
|
|
343
|
+
|
|
344
|
+
In live mode, the agent can comment on what it sees in real-time. Control verbosity with `agentCommentaryLevel` (0-10):
|
|
345
|
+
|
|
346
|
+
| Level | Behavior |
|
|
347
|
+
|-------|----------|
|
|
348
|
+
| 0 | Silent (default) |
|
|
349
|
+
| 1-3 | Only critical observations |
|
|
350
|
+
| 4-6 | Moderate commentary |
|
|
351
|
+
| 7-9 | Detailed observations and suggestions |
|
|
352
|
+
| 10 | Agent comments on everything it sees |
|
|
353
|
+
|
|
354
|
+
Commentary messages appear in a chat-like view inside the feedback modal.
|
|
355
|
+
|
|
356
|
+
## Voice-Driven Live Coding
|
|
357
|
+
|
|
358
|
+
In live mode, the feedback modal shows a "Speak to Fix" button. When you tap it:
|
|
359
|
+
|
|
360
|
+
1. Records your voice (uses `react-native-audio-recorder-player`)
|
|
361
|
+
2. Sends the recording to the agent as a `voice_command` event
|
|
362
|
+
3. The agent can transcribe and act on your instruction
|
|
363
|
+
|
|
364
|
+
This enables a hands-free workflow: see a bug, say what to fix, and the agent makes the change.
|
|
365
|
+
|
|
366
|
+
## Error Capture
|
|
367
|
+
|
|
368
|
+
Capture JS errors with full stack traces and attach them to feedback reports. The agent gets file names, line numbers, and optional context — goes straight to the right line.
|
|
369
|
+
|
|
370
|
+
**No conflicts with Sentry, Crashlytics, Bugsnag, or any other tool.** The SDK never auto-hooks global error handlers. You explicitly insert it into your error chain wherever you want.
|
|
371
|
+
|
|
372
|
+
### Option 1: Wrap the error handler (recommended)
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
import { ErrorUtils } from 'react-native';
|
|
376
|
+
|
|
377
|
+
// Insert Yaver into the error chain — works alongside Sentry, Crashlytics, etc.
|
|
378
|
+
const existing = ErrorUtils.getGlobalHandler();
|
|
379
|
+
ErrorUtils.setGlobalHandler(YaverFeedback.wrapErrorHandler(existing));
|
|
380
|
+
|
|
381
|
+
// Other tools can still wrap after this. The chain stays intact:
|
|
382
|
+
// Sentry → Yaver wrapper → original RN handler
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`wrapErrorHandler` returns a pass-through function that records the error in Yaver's ring buffer, then calls the next handler. It never swallows errors.
|
|
386
|
+
|
|
387
|
+
### Option 2: Manual attach (in catch blocks)
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
try {
|
|
391
|
+
await riskyOperation();
|
|
392
|
+
} catch (err) {
|
|
393
|
+
YaverFeedback.attachError(err, {
|
|
394
|
+
context: 'checkout-flow',
|
|
395
|
+
userId: currentUser.id,
|
|
396
|
+
cartItems: cart.length,
|
|
397
|
+
});
|
|
398
|
+
throw err; // still propagate
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### What the agent receives
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"errors": [
|
|
407
|
+
{
|
|
408
|
+
"message": "Cannot read property 'id' of undefined",
|
|
409
|
+
"stack": [
|
|
410
|
+
"at CheckoutButton.handlePress (CheckoutScreen.tsx:47)",
|
|
411
|
+
"at processQueue (react-native/Libraries/Renderer/...)"
|
|
412
|
+
],
|
|
413
|
+
"isFatal": false,
|
|
414
|
+
"timestamp": 1742812200000,
|
|
415
|
+
"metadata": {
|
|
416
|
+
"context": "checkout-flow",
|
|
417
|
+
"cartItems": 3
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
]
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### API
|
|
425
|
+
|
|
426
|
+
| Method | Description |
|
|
427
|
+
|--------|-------------|
|
|
428
|
+
| `attachError(error, metadata?)` | Manually attach an error with optional context |
|
|
429
|
+
| `wrapErrorHandler(next?)` | Returns a pass-through handler for the error chain |
|
|
430
|
+
| `getCapturedErrors()` | Get the current error buffer |
|
|
431
|
+
| `clearCapturedErrors()` | Clear the error buffer |
|
|
432
|
+
|
|
433
|
+
## Configuration
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
YaverFeedback.init({
|
|
437
|
+
// Required
|
|
438
|
+
authToken: 'your-token', // Auth token for the agent
|
|
439
|
+
|
|
440
|
+
// Optional
|
|
441
|
+
agentUrl: 'http://192.168.1.10:18080', // Agent URL (auto-discovered if omitted)
|
|
442
|
+
trigger: 'shake', // 'shake' | 'floating-button' | 'manual'
|
|
443
|
+
enabled: true, // Default: __DEV__ (auto-disabled in production)
|
|
444
|
+
maxRecordingDuration: 120, // Max recording duration in seconds (default: 120)
|
|
445
|
+
feedbackMode: 'batch', // 'live' | 'narrated' | 'batch' (default: 'batch')
|
|
446
|
+
agentCommentaryLevel: 0, // 0-10 (default: 0, only relevant in live mode)
|
|
447
|
+
maxCapturedErrors: 5, // Error ring buffer size (default: 5)
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Trigger Modes
|
|
452
|
+
|
|
453
|
+
### Shake (default)
|
|
454
|
+
|
|
455
|
+
Shake the device to open the feedback modal. Uses the built-in shake event on iOS and `ShakeEvent` on Android.
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
YaverFeedback.init({ authToken, trigger: 'shake' });
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Floating Button
|
|
462
|
+
|
|
463
|
+
A small draggable "Y" button overlays the app. Tap to open the feedback modal.
|
|
464
|
+
|
|
465
|
+
```tsx
|
|
466
|
+
import { FloatingButton, FeedbackModal, YaverFeedback } from '@yaver/feedback-react-native';
|
|
467
|
+
|
|
468
|
+
function App() {
|
|
469
|
+
return (
|
|
470
|
+
<>
|
|
471
|
+
<YourApp />
|
|
472
|
+
<FloatingButton onPress={() => YaverFeedback.startReport()} />
|
|
473
|
+
<FeedbackModal />
|
|
474
|
+
</>
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Manual
|
|
480
|
+
|
|
481
|
+
Trigger feedback collection programmatically from anywhere in your app.
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
import { YaverFeedback } from '@yaver/feedback-react-native';
|
|
485
|
+
|
|
486
|
+
// In a button handler, debug menu, etc.
|
|
487
|
+
YaverFeedback.startReport();
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## P2P Client
|
|
491
|
+
|
|
492
|
+
For direct communication with the Yaver agent beyond feedback:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import { P2PClient } from '@yaver/feedback-react-native';
|
|
496
|
+
|
|
497
|
+
const client = new P2PClient('http://192.168.1.10:18080', 'your-token');
|
|
498
|
+
|
|
499
|
+
// Health check
|
|
500
|
+
const isUp = await client.health();
|
|
501
|
+
|
|
502
|
+
// Get agent info
|
|
503
|
+
const info = await client.info();
|
|
504
|
+
// { hostname: 'MacBook-Air', version: '1.45.0', platform: 'darwin' }
|
|
505
|
+
|
|
506
|
+
// Upload feedback bundle
|
|
507
|
+
const reportId = await client.uploadFeedback(bundle);
|
|
508
|
+
|
|
509
|
+
// Builds
|
|
510
|
+
const builds = await client.listBuilds();
|
|
511
|
+
const build = await client.startBuild('ios');
|
|
512
|
+
const url = client.getArtifactUrl(build.id);
|
|
513
|
+
|
|
514
|
+
// Voice
|
|
515
|
+
const voiceCap = await client.voiceStatus();
|
|
516
|
+
const { text, provider } = await client.transcribeVoice('/path/to/audio.wav');
|
|
517
|
+
|
|
518
|
+
// Autonomous test sessions
|
|
519
|
+
const { sessionId } = await client.startTestSession();
|
|
520
|
+
const session = await client.getTestSession();
|
|
521
|
+
await client.stopTestSession();
|
|
522
|
+
|
|
523
|
+
// Update connection dynamically (e.g. after re-discovery)
|
|
524
|
+
client.setBaseUrl('http://10.0.0.2:18080');
|
|
525
|
+
client.setAuthToken('new-token');
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## How It Works
|
|
529
|
+
|
|
530
|
+
1. User triggers feedback (shake, button tap, or manual call)
|
|
531
|
+
2. Feedback modal opens with mode selector (Live / Narrated / Batch)
|
|
532
|
+
3. User captures screenshots, records voice notes, or speaks commands
|
|
533
|
+
4. In live mode: events stream to the agent in real-time, agent can respond with commentary
|
|
534
|
+
5. In narrated/batch mode: everything is collected and uploaded on send
|
|
535
|
+
6. SDK uploads via multipart POST to `/feedback` (or streams to `/feedback/stream`)
|
|
536
|
+
7. The agent receives the report and can create a task from it
|
|
537
|
+
|
|
538
|
+
All data flows directly to your dev machine via the Yaver agent. Nothing goes through third-party servers.
|
|
539
|
+
|
|
540
|
+
## Development vs Production
|
|
541
|
+
|
|
542
|
+
By default, the SDK is only enabled when `__DEV__` is `true` (React Native's built-in dev mode flag). In production builds, the SDK is automatically disabled and all methods are no-ops.
|
|
543
|
+
|
|
544
|
+
Override this behavior:
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
// Force enable in production (e.g., for internal beta testers)
|
|
548
|
+
YaverFeedback.init({ authToken, enabled: true });
|
|
549
|
+
|
|
550
|
+
// Disable at runtime
|
|
551
|
+
YaverFeedback.setEnabled(false);
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
## Requirements
|
|
555
|
+
|
|
556
|
+
- React Native >= 0.70
|
|
557
|
+
- React >= 18
|
|
558
|
+
- Yaver CLI running on your dev machine (`yaver serve`)
|
|
559
|
+
- Optional: `@react-native-async-storage/async-storage` for device discovery persistence
|
|
560
|
+
- Optional: `react-native-view-shot` for screenshots
|
|
561
|
+
- Optional: `react-native-audio-recorder-player` for voice notes
|
|
562
|
+
|
|
563
|
+
## API Reference
|
|
564
|
+
|
|
565
|
+
### YaverFeedback
|
|
566
|
+
|
|
567
|
+
| Method | Description |
|
|
568
|
+
|--------|-------------|
|
|
569
|
+
| `init(config)` | Initialize the SDK with agent URL, auth token, and options |
|
|
570
|
+
| `startReport()` | Manually trigger the feedback modal (auto-discovers if needed) |
|
|
571
|
+
| `isInitialized()` | Check if the SDK has been initialized |
|
|
572
|
+
| `setEnabled(bool)` | Enable or disable at runtime |
|
|
573
|
+
| `isEnabled()` | Check if the SDK is currently enabled |
|
|
574
|
+
| `getP2PClient()` | Get the P2P client instance |
|
|
575
|
+
| `getFeedbackMode()` | Get the current feedback mode |
|
|
576
|
+
| `getCommentaryLevel()` | Get the agent commentary level (0-10) |
|
|
577
|
+
| `attachError(error, metadata?)` | Manually attach an error with optional context |
|
|
578
|
+
| `getCapturedErrors()` | Get the current captured errors buffer |
|
|
579
|
+
| `clearCapturedErrors()` | Clear the captured errors buffer |
|
|
580
|
+
|
|
581
|
+
### YaverDiscovery
|
|
582
|
+
|
|
583
|
+
| Method | Description |
|
|
584
|
+
|--------|-------------|
|
|
585
|
+
| `discover()` | Try stored connection, then scan LAN |
|
|
586
|
+
| `probe(url)` | Probe a specific URL for an agent |
|
|
587
|
+
| `connect(url)` | Connect and store for future sessions |
|
|
588
|
+
| `getStored()` | Get cached connection from storage |
|
|
589
|
+
| `store(result)` | Cache a discovery result |
|
|
590
|
+
| `clear()` | Clear stored connection |
|
|
591
|
+
|
|
592
|
+
### BlackBox
|
|
593
|
+
|
|
594
|
+
| Method | Description |
|
|
595
|
+
|--------|-------------|
|
|
596
|
+
| `start(config?)` | Start streaming events to the agent |
|
|
597
|
+
| `stop()` | Flush remaining events and stop |
|
|
598
|
+
| `isStreaming` | Whether streaming is active (getter) |
|
|
599
|
+
| `log(msg, source?, meta?)` | Log an info message |
|
|
600
|
+
| `warn(msg, source?, meta?)` | Log a warning |
|
|
601
|
+
| `error(msg, source?, meta?)` | Log an error |
|
|
602
|
+
| `captureError(err, isFatal?, meta?)` | Capture error with stack trace |
|
|
603
|
+
| `navigation(route, prevRoute?, meta?)` | Record screen navigation |
|
|
604
|
+
| `lifecycle(event, meta?)` | Record app lifecycle event |
|
|
605
|
+
| `networkRequest(method, url, status?, duration?, meta?)` | Record HTTP request |
|
|
606
|
+
| `stateChange(description, meta?)` | Record state mutation |
|
|
607
|
+
| `render(component, duration?, meta?)` | Record render event |
|
|
608
|
+
| `wrapConsole()` | Intercept console.log/warn/error |
|
|
609
|
+
| `unwrapConsole()` | Restore original console methods |
|
|
610
|
+
| `wrapErrorHandler(next?)` | Pass-through error handler with real-time streaming |
|
|
611
|
+
|
|
612
|
+
### P2PClient
|
|
613
|
+
|
|
614
|
+
| Method | Description |
|
|
615
|
+
|--------|-------------|
|
|
616
|
+
| `health()` | Health check (returns boolean) |
|
|
617
|
+
| `info()` | Get agent hostname, version, platform |
|
|
618
|
+
| `uploadFeedback(bundle)` | Upload feedback bundle via multipart POST |
|
|
619
|
+
| `streamFeedback(events)` | Stream feedback events in live mode |
|
|
620
|
+
| `listBuilds()` | List available builds |
|
|
621
|
+
| `startBuild(platform)` | Start a build for the given platform |
|
|
622
|
+
| `getArtifactUrl(buildId)` | Get download URL for a build artifact |
|
|
623
|
+
| `voiceStatus()` | Get voice capability info |
|
|
624
|
+
| `transcribeVoice(audioUri)` | Send audio for transcription |
|
|
625
|
+
| `startTestSession()` | Start autonomous test session |
|
|
626
|
+
| `stopTestSession()` | Stop test session |
|
|
627
|
+
| `getTestSession()` | Get test session status + fixes |
|
|
628
|
+
| `setBaseUrl(url)` | Update connection URL |
|
|
629
|
+
| `setAuthToken(token)` | Update auth token |
|
|
630
|
+
|
|
631
|
+
### Components
|
|
632
|
+
|
|
633
|
+
| Component | Description |
|
|
634
|
+
|-----------|-------------|
|
|
635
|
+
| `FeedbackModal` | Modal with mode selector, commentary, screenshots, voice |
|
|
636
|
+
| `FloatingButton` | Draggable overlay button to trigger feedback |
|
|
637
|
+
| `YaverConnectionScreen` | Full-screen device discovery and connection UI |
|
|
638
|
+
|
|
639
|
+
### Helpers
|
|
640
|
+
|
|
641
|
+
| Function | Description |
|
|
642
|
+
|----------|-------------|
|
|
643
|
+
| `captureScreenshot()` | Capture the current screen (requires `react-native-view-shot`) |
|
|
644
|
+
| `startAudioRecording()` | Start recording a voice note |
|
|
645
|
+
| `stopAudioRecording()` | Stop recording, returns `{ path, duration }` |
|
|
646
|
+
| `uploadFeedback(url, token, bundle)` | Upload a feedback bundle to the agent |
|
|
647
|
+
|
|
648
|
+
## Agent Endpoints
|
|
649
|
+
|
|
650
|
+
The SDK communicates with these agent HTTP endpoints:
|
|
651
|
+
|
|
652
|
+
| Endpoint | Method | Description |
|
|
653
|
+
|----------|--------|-------------|
|
|
654
|
+
| `/health` | GET | Health check (no auth) |
|
|
655
|
+
| `/feedback` | POST | Upload feedback bundle (multipart) |
|
|
656
|
+
| `/feedback/stream` | POST | Stream live feedback events |
|
|
657
|
+
| `/blackbox/events` | POST | Batch stream Black Box events |
|
|
658
|
+
| `/blackbox/subscribe` | GET | SSE live log stream |
|
|
659
|
+
| `/blackbox/context` | GET | Get generated prompt context |
|
|
660
|
+
| `/builds` | GET/POST | List or start builds |
|
|
661
|
+
| `/voice/status` | GET | Voice capability info |
|
|
662
|
+
| `/voice/transcribe` | POST | Send audio for transcription |
|
|
663
|
+
| `/test-app/start` | POST | Start autonomous test session |
|
|
664
|
+
| `/test-app/stop` | POST | Stop test session |
|
|
665
|
+
| `/test-app/status` | GET | Test session status + fixes |
|
|
666
|
+
|
|
667
|
+
## Architecture
|
|
668
|
+
|
|
669
|
+
```
|
|
670
|
+
┌──────────────────┐ HTTP (Bearer auth) ┌──────────────────┐
|
|
671
|
+
│ Your App │────────────────────────────►│ Yaver Agent │
|
|
672
|
+
│ + Feedback SDK │ feedback, blackbox events │ (Go CLI) │
|
|
673
|
+
│ + BlackBox │ screenshots, voice, video │ on your machine │
|
|
674
|
+
│ │◄────────────────────────────│ │
|
|
675
|
+
│ │ fixes, build status, voice │ runs AI agent │
|
|
676
|
+
└──────────────────┘ └──────────────────┘
|
|
677
|
+
│ │
|
|
678
|
+
│ Auth only │ Auth only
|
|
679
|
+
▼ ▼
|
|
680
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
681
|
+
│ Convex Backend │
|
|
682
|
+
│ Token validation + device registry (no task data stored) │
|
|
683
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
All feedback data flows P2P between your app and the agent. Convex handles only auth and device discovery.
|
|
687
|
+
|
|
688
|
+
## License
|
|
689
|
+
|
|
690
|
+
MIT
|