yaver-feedback-react-native 0.7.4 → 0.7.6
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/FeedbackModal.js +13 -0
- package/dist/P2PClient.js +63 -28
- package/dist/YaverFeedback.js +37 -3
- package/package.json +1 -1
- package/src/FeedbackModal.tsx +14 -0
- package/src/P2PClient.ts +66 -28
- package/src/YaverFeedback.ts +37 -3
package/dist/FeedbackModal.js
CHANGED
|
@@ -58,9 +58,22 @@ const FeedbackModal = () => {
|
|
|
58
58
|
setAction('idle');
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
|
+
// Agent streams build / compile progress through the BlackBox
|
|
62
|
+
// SSE command channel as `command: "status"`; YaverFeedback re-emits
|
|
63
|
+
// it as `yaverFeedback:status`. Show the most recent message in the
|
|
64
|
+
// toast so a multi-second rebuild feels like "working" instead of
|
|
65
|
+
// "stuck".
|
|
66
|
+
const statusSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:status', (payload) => {
|
|
67
|
+
if (!mountedRef.current)
|
|
68
|
+
return;
|
|
69
|
+
const msg = payload?.message || payload?.phase || '';
|
|
70
|
+
if (msg)
|
|
71
|
+
setToast(msg);
|
|
72
|
+
});
|
|
61
73
|
return () => {
|
|
62
74
|
mountedRef.current = false;
|
|
63
75
|
sub.remove();
|
|
76
|
+
statusSub.remove();
|
|
64
77
|
};
|
|
65
78
|
}, []);
|
|
66
79
|
const closeSoon = (0, react_1.useCallback)((delayMs = 1200) => {
|
package/dist/P2PClient.js
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.P2PClient = void 0;
|
|
4
4
|
const react_native_1 = require("react-native");
|
|
5
|
+
/**
|
|
6
|
+
* Translate a raw Go-agent error into something a user can act on.
|
|
7
|
+
*
|
|
8
|
+
* The agent surfaces Go's low-level error text verbatim inside JSON,
|
|
9
|
+
* e.g. `Get "http://127.0.0.1:8081/reload": dial tcp 127.0.0.1:8081:
|
|
10
|
+
* connect: connection refused`. That's accurate but unreadable for a
|
|
11
|
+
* phone user and — more importantly — leads them to think the SDK is
|
|
12
|
+
* misconfigured. The real cause is almost always "no dev server
|
|
13
|
+
* running on the host machine".
|
|
14
|
+
*/
|
|
15
|
+
function friendlyReloadError(status, body) {
|
|
16
|
+
const lower = body.toLowerCase();
|
|
17
|
+
if (/connection refused|econnrefused/.test(lower) &&
|
|
18
|
+
/127\.0\.0\.1|localhost/.test(lower)) {
|
|
19
|
+
return ('No dev server running on your machine. ' +
|
|
20
|
+
'Start Metro with `yaver dev start` or use "Screenshot & Fix" instead.');
|
|
21
|
+
}
|
|
22
|
+
if (/no dev server/.test(lower) || /not running/.test(lower)) {
|
|
23
|
+
return 'No dev server running on your machine. Start Metro first.';
|
|
24
|
+
}
|
|
25
|
+
if (status === 403)
|
|
26
|
+
return 'Agent rejected the session — please sign in again.';
|
|
27
|
+
if (status === 401)
|
|
28
|
+
return 'Agent rejected the session — please sign in again.';
|
|
29
|
+
if (status >= 500)
|
|
30
|
+
return 'Agent hit an internal error while reloading. Check `yaver logs`.';
|
|
31
|
+
return `Reload failed (${status}).`;
|
|
32
|
+
}
|
|
5
33
|
/**
|
|
6
34
|
* Lightweight P2P HTTP client for communicating with a Yaver agent.
|
|
7
35
|
*
|
|
@@ -180,39 +208,46 @@ class P2PClient {
|
|
|
180
208
|
* via the BlackBox command channel.
|
|
181
209
|
* @param mode - "dev" for hot reload, "bundle" for native bundle rebuild
|
|
182
210
|
*/
|
|
183
|
-
async reloadApp(mode = '
|
|
184
|
-
//
|
|
185
|
-
// Triggers Metro/Expo HMR synchronously and emits an SSE `reload` event
|
|
186
|
-
// on /dev/events that the FeedbackModal can subscribe to for progress.
|
|
211
|
+
async reloadApp(mode = 'bundle') {
|
|
212
|
+
// Default path: always rebuild a fresh Hermes bundle.
|
|
187
213
|
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
214
|
+
// Rationale: the SDK's common caller is a phone user who's not
|
|
215
|
+
// sitting at their Mac — they're doing vibe coding with an AI agent
|
|
216
|
+
// editing files on the Mac remotely, or they installed the app via
|
|
217
|
+
// TestFlight and there's no Metro running at all. A Metro-based
|
|
218
|
+
// reload would either fail (Metro offline) or — worse — serve a
|
|
219
|
+
// stale bundle because Metro can be slow to re-index fresh edits.
|
|
220
|
+
// Bundle mode always produces the correct bytecode from the current
|
|
221
|
+
// filesystem state, taking ~30–60 s, and hits /dev/reload-app which
|
|
222
|
+
// uses BlackBox SSE to push the fresh bundle URL to the device.
|
|
223
|
+
//
|
|
224
|
+
// Callers who know they want Metro HMR pass `mode='dev'` explicitly;
|
|
225
|
+
// we still honour that. Everything else defaults to bundle.
|
|
226
|
+
if (mode === 'dev') {
|
|
227
|
+
const primary = await fetch(`${this.baseUrl}/dev/reload`, {
|
|
201
228
|
method: 'POST',
|
|
202
|
-
headers: {
|
|
203
|
-
Authorization: `Bearer ${this.authToken}`,
|
|
204
|
-
'Content-Type': 'application/json',
|
|
205
|
-
},
|
|
206
|
-
body: JSON.stringify({ mode }),
|
|
229
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
207
230
|
});
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
throw new Error(`[P2PClient] Reload failed (${fallback.status}): ${text}`);
|
|
231
|
+
if (primary.ok) {
|
|
232
|
+
return primary.json().catch(() => ({ ok: true }));
|
|
211
233
|
}
|
|
212
|
-
|
|
234
|
+
// Dev mode failed — fall through to bundle rebuild below rather
|
|
235
|
+
// than surfacing the raw error, so the user never has to know
|
|
236
|
+
// Metro wasn't running.
|
|
237
|
+
}
|
|
238
|
+
const res = await fetch(`${this.baseUrl}/dev/reload-app`, {
|
|
239
|
+
method: 'POST',
|
|
240
|
+
headers: {
|
|
241
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
242
|
+
'Content-Type': 'application/json',
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify({ mode: 'bundle' }),
|
|
245
|
+
});
|
|
246
|
+
if (!res.ok) {
|
|
247
|
+
const text = await res.text().catch(() => '');
|
|
248
|
+
throw new Error(friendlyReloadError(res.status, text));
|
|
213
249
|
}
|
|
214
|
-
|
|
215
|
-
throw new Error(`[P2PClient] Reload failed (${primary.status}): ${text}`);
|
|
250
|
+
return res.json().catch(() => ({ ok: true }));
|
|
216
251
|
}
|
|
217
252
|
/**
|
|
218
253
|
* Open a vibing session on the connected agent. Vibing is the Yaver
|
package/dist/YaverFeedback.js
CHANGED
|
@@ -126,9 +126,13 @@ class YaverFeedback {
|
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
|
-
// Wire up BlackBox command handlers for reload signals from
|
|
130
|
-
//
|
|
131
|
-
//
|
|
129
|
+
// Wire up BlackBox command handlers for reload + status signals from
|
|
130
|
+
// the agent. Opens an SSE channel the agent uses to:
|
|
131
|
+
// - broadcast reload_bundle so the phone picks up a fresh Hermes
|
|
132
|
+
// bundle after a vibe-coding edit;
|
|
133
|
+
// - stream build/compile progress ("Compiling bundle…", "Pushing
|
|
134
|
+
// assets…", "Done") so the SDK can surface it like a normal
|
|
135
|
+
// "Working…" spinner instead of a silent 60-second freeze.
|
|
132
136
|
if (enabled) {
|
|
133
137
|
BlackBox_1.BlackBox.onCommand((cmd) => {
|
|
134
138
|
if (cmd.command === 'reload') {
|
|
@@ -149,7 +153,37 @@ class YaverFeedback {
|
|
|
149
153
|
YaverFeedback.defaultReloadBundle(bundleUrl, assetsUrl);
|
|
150
154
|
}
|
|
151
155
|
}
|
|
156
|
+
else if (cmd.command === 'status') {
|
|
157
|
+
// Pipe agent progress pings to the UI. The FeedbackModal
|
|
158
|
+
// subscribes to this event and renders the message while a
|
|
159
|
+
// reload / build is in flight.
|
|
160
|
+
const message = typeof cmd.data?.message === 'string'
|
|
161
|
+
? cmd.data.message
|
|
162
|
+
: '';
|
|
163
|
+
const phase = typeof cmd.data?.phase === 'string'
|
|
164
|
+
? cmd.data.phase
|
|
165
|
+
: '';
|
|
166
|
+
const { DeviceEventEmitter } = require('react-native');
|
|
167
|
+
DeviceEventEmitter.emit('yaverFeedback:status', {
|
|
168
|
+
message,
|
|
169
|
+
phase,
|
|
170
|
+
at: Date.now(),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
152
173
|
});
|
|
174
|
+
// Auto-open the command-stream SSE channel so `status` +
|
|
175
|
+
// `reload_bundle` messages from the agent actually reach us.
|
|
176
|
+
// Host apps can still call BlackBox.start(...) themselves to
|
|
177
|
+
// customise deviceId / appName, but they don't have to.
|
|
178
|
+
if (!BlackBox_1.BlackBox.isStreaming) {
|
|
179
|
+
try {
|
|
180
|
+
BlackBox_1.BlackBox.start();
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// BlackBox.start throws if agentUrl/token aren't ready yet;
|
|
184
|
+
// a later setAuthToken() / discoverAgent() tick will retry.
|
|
185
|
+
}
|
|
186
|
+
}
|
|
153
187
|
}
|
|
154
188
|
// NOTE: We intentionally do NOT hook ErrorUtils.setGlobalHandler().
|
|
155
189
|
// Sentry, Crashlytics, Bugsnag, and other tools all compete for that
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yaver-feedback-react-native",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"description": "Visual feedback SDK for Yaver — bug reports, screen recording, voice annotations, and local-first developer workflows",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/FeedbackModal.tsx
CHANGED
|
@@ -67,9 +67,23 @@ export const FeedbackModal: React.FC = () => {
|
|
|
67
67
|
setAction('idle');
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
|
+
// Agent streams build / compile progress through the BlackBox
|
|
71
|
+
// SSE command channel as `command: "status"`; YaverFeedback re-emits
|
|
72
|
+
// it as `yaverFeedback:status`. Show the most recent message in the
|
|
73
|
+
// toast so a multi-second rebuild feels like "working" instead of
|
|
74
|
+
// "stuck".
|
|
75
|
+
const statusSub = DeviceEventEmitter.addListener(
|
|
76
|
+
'yaverFeedback:status',
|
|
77
|
+
(payload: { message?: string; phase?: string }) => {
|
|
78
|
+
if (!mountedRef.current) return;
|
|
79
|
+
const msg = payload?.message || payload?.phase || '';
|
|
80
|
+
if (msg) setToast(msg);
|
|
81
|
+
},
|
|
82
|
+
);
|
|
70
83
|
return () => {
|
|
71
84
|
mountedRef.current = false;
|
|
72
85
|
sub.remove();
|
|
86
|
+
statusSub.remove();
|
|
73
87
|
};
|
|
74
88
|
}, []);
|
|
75
89
|
|
package/src/P2PClient.ts
CHANGED
|
@@ -7,6 +7,36 @@ export interface FeedbackEvent {
|
|
|
7
7
|
data: any;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Translate a raw Go-agent error into something a user can act on.
|
|
12
|
+
*
|
|
13
|
+
* The agent surfaces Go's low-level error text verbatim inside JSON,
|
|
14
|
+
* e.g. `Get "http://127.0.0.1:8081/reload": dial tcp 127.0.0.1:8081:
|
|
15
|
+
* connect: connection refused`. That's accurate but unreadable for a
|
|
16
|
+
* phone user and — more importantly — leads them to think the SDK is
|
|
17
|
+
* misconfigured. The real cause is almost always "no dev server
|
|
18
|
+
* running on the host machine".
|
|
19
|
+
*/
|
|
20
|
+
function friendlyReloadError(status: number, body: string): string {
|
|
21
|
+
const lower = body.toLowerCase();
|
|
22
|
+
if (
|
|
23
|
+
/connection refused|econnrefused/.test(lower) &&
|
|
24
|
+
/127\.0\.0\.1|localhost/.test(lower)
|
|
25
|
+
) {
|
|
26
|
+
return (
|
|
27
|
+
'No dev server running on your machine. ' +
|
|
28
|
+
'Start Metro with `yaver dev start` or use "Screenshot & Fix" instead.'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (/no dev server/.test(lower) || /not running/.test(lower)) {
|
|
32
|
+
return 'No dev server running on your machine. Start Metro first.';
|
|
33
|
+
}
|
|
34
|
+
if (status === 403) return 'Agent rejected the session — please sign in again.';
|
|
35
|
+
if (status === 401) return 'Agent rejected the session — please sign in again.';
|
|
36
|
+
if (status >= 500) return 'Agent hit an internal error while reloading. Check `yaver logs`.';
|
|
37
|
+
return `Reload failed (${status}).`;
|
|
38
|
+
}
|
|
39
|
+
|
|
10
40
|
/**
|
|
11
41
|
* Lightweight P2P HTTP client for communicating with a Yaver agent.
|
|
12
42
|
*
|
|
@@ -214,39 +244,47 @@ export class P2PClient {
|
|
|
214
244
|
* via the BlackBox command channel.
|
|
215
245
|
* @param mode - "dev" for hot reload, "bundle" for native bundle rebuild
|
|
216
246
|
*/
|
|
217
|
-
async reloadApp(mode: 'dev' | 'bundle' = '
|
|
218
|
-
//
|
|
219
|
-
// Triggers Metro/Expo HMR synchronously and emits an SSE `reload` event
|
|
220
|
-
// on /dev/events that the FeedbackModal can subscribe to for progress.
|
|
247
|
+
async reloadApp(mode: 'dev' | 'bundle' = 'bundle'): Promise<{ ok: boolean }> {
|
|
248
|
+
// Default path: always rebuild a fresh Hermes bundle.
|
|
221
249
|
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
250
|
+
// Rationale: the SDK's common caller is a phone user who's not
|
|
251
|
+
// sitting at their Mac — they're doing vibe coding with an AI agent
|
|
252
|
+
// editing files on the Mac remotely, or they installed the app via
|
|
253
|
+
// TestFlight and there's no Metro running at all. A Metro-based
|
|
254
|
+
// reload would either fail (Metro offline) or — worse — serve a
|
|
255
|
+
// stale bundle because Metro can be slow to re-index fresh edits.
|
|
256
|
+
// Bundle mode always produces the correct bytecode from the current
|
|
257
|
+
// filesystem state, taking ~30–60 s, and hits /dev/reload-app which
|
|
258
|
+
// uses BlackBox SSE to push the fresh bundle URL to the device.
|
|
259
|
+
//
|
|
260
|
+
// Callers who know they want Metro HMR pass `mode='dev'` explicitly;
|
|
261
|
+
// we still honour that. Everything else defaults to bundle.
|
|
262
|
+
if (mode === 'dev') {
|
|
263
|
+
const primary = await fetch(`${this.baseUrl}/dev/reload`, {
|
|
235
264
|
method: 'POST',
|
|
236
|
-
headers: {
|
|
237
|
-
Authorization: `Bearer ${this.authToken}`,
|
|
238
|
-
'Content-Type': 'application/json',
|
|
239
|
-
},
|
|
240
|
-
body: JSON.stringify({ mode }),
|
|
265
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
241
266
|
});
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
throw new Error(`[P2PClient] Reload failed (${fallback.status}): ${text}`);
|
|
267
|
+
if (primary.ok) {
|
|
268
|
+
return primary.json().catch(() => ({ ok: true }));
|
|
245
269
|
}
|
|
246
|
-
|
|
270
|
+
// Dev mode failed — fall through to bundle rebuild below rather
|
|
271
|
+
// than surfacing the raw error, so the user never has to know
|
|
272
|
+
// Metro wasn't running.
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const res = await fetch(`${this.baseUrl}/dev/reload-app`, {
|
|
276
|
+
method: 'POST',
|
|
277
|
+
headers: {
|
|
278
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
279
|
+
'Content-Type': 'application/json',
|
|
280
|
+
},
|
|
281
|
+
body: JSON.stringify({ mode: 'bundle' }),
|
|
282
|
+
});
|
|
283
|
+
if (!res.ok) {
|
|
284
|
+
const text = await res.text().catch(() => '');
|
|
285
|
+
throw new Error(friendlyReloadError(res.status, text));
|
|
247
286
|
}
|
|
248
|
-
|
|
249
|
-
throw new Error(`[P2PClient] Reload failed (${primary.status}): ${text}`);
|
|
287
|
+
return res.json().catch(() => ({ ok: true }));
|
|
250
288
|
}
|
|
251
289
|
|
|
252
290
|
/**
|
package/src/YaverFeedback.ts
CHANGED
|
@@ -142,9 +142,13 @@ export class YaverFeedback {
|
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
// Wire up BlackBox command handlers for reload signals from
|
|
146
|
-
//
|
|
147
|
-
//
|
|
145
|
+
// Wire up BlackBox command handlers for reload + status signals from
|
|
146
|
+
// the agent. Opens an SSE channel the agent uses to:
|
|
147
|
+
// - broadcast reload_bundle so the phone picks up a fresh Hermes
|
|
148
|
+
// bundle after a vibe-coding edit;
|
|
149
|
+
// - stream build/compile progress ("Compiling bundle…", "Pushing
|
|
150
|
+
// assets…", "Done") so the SDK can surface it like a normal
|
|
151
|
+
// "Working…" spinner instead of a silent 60-second freeze.
|
|
148
152
|
if (enabled) {
|
|
149
153
|
BlackBox.onCommand((cmd) => {
|
|
150
154
|
if (cmd.command === 'reload') {
|
|
@@ -161,8 +165,38 @@ export class YaverFeedback {
|
|
|
161
165
|
} else {
|
|
162
166
|
YaverFeedback.defaultReloadBundle(bundleUrl, assetsUrl);
|
|
163
167
|
}
|
|
168
|
+
} else if (cmd.command === 'status') {
|
|
169
|
+
// Pipe agent progress pings to the UI. The FeedbackModal
|
|
170
|
+
// subscribes to this event and renders the message while a
|
|
171
|
+
// reload / build is in flight.
|
|
172
|
+
const message =
|
|
173
|
+
typeof cmd.data?.message === 'string'
|
|
174
|
+
? (cmd.data.message as string)
|
|
175
|
+
: '';
|
|
176
|
+
const phase =
|
|
177
|
+
typeof cmd.data?.phase === 'string'
|
|
178
|
+
? (cmd.data.phase as string)
|
|
179
|
+
: '';
|
|
180
|
+
const { DeviceEventEmitter } = require('react-native');
|
|
181
|
+
DeviceEventEmitter.emit('yaverFeedback:status', {
|
|
182
|
+
message,
|
|
183
|
+
phase,
|
|
184
|
+
at: Date.now(),
|
|
185
|
+
});
|
|
164
186
|
}
|
|
165
187
|
});
|
|
188
|
+
// Auto-open the command-stream SSE channel so `status` +
|
|
189
|
+
// `reload_bundle` messages from the agent actually reach us.
|
|
190
|
+
// Host apps can still call BlackBox.start(...) themselves to
|
|
191
|
+
// customise deviceId / appName, but they don't have to.
|
|
192
|
+
if (!BlackBox.isStreaming) {
|
|
193
|
+
try {
|
|
194
|
+
BlackBox.start();
|
|
195
|
+
} catch {
|
|
196
|
+
// BlackBox.start throws if agentUrl/token aren't ready yet;
|
|
197
|
+
// a later setAuthToken() / discoverAgent() tick will retry.
|
|
198
|
+
}
|
|
199
|
+
}
|
|
166
200
|
}
|
|
167
201
|
|
|
168
202
|
// NOTE: We intentionally do NOT hook ErrorUtils.setGlobalHandler().
|