yaver-feedback-react-native 0.8.4 → 0.8.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/AuthOverlay.js +45 -18
- package/dist/FeedbackModal.js +281 -0
- package/dist/P2PClient.d.ts +23 -2
- package/dist/P2PClient.js +53 -4
- package/dist/QuickActionIcon.js +27 -2
- package/dist/YaverFeedback.d.ts +13 -0
- package/dist/YaverFeedback.js +78 -34
- package/dist/__tests__/AuthDevices.test.d.ts +1 -0
- package/dist/__tests__/AuthDevices.test.js +82 -0
- package/dist/types.d.ts +21 -0
- package/package.json +2 -2
- package/src/AuthOverlay.tsx +48 -19
- package/src/FeedbackModal.tsx +320 -0
- package/src/P2PClient.ts +61 -5
- package/src/QuickActionIcon.tsx +37 -2
- package/src/YaverFeedback.ts +83 -34
- package/src/__tests__/AuthDevices.test.ts +93 -0
- package/src/types.ts +22 -0
package/dist/AuthOverlay.js
CHANGED
|
@@ -61,6 +61,31 @@ const AuthOverlay = () => {
|
|
|
61
61
|
const [pickerVisible, setPickerVisible] = (0, react_1.useState)(false);
|
|
62
62
|
const [token, setToken] = (0, react_1.useState)(null);
|
|
63
63
|
const [pendingInviteCode, setPendingInviteCode] = (0, react_1.useState)(null);
|
|
64
|
+
const activeOverlayRef = (0, react_1.useRef)('none');
|
|
65
|
+
const openLogin = (0, react_1.useCallback)(() => {
|
|
66
|
+
activeOverlayRef.current = 'login';
|
|
67
|
+
setGuestVisible(false);
|
|
68
|
+
setPickerVisible(false);
|
|
69
|
+
setLoginVisible(true);
|
|
70
|
+
}, []);
|
|
71
|
+
const openGuest = (0, react_1.useCallback)(() => {
|
|
72
|
+
activeOverlayRef.current = 'guest';
|
|
73
|
+
setLoginVisible(false);
|
|
74
|
+
setPickerVisible(false);
|
|
75
|
+
setGuestVisible(true);
|
|
76
|
+
}, []);
|
|
77
|
+
const openPicker = (0, react_1.useCallback)(() => {
|
|
78
|
+
activeOverlayRef.current = 'picker';
|
|
79
|
+
setLoginVisible(false);
|
|
80
|
+
setGuestVisible(false);
|
|
81
|
+
setPickerVisible(true);
|
|
82
|
+
}, []);
|
|
83
|
+
const closeAll = (0, react_1.useCallback)(() => {
|
|
84
|
+
activeOverlayRef.current = 'none';
|
|
85
|
+
setLoginVisible(false);
|
|
86
|
+
setGuestVisible(false);
|
|
87
|
+
setPickerVisible(false);
|
|
88
|
+
}, []);
|
|
64
89
|
(0, react_1.useEffect)(() => {
|
|
65
90
|
let mounted = true;
|
|
66
91
|
(async () => {
|
|
@@ -68,65 +93,67 @@ const AuthOverlay = () => {
|
|
|
68
93
|
if (mounted && cached)
|
|
69
94
|
setToken(cached);
|
|
70
95
|
})();
|
|
71
|
-
const loginSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startLogin', () =>
|
|
96
|
+
const loginSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startLogin', () => {
|
|
97
|
+
if (activeOverlayRef.current !== 'none')
|
|
98
|
+
return;
|
|
99
|
+
openLogin();
|
|
100
|
+
});
|
|
72
101
|
const pickerSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startMachinePicker', async () => {
|
|
102
|
+
if (activeOverlayRef.current !== 'none')
|
|
103
|
+
return;
|
|
73
104
|
const cached = await (0, auth_1.getToken)();
|
|
74
105
|
if (cached)
|
|
75
106
|
setToken(cached);
|
|
76
107
|
if (cached)
|
|
77
|
-
|
|
108
|
+
openPicker();
|
|
78
109
|
});
|
|
79
110
|
return () => {
|
|
80
111
|
mounted = false;
|
|
81
112
|
loginSub.remove();
|
|
82
113
|
pickerSub.remove();
|
|
83
114
|
};
|
|
84
|
-
}, []);
|
|
115
|
+
}, [openLogin, openPicker]);
|
|
85
116
|
const continueAfterAuth = async (newToken, inviteCode) => {
|
|
86
117
|
setToken(newToken);
|
|
87
118
|
await YaverFeedback_1.YaverFeedback.setAuthToken(newToken);
|
|
88
119
|
const devices = await (0, auth_1.listReachableDevices)(newToken).catch(() => ({ owned: [], shared: [] }));
|
|
89
|
-
setLoginVisible(false);
|
|
90
120
|
const cleanedInviteCode = (inviteCode ?? '').trim().toUpperCase();
|
|
91
121
|
if (cleanedInviteCode) {
|
|
92
122
|
setPendingInviteCode(cleanedInviteCode);
|
|
93
|
-
|
|
123
|
+
openGuest();
|
|
94
124
|
return;
|
|
95
125
|
}
|
|
96
126
|
if (devices.owned.length === 0 && devices.shared.length === 0) {
|
|
97
|
-
|
|
127
|
+
openGuest();
|
|
98
128
|
return;
|
|
99
129
|
}
|
|
100
|
-
|
|
130
|
+
openPicker();
|
|
101
131
|
};
|
|
102
132
|
const handleLoggedIn = async (newToken, opts) => {
|
|
103
133
|
await continueAfterAuth(newToken, opts?.inviteCode);
|
|
104
134
|
};
|
|
105
135
|
const handleDevicePicked = async (device) => {
|
|
106
136
|
await YaverFeedback_1.YaverFeedback.setPreferredDevice(device.deviceId);
|
|
107
|
-
|
|
108
|
-
setGuestVisible(false);
|
|
137
|
+
closeAll();
|
|
109
138
|
// Continue straight into the feedback flow the user originally triggered.
|
|
110
139
|
react_native_1.DeviceEventEmitter.emit('yaverFeedback:startReport');
|
|
111
140
|
};
|
|
112
141
|
return (<>
|
|
113
|
-
<react_native_1.Modal visible={loginVisible} animationType="slide" presentationStyle="fullScreen" onRequestClose={
|
|
114
|
-
<LoginScreen_1.YaverLoginScreen onLoggedIn={handleLoggedIn} onCancel={
|
|
142
|
+
<react_native_1.Modal visible={loginVisible} animationType="slide" presentationStyle="fullScreen" onRequestClose={closeAll}>
|
|
143
|
+
<LoginScreen_1.YaverLoginScreen onLoggedIn={handleLoggedIn} onCancel={closeAll} initialInviteCode={pendingInviteCode ?? YaverFeedback_1.YaverFeedback.getConfig()?.guestInviteCode}/>
|
|
115
144
|
</react_native_1.Modal>
|
|
116
145
|
|
|
117
|
-
<react_native_1.Modal visible={pickerVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={
|
|
118
|
-
{token && (<MachinePickerScreen_1.YaverMachinePickerScreen token={token} currentDeviceId={YaverFeedback_1.YaverFeedback.getConfig()?.preferredDeviceId} onPick={handleDevicePicked} onCancel={
|
|
146
|
+
<react_native_1.Modal visible={pickerVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={closeAll}>
|
|
147
|
+
{token && (<MachinePickerScreen_1.YaverMachinePickerScreen token={token} currentDeviceId={YaverFeedback_1.YaverFeedback.getConfig()?.preferredDeviceId} onPick={handleDevicePicked} onCancel={closeAll}/>)}
|
|
119
148
|
</react_native_1.Modal>
|
|
120
149
|
|
|
121
|
-
<react_native_1.Modal visible={guestVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={
|
|
150
|
+
<react_native_1.Modal visible={guestVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={closeAll}>
|
|
122
151
|
{token && (<GuestOnboardingScreen_1.YaverGuestOnboardingScreen token={token} initialInviteCode={pendingInviteCode ?? YaverFeedback_1.YaverFeedback.getConfig()?.guestInviteCode} onContinue={() => {
|
|
123
|
-
setGuestVisible(false);
|
|
124
152
|
setPendingInviteCode(null);
|
|
125
|
-
|
|
153
|
+
openPicker();
|
|
126
154
|
}} onCancel={() => {
|
|
127
|
-
setGuestVisible(false);
|
|
128
155
|
setPendingInviteCode(null);
|
|
129
|
-
|
|
156
|
+
openPicker();
|
|
130
157
|
}}/>)}
|
|
131
158
|
</react_native_1.Modal>
|
|
132
159
|
</>);
|
package/dist/FeedbackModal.js
CHANGED
|
@@ -54,6 +54,7 @@ const FeedbackModal = () => {
|
|
|
54
54
|
// is our guaranteed UI for bringing the icon back — we surface a
|
|
55
55
|
// small "Show quick icon" row when this is true.
|
|
56
56
|
const [quickIconHidden, setQuickIconHidden] = (0, react_1.useState)(false);
|
|
57
|
+
const [runnerAuthModal, setRunnerAuthModal] = (0, react_1.useState)(null);
|
|
57
58
|
// Vibing-input mode: same expand-on-tap pattern as email login.
|
|
58
59
|
// Tap "Vibing" once → the button reveals an input + Send; that lets
|
|
59
60
|
// the user say WHAT they want to vibe on instead of firing a canned
|
|
@@ -666,6 +667,29 @@ const FeedbackModal = () => {
|
|
|
666
667
|
{/* Screenshot & Fix */}
|
|
667
668
|
<ActionRow label={action === 'capturing' ? 'Working…' : 'Screenshot & Fix'} tint="#22c55e" onPress={handleScreenshotAndFix} disabled={busy} busy={action === 'capturing'}/>
|
|
668
669
|
|
|
670
|
+
{/* Remote sign-in buttons — trigger codex/claude device-auth
|
|
671
|
+
on the selected agent without leaving the app. Opens a
|
|
672
|
+
small native modal showing the verification URL + 8-char
|
|
673
|
+
code the user enters in any browser. No API keys. */}
|
|
674
|
+
<react_native_1.View style={runnerAuthRowStyles.container}>
|
|
675
|
+
<react_native_1.Pressable onPress={() => setRunnerAuthModal('codex')} disabled={busy} style={({ pressed }) => [
|
|
676
|
+
runnerAuthRowStyles.button,
|
|
677
|
+
pressed && runnerAuthRowStyles.buttonPressed,
|
|
678
|
+
busy && runnerAuthRowStyles.buttonDisabled,
|
|
679
|
+
]} accessibilityRole="button" accessibilityLabel="Remote sign-in Codex">
|
|
680
|
+
<react_native_1.Text style={runnerAuthRowStyles.buttonLabel}>Remote sign-in</react_native_1.Text>
|
|
681
|
+
<react_native_1.Text style={runnerAuthRowStyles.buttonName}>Codex</react_native_1.Text>
|
|
682
|
+
</react_native_1.Pressable>
|
|
683
|
+
<react_native_1.Pressable onPress={() => setRunnerAuthModal('claude')} disabled={busy} style={({ pressed }) => [
|
|
684
|
+
runnerAuthRowStyles.button,
|
|
685
|
+
pressed && runnerAuthRowStyles.buttonPressed,
|
|
686
|
+
busy && runnerAuthRowStyles.buttonDisabled,
|
|
687
|
+
]} accessibilityRole="button" accessibilityLabel="Remote sign-in Claude">
|
|
688
|
+
<react_native_1.Text style={runnerAuthRowStyles.buttonLabel}>Remote sign-in</react_native_1.Text>
|
|
689
|
+
<react_native_1.Text style={runnerAuthRowStyles.buttonName}>Claude</react_native_1.Text>
|
|
690
|
+
</react_native_1.Pressable>
|
|
691
|
+
</react_native_1.View>
|
|
692
|
+
|
|
669
693
|
{progress !== null && (<react_native_1.View style={styles.progressTrack}>
|
|
670
694
|
<react_native_1.View style={[
|
|
671
695
|
styles.progressFill,
|
|
@@ -686,6 +710,7 @@ const FeedbackModal = () => {
|
|
|
686
710
|
</react_native_1.KeyboardAvoidingView>
|
|
687
711
|
</react_native_1.Pressable>
|
|
688
712
|
</react_native_1.Modal>)}
|
|
713
|
+
{runnerAuthModal ? (<RunnerAuthNativeModal runner={runnerAuthModal} onClose={() => setRunnerAuthModal(null)}/>) : null}
|
|
689
714
|
</>);
|
|
690
715
|
};
|
|
691
716
|
exports.FeedbackModal = FeedbackModal;
|
|
@@ -1014,3 +1039,259 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
1014
1039
|
opacity: 0.7,
|
|
1015
1040
|
},
|
|
1016
1041
|
});
|
|
1042
|
+
/**
|
|
1043
|
+
* Minimal native modal for the codex/claude remote sign-in flow. Opens
|
|
1044
|
+
* the device-auth session on the connected agent, surfaces the
|
|
1045
|
+
* verification URL + one-time code, polls every 1.5 s, and turns green
|
|
1046
|
+
* the moment the CLI writes its auth.json. No API keys, no SSH.
|
|
1047
|
+
*/
|
|
1048
|
+
const RunnerAuthNativeModal = ({ runner, onClose }) => {
|
|
1049
|
+
const [session, setSession] = (0, react_1.useState)(null);
|
|
1050
|
+
const [startError, setStartError] = (0, react_1.useState)(null);
|
|
1051
|
+
const [copied, setCopied] = (0, react_1.useState)(false);
|
|
1052
|
+
const startedRef = (0, react_1.useRef)(false);
|
|
1053
|
+
(0, react_1.useEffect)(() => {
|
|
1054
|
+
if (startedRef.current)
|
|
1055
|
+
return;
|
|
1056
|
+
startedRef.current = true;
|
|
1057
|
+
(async () => {
|
|
1058
|
+
try {
|
|
1059
|
+
const s = await YaverFeedback_1.YaverFeedback.startRunnerBrowserAuth(runner);
|
|
1060
|
+
setSession(s);
|
|
1061
|
+
}
|
|
1062
|
+
catch (err) {
|
|
1063
|
+
setStartError(err instanceof Error ? err.message : String(err));
|
|
1064
|
+
}
|
|
1065
|
+
})();
|
|
1066
|
+
}, [runner]);
|
|
1067
|
+
(0, react_1.useEffect)(() => {
|
|
1068
|
+
if (!session)
|
|
1069
|
+
return;
|
|
1070
|
+
if (['completed', 'failed', 'cancelled'].includes(session.status))
|
|
1071
|
+
return;
|
|
1072
|
+
const iv = setInterval(async () => {
|
|
1073
|
+
try {
|
|
1074
|
+
const s = await YaverFeedback_1.YaverFeedback.getRunnerBrowserAuthStatus(session.id);
|
|
1075
|
+
setSession(s);
|
|
1076
|
+
}
|
|
1077
|
+
catch {
|
|
1078
|
+
// keep polling
|
|
1079
|
+
}
|
|
1080
|
+
}, 1500);
|
|
1081
|
+
return () => clearInterval(iv);
|
|
1082
|
+
}, [session?.id, session?.status]);
|
|
1083
|
+
const terminal = session && ['completed', 'failed', 'cancelled'].includes(session.status);
|
|
1084
|
+
const runnerLabel = runner === 'codex' ? 'OpenAI Codex' : runner === 'claude' ? 'Claude Code' : runner;
|
|
1085
|
+
const handleClose = () => {
|
|
1086
|
+
if (session && !terminal) {
|
|
1087
|
+
YaverFeedback_1.YaverFeedback.cancelRunnerBrowserAuth(session.id).catch(() => { });
|
|
1088
|
+
}
|
|
1089
|
+
onClose();
|
|
1090
|
+
};
|
|
1091
|
+
const copyCode = () => {
|
|
1092
|
+
if (!session?.code)
|
|
1093
|
+
return;
|
|
1094
|
+
try {
|
|
1095
|
+
// Avoid a hard Clipboard dep — host app can polyfill.
|
|
1096
|
+
const Clipboard = require('react-native').Clipboard;
|
|
1097
|
+
if (Clipboard?.setString) {
|
|
1098
|
+
Clipboard.setString(session.code);
|
|
1099
|
+
setCopied(true);
|
|
1100
|
+
setTimeout(() => setCopied(false), 1500);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
catch {
|
|
1104
|
+
// best-effort — code is visible on screen regardless
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
const openUrl = () => {
|
|
1108
|
+
if (!session?.openUrl)
|
|
1109
|
+
return;
|
|
1110
|
+
try {
|
|
1111
|
+
const { Linking } = require('react-native');
|
|
1112
|
+
Linking.openURL(session.openUrl).catch(() => { });
|
|
1113
|
+
}
|
|
1114
|
+
catch {
|
|
1115
|
+
/* ignore */
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
return (<react_native_1.Modal visible={true} transparent animationType="fade" onRequestClose={handleClose}>
|
|
1119
|
+
<react_native_1.View style={runnerAuthModalStyles.overlay}>
|
|
1120
|
+
<react_native_1.View style={runnerAuthModalStyles.card}>
|
|
1121
|
+
<react_native_1.View style={runnerAuthModalStyles.header}>
|
|
1122
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
1123
|
+
<react_native_1.Text style={runnerAuthModalStyles.title}>Sign in to {runnerLabel}</react_native_1.Text>
|
|
1124
|
+
<react_native_1.Text style={runnerAuthModalStyles.subtitle}>
|
|
1125
|
+
Opens a one-time URL + code. Enter it in any browser.
|
|
1126
|
+
</react_native_1.Text>
|
|
1127
|
+
</react_native_1.View>
|
|
1128
|
+
<react_native_1.Pressable onPress={handleClose} hitSlop={10}>
|
|
1129
|
+
<react_native_1.Text style={runnerAuthModalStyles.close}>×</react_native_1.Text>
|
|
1130
|
+
</react_native_1.Pressable>
|
|
1131
|
+
</react_native_1.View>
|
|
1132
|
+
|
|
1133
|
+
{startError ? (<react_native_1.View style={runnerAuthModalStyles.errorBox}>
|
|
1134
|
+
<react_native_1.Text style={runnerAuthModalStyles.errorTitle}>Couldn't start</react_native_1.Text>
|
|
1135
|
+
<react_native_1.Text style={runnerAuthModalStyles.errorBody}>{startError}</react_native_1.Text>
|
|
1136
|
+
</react_native_1.View>) : !session ? (<react_native_1.Text style={runnerAuthModalStyles.dim}>
|
|
1137
|
+
Starting the sign-in flow on the remote machine…
|
|
1138
|
+
</react_native_1.Text>) : session.status === 'completed' ? (<react_native_1.View style={runnerAuthModalStyles.successBox}>
|
|
1139
|
+
<react_native_1.Text style={runnerAuthModalStyles.successTitle}>✓ Signed in</react_native_1.Text>
|
|
1140
|
+
<react_native_1.Text style={runnerAuthModalStyles.successBody}>
|
|
1141
|
+
{session.detail || 'Auth stored on the remote machine.'}
|
|
1142
|
+
</react_native_1.Text>
|
|
1143
|
+
</react_native_1.View>) : session.status === 'failed' || session.status === 'cancelled' ? (<react_native_1.View style={runnerAuthModalStyles.errorBox}>
|
|
1144
|
+
<react_native_1.Text style={runnerAuthModalStyles.errorTitle}>
|
|
1145
|
+
{session.status === 'cancelled' ? 'Cancelled' : 'Failed'}
|
|
1146
|
+
</react_native_1.Text>
|
|
1147
|
+
<react_native_1.Text style={runnerAuthModalStyles.errorBody}>
|
|
1148
|
+
{session.error || session.detail || 'The CLI exited before sign-in completed.'}
|
|
1149
|
+
</react_native_1.Text>
|
|
1150
|
+
</react_native_1.View>) : (<react_native_1.View>
|
|
1151
|
+
{session.openUrl ? (<react_native_1.Pressable onPress={openUrl} style={runnerAuthModalStyles.urlBox}>
|
|
1152
|
+
<react_native_1.Text style={runnerAuthModalStyles.urlText} numberOfLines={2}>
|
|
1153
|
+
↗ {session.openUrl}
|
|
1154
|
+
</react_native_1.Text>
|
|
1155
|
+
</react_native_1.Pressable>) : (<react_native_1.Text style={runnerAuthModalStyles.dim}>
|
|
1156
|
+
Waiting for verification URL from the remote CLI…
|
|
1157
|
+
</react_native_1.Text>)}
|
|
1158
|
+
{session.code ? (<react_native_1.View style={{ marginTop: 12 }}>
|
|
1159
|
+
<react_native_1.Text style={runnerAuthModalStyles.codeLabel}>ENTER THIS CODE</react_native_1.Text>
|
|
1160
|
+
<react_native_1.Pressable onPress={copyCode} style={runnerAuthModalStyles.codeBox}>
|
|
1161
|
+
<react_native_1.Text style={runnerAuthModalStyles.codeText}>{session.code}</react_native_1.Text>
|
|
1162
|
+
<react_native_1.Text style={runnerAuthModalStyles.codeHint}>
|
|
1163
|
+
{copied ? 'copied' : 'tap to copy'}
|
|
1164
|
+
</react_native_1.Text>
|
|
1165
|
+
</react_native_1.Pressable>
|
|
1166
|
+
</react_native_1.View>) : null}
|
|
1167
|
+
<react_native_1.Text style={runnerAuthModalStyles.phishingHint}>
|
|
1168
|
+
Device codes are a common phishing target. Never share this code. This dialog
|
|
1169
|
+
turns green automatically once sign-in completes.
|
|
1170
|
+
</react_native_1.Text>
|
|
1171
|
+
</react_native_1.View>)}
|
|
1172
|
+
</react_native_1.View>
|
|
1173
|
+
</react_native_1.View>
|
|
1174
|
+
</react_native_1.Modal>);
|
|
1175
|
+
};
|
|
1176
|
+
const runnerAuthRowStyles = react_native_1.StyleSheet.create({
|
|
1177
|
+
container: {
|
|
1178
|
+
flexDirection: 'row',
|
|
1179
|
+
gap: 8,
|
|
1180
|
+
marginTop: 8,
|
|
1181
|
+
flexWrap: 'wrap',
|
|
1182
|
+
},
|
|
1183
|
+
button: {
|
|
1184
|
+
flexGrow: 1,
|
|
1185
|
+
flexBasis: 0,
|
|
1186
|
+
minWidth: 120,
|
|
1187
|
+
paddingHorizontal: 12,
|
|
1188
|
+
paddingVertical: 10,
|
|
1189
|
+
borderRadius: 10,
|
|
1190
|
+
borderWidth: 1,
|
|
1191
|
+
borderColor: 'rgba(148,163,184,0.22)',
|
|
1192
|
+
backgroundColor: 'rgba(15,23,42,0.6)',
|
|
1193
|
+
},
|
|
1194
|
+
buttonPressed: { opacity: 0.7 },
|
|
1195
|
+
buttonDisabled: { opacity: 0.4 },
|
|
1196
|
+
buttonLabel: {
|
|
1197
|
+
fontSize: 10,
|
|
1198
|
+
color: '#94a3b8',
|
|
1199
|
+
textTransform: 'uppercase',
|
|
1200
|
+
letterSpacing: 0.8,
|
|
1201
|
+
},
|
|
1202
|
+
buttonName: {
|
|
1203
|
+
marginTop: 2,
|
|
1204
|
+
fontSize: 14,
|
|
1205
|
+
fontWeight: '600',
|
|
1206
|
+
color: '#f1f5f9',
|
|
1207
|
+
},
|
|
1208
|
+
});
|
|
1209
|
+
const runnerAuthModalStyles = react_native_1.StyleSheet.create({
|
|
1210
|
+
overlay: {
|
|
1211
|
+
flex: 1,
|
|
1212
|
+
justifyContent: 'center',
|
|
1213
|
+
alignItems: 'center',
|
|
1214
|
+
backgroundColor: 'rgba(2,6,23,0.75)',
|
|
1215
|
+
padding: 16,
|
|
1216
|
+
},
|
|
1217
|
+
card: {
|
|
1218
|
+
width: '100%',
|
|
1219
|
+
maxWidth: 420,
|
|
1220
|
+
backgroundColor: '#0f172a',
|
|
1221
|
+
borderRadius: 14,
|
|
1222
|
+
borderWidth: 1,
|
|
1223
|
+
borderColor: 'rgba(148,163,184,0.18)',
|
|
1224
|
+
padding: 18,
|
|
1225
|
+
},
|
|
1226
|
+
header: {
|
|
1227
|
+
flexDirection: 'row',
|
|
1228
|
+
alignItems: 'flex-start',
|
|
1229
|
+
marginBottom: 12,
|
|
1230
|
+
},
|
|
1231
|
+
title: { color: '#f1f5f9', fontSize: 16, fontWeight: '600' },
|
|
1232
|
+
subtitle: { color: '#94a3b8', fontSize: 11, marginTop: 2 },
|
|
1233
|
+
close: { color: '#94a3b8', fontSize: 22, lineHeight: 22, paddingHorizontal: 4 },
|
|
1234
|
+
dim: {
|
|
1235
|
+
color: '#94a3b8',
|
|
1236
|
+
fontSize: 12,
|
|
1237
|
+
padding: 12,
|
|
1238
|
+
borderRadius: 10,
|
|
1239
|
+
borderWidth: 1,
|
|
1240
|
+
borderColor: 'rgba(148,163,184,0.2)',
|
|
1241
|
+
backgroundColor: 'rgba(15,23,42,0.6)',
|
|
1242
|
+
},
|
|
1243
|
+
errorBox: {
|
|
1244
|
+
padding: 12,
|
|
1245
|
+
borderRadius: 10,
|
|
1246
|
+
borderWidth: 1,
|
|
1247
|
+
borderColor: 'rgba(248,113,113,0.35)',
|
|
1248
|
+
backgroundColor: 'rgba(248,113,113,0.1)',
|
|
1249
|
+
},
|
|
1250
|
+
errorTitle: { color: '#fca5a5', fontWeight: '600', marginBottom: 4, fontSize: 13 },
|
|
1251
|
+
errorBody: { color: '#fca5a5', fontSize: 12 },
|
|
1252
|
+
successBox: {
|
|
1253
|
+
padding: 14,
|
|
1254
|
+
borderRadius: 10,
|
|
1255
|
+
borderWidth: 1,
|
|
1256
|
+
borderColor: 'rgba(34,197,94,0.35)',
|
|
1257
|
+
backgroundColor: 'rgba(34,197,94,0.1)',
|
|
1258
|
+
},
|
|
1259
|
+
successTitle: { color: '#4ade80', fontSize: 14, fontWeight: '600', marginBottom: 4 },
|
|
1260
|
+
successBody: { color: '#86efac', fontSize: 12 },
|
|
1261
|
+
urlBox: {
|
|
1262
|
+
padding: 12,
|
|
1263
|
+
borderRadius: 10,
|
|
1264
|
+
borderWidth: 1,
|
|
1265
|
+
borderColor: 'rgba(99,102,241,0.35)',
|
|
1266
|
+
backgroundColor: 'rgba(99,102,241,0.1)',
|
|
1267
|
+
},
|
|
1268
|
+
urlText: { color: '#c7d2fe', fontSize: 13 },
|
|
1269
|
+
codeLabel: {
|
|
1270
|
+
fontSize: 10,
|
|
1271
|
+
fontWeight: '600',
|
|
1272
|
+
color: '#94a3b8',
|
|
1273
|
+
letterSpacing: 0.8,
|
|
1274
|
+
marginBottom: 4,
|
|
1275
|
+
},
|
|
1276
|
+
codeBox: {
|
|
1277
|
+
padding: 14,
|
|
1278
|
+
borderRadius: 10,
|
|
1279
|
+
borderWidth: 1,
|
|
1280
|
+
borderColor: 'rgba(148,163,184,0.22)',
|
|
1281
|
+
backgroundColor: 'rgba(15,23,42,0.8)',
|
|
1282
|
+
alignItems: 'center',
|
|
1283
|
+
},
|
|
1284
|
+
codeText: {
|
|
1285
|
+
color: '#f1f5f9',
|
|
1286
|
+
fontSize: 22,
|
|
1287
|
+
letterSpacing: 6,
|
|
1288
|
+
fontFamily: 'Menlo',
|
|
1289
|
+
},
|
|
1290
|
+
codeHint: { color: '#64748b', fontSize: 10, marginTop: 4, textTransform: 'uppercase' },
|
|
1291
|
+
phishingHint: {
|
|
1292
|
+
color: '#475569',
|
|
1293
|
+
fontSize: 10,
|
|
1294
|
+
marginTop: 12,
|
|
1295
|
+
lineHeight: 14,
|
|
1296
|
+
},
|
|
1297
|
+
});
|
package/dist/P2PClient.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FeedbackBundle, TestSession, VoiceCapability } from './types';
|
|
1
|
+
import { FeedbackBundle, RunnerBrowserAuthSession, TestSession, VoiceCapability } from './types';
|
|
2
2
|
export interface FeedbackEvent {
|
|
3
3
|
type: string;
|
|
4
4
|
timestamp: string;
|
|
@@ -21,11 +21,32 @@ export interface ReloadAck {
|
|
|
21
21
|
export declare class P2PClient {
|
|
22
22
|
private baseUrl;
|
|
23
23
|
private authToken;
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Shared relay password. Required when baseUrl points through the
|
|
26
|
+
* Yaver managed relay (e.g. https://public.yaver.io/d/<deviceId>) —
|
|
27
|
+
* the relay rejects unauthenticated requests with 401. Attached as
|
|
28
|
+
* X-Relay-Password on every agent request.
|
|
29
|
+
*/
|
|
30
|
+
private relayPassword;
|
|
31
|
+
constructor(baseUrl: string, authToken: string, relayPassword?: string);
|
|
25
32
|
/** Update the base URL (e.g. after re-discovery). */
|
|
26
33
|
setBaseUrl(url: string): void;
|
|
27
34
|
/** Update the auth token. */
|
|
28
35
|
setAuthToken(token: string): void;
|
|
36
|
+
/** Update the relay password (used for managed-relay baseUrls). */
|
|
37
|
+
setRelayPassword(password: string): void;
|
|
38
|
+
/** Merge in Authorization + (optional) X-Relay-Password on top of a header block. */
|
|
39
|
+
private authHeaders;
|
|
40
|
+
/**
|
|
41
|
+
* Start a remote browser-style sign-in for a runner (codex --device-auth
|
|
42
|
+
* / claude auth login --console). Returns a session id; callers poll
|
|
43
|
+
* getRunnerBrowserAuthStatus to surface the verification URL + one-time
|
|
44
|
+
* code. No API keys involved — the CLI writes its own auth.json once
|
|
45
|
+
* the user completes the flow in any browser.
|
|
46
|
+
*/
|
|
47
|
+
startRunnerBrowserAuth(runner: string): Promise<RunnerBrowserAuthSession>;
|
|
48
|
+
getRunnerBrowserAuthStatus(sessionId: string): Promise<RunnerBrowserAuthSession>;
|
|
49
|
+
cancelRunnerBrowserAuth(sessionId: string): Promise<void>;
|
|
29
50
|
/** Health check — returns true if the agent is reachable. */
|
|
30
51
|
health(): Promise<boolean>;
|
|
31
52
|
/** Get agent info (hostname, version, platform). */
|
package/dist/P2PClient.js
CHANGED
|
@@ -92,9 +92,10 @@ function friendlyReloadError(status, body) {
|
|
|
92
92
|
* support for streaming feedback, listing builds, and triggering builds.
|
|
93
93
|
*/
|
|
94
94
|
class P2PClient {
|
|
95
|
-
constructor(baseUrl, authToken) {
|
|
95
|
+
constructor(baseUrl, authToken, relayPassword = '') {
|
|
96
96
|
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
97
97
|
this.authToken = authToken;
|
|
98
|
+
this.relayPassword = relayPassword;
|
|
98
99
|
}
|
|
99
100
|
/** Update the base URL (e.g. after re-discovery). */
|
|
100
101
|
setBaseUrl(url) {
|
|
@@ -104,6 +105,56 @@ class P2PClient {
|
|
|
104
105
|
setAuthToken(token) {
|
|
105
106
|
this.authToken = token;
|
|
106
107
|
}
|
|
108
|
+
/** Update the relay password (used for managed-relay baseUrls). */
|
|
109
|
+
setRelayPassword(password) {
|
|
110
|
+
this.relayPassword = password;
|
|
111
|
+
}
|
|
112
|
+
/** Merge in Authorization + (optional) X-Relay-Password on top of a header block. */
|
|
113
|
+
authHeaders(extra = {}) {
|
|
114
|
+
const h = { ...extra };
|
|
115
|
+
if (this.authToken)
|
|
116
|
+
h.Authorization = `Bearer ${this.authToken}`;
|
|
117
|
+
if (this.relayPassword)
|
|
118
|
+
h['X-Relay-Password'] = this.relayPassword;
|
|
119
|
+
return h;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Start a remote browser-style sign-in for a runner (codex --device-auth
|
|
123
|
+
* / claude auth login --console). Returns a session id; callers poll
|
|
124
|
+
* getRunnerBrowserAuthStatus to surface the verification URL + one-time
|
|
125
|
+
* code. No API keys involved — the CLI writes its own auth.json once
|
|
126
|
+
* the user completes the flow in any browser.
|
|
127
|
+
*/
|
|
128
|
+
async startRunnerBrowserAuth(runner) {
|
|
129
|
+
const resp = await fetch(`${this.baseUrl}/runner-auth/browser/start`, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: this.authHeaders({ 'Content-Type': 'application/json' }),
|
|
132
|
+
body: JSON.stringify({ runner }),
|
|
133
|
+
});
|
|
134
|
+
if (!resp.ok) {
|
|
135
|
+
const text = await resp.text().catch(() => '');
|
|
136
|
+
throw new Error(`startRunnerBrowserAuth(${runner}) HTTP ${resp.status}: ${text}`);
|
|
137
|
+
}
|
|
138
|
+
const data = await resp.json();
|
|
139
|
+
return data.session;
|
|
140
|
+
}
|
|
141
|
+
async getRunnerBrowserAuthStatus(sessionId) {
|
|
142
|
+
const url = `${this.baseUrl}/runner-auth/browser/status?id=${encodeURIComponent(sessionId)}`;
|
|
143
|
+
const resp = await fetch(url, { headers: this.authHeaders() });
|
|
144
|
+
if (!resp.ok) {
|
|
145
|
+
const text = await resp.text().catch(() => '');
|
|
146
|
+
throw new Error(`getRunnerBrowserAuthStatus HTTP ${resp.status}: ${text}`);
|
|
147
|
+
}
|
|
148
|
+
const data = await resp.json();
|
|
149
|
+
return data.session;
|
|
150
|
+
}
|
|
151
|
+
async cancelRunnerBrowserAuth(sessionId) {
|
|
152
|
+
const url = `${this.baseUrl}/runner-auth/browser/cancel?id=${encodeURIComponent(sessionId)}`;
|
|
153
|
+
try {
|
|
154
|
+
await fetch(url, { method: 'POST', headers: this.authHeaders() });
|
|
155
|
+
}
|
|
156
|
+
catch { /* best-effort */ }
|
|
157
|
+
}
|
|
107
158
|
/** Health check — returns true if the agent is reachable. */
|
|
108
159
|
async health() {
|
|
109
160
|
try {
|
|
@@ -563,9 +614,7 @@ class P2PClient {
|
|
|
563
614
|
async request(method, path) {
|
|
564
615
|
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
565
616
|
method,
|
|
566
|
-
headers:
|
|
567
|
-
Authorization: `Bearer ${this.authToken}`,
|
|
568
|
-
},
|
|
617
|
+
headers: this.authHeaders(),
|
|
569
618
|
});
|
|
570
619
|
if (!response.ok) {
|
|
571
620
|
const text = await response.text().catch(() => '');
|
package/dist/QuickActionIcon.js
CHANGED
|
@@ -115,6 +115,7 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
115
115
|
const [shakenThisSession, setShakenThisSession] = (0, react_1.useState)(false);
|
|
116
116
|
const [menuOpen, setMenuOpen] = (0, react_1.useState)(false);
|
|
117
117
|
const [hostSuppressed] = (0, react_1.useState)(() => isRunningInsideYaverHost());
|
|
118
|
+
const [launching, setLaunching] = (0, react_1.useState)(false);
|
|
118
119
|
// Load the persisted disable flag once on mount. Until it resolves we
|
|
119
120
|
// render nothing — a one-frame flash of the icon before hiding would
|
|
120
121
|
// be worse than a tiny delayed appearance.
|
|
@@ -162,6 +163,24 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
162
163
|
colorSub.remove();
|
|
163
164
|
};
|
|
164
165
|
}, []);
|
|
166
|
+
(0, react_1.useEffect)(() => {
|
|
167
|
+
const launchSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:reportLaunch', (event) => {
|
|
168
|
+
if (event?.state === 'starting') {
|
|
169
|
+
setLaunching(true);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
setLaunching(false);
|
|
173
|
+
});
|
|
174
|
+
const reportSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startReport', () => setLaunching(false));
|
|
175
|
+
const loginSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startLogin', () => setLaunching(false));
|
|
176
|
+
const pickerSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startMachinePicker', () => setLaunching(false));
|
|
177
|
+
return () => {
|
|
178
|
+
launchSub.remove();
|
|
179
|
+
reportSub.remove();
|
|
180
|
+
loginSub.remove();
|
|
181
|
+
pickerSub.remove();
|
|
182
|
+
};
|
|
183
|
+
}, []);
|
|
165
184
|
const panResponder = (0, react_1.useRef)(react_native_1.PanResponder.create({
|
|
166
185
|
onStartShouldSetPanResponder: () => true,
|
|
167
186
|
onMoveShouldSetPanResponder: (_, g) => Math.abs(g.dx) > 3 || Math.abs(g.dy) > 3,
|
|
@@ -195,9 +214,11 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
195
214
|
},
|
|
196
215
|
})).current;
|
|
197
216
|
const openFeedback = (0, react_1.useCallback)(() => {
|
|
217
|
+
if (launching)
|
|
218
|
+
return;
|
|
198
219
|
setMenuOpen(false);
|
|
199
220
|
void YaverFeedback_1.YaverFeedback.startReport();
|
|
200
|
-
}, []);
|
|
221
|
+
}, [launching]);
|
|
201
222
|
const hideForever = (0, react_1.useCallback)(() => {
|
|
202
223
|
setMenuOpen(false);
|
|
203
224
|
setUserDisabled(true);
|
|
@@ -233,10 +254,14 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
233
254
|
didDrag.current = false;
|
|
234
255
|
return;
|
|
235
256
|
}
|
|
257
|
+
if (launching)
|
|
258
|
+
return;
|
|
236
259
|
openFeedback();
|
|
237
260
|
}} onLongPress={() => {
|
|
238
261
|
if (didDrag.current)
|
|
239
262
|
return;
|
|
263
|
+
if (launching)
|
|
264
|
+
return;
|
|
240
265
|
setMenuOpen((m) => !m);
|
|
241
266
|
}} delayLongPress={LONG_PRESS_MS} hitSlop={6} accessibilityRole="button" accessibilityLabel="Open Yaver feedback" style={({ pressed }) => [
|
|
242
267
|
styles.icon,
|
|
@@ -247,7 +272,7 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
247
272
|
backgroundColor: presetColors?.backgroundColor ?? backgroundColor,
|
|
248
273
|
borderColor: presetColors?.borderColor ?? borderColor,
|
|
249
274
|
shadowColor: presetColors?.shadowColor ?? shadowColor,
|
|
250
|
-
opacity: pressed ? 0.85 : 1,
|
|
275
|
+
opacity: launching ? 0.62 : pressed ? 0.85 : 1,
|
|
251
276
|
},
|
|
252
277
|
]}>
|
|
253
278
|
<react_native_1.Text style={[
|
package/dist/YaverFeedback.d.ts
CHANGED
|
@@ -65,6 +65,19 @@ export declare class YaverFeedback {
|
|
|
65
65
|
static setPreferredDevice(deviceId: string): Promise<void>;
|
|
66
66
|
/** Resolve the currently selected remote machine from the authenticated device list. */
|
|
67
67
|
static getSelectedRemoteDevice(): Promise<import("./auth").RemoteDevice | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Trigger remote device-auth for a CLI runner on the selected agent
|
|
70
|
+
* (codex login --device-auth / claude auth login --console). Returns
|
|
71
|
+
* the session so the host UI can render the verification URL + code.
|
|
72
|
+
*
|
|
73
|
+
* RN UI layer owns the modal (see FeedbackModal's runner sign-in
|
|
74
|
+
* buttons). This method just proxies into P2PClient — no browser
|
|
75
|
+
* launch, no API keys, works through the relay with an SDK token
|
|
76
|
+
* that carries the runner-auth scope.
|
|
77
|
+
*/
|
|
78
|
+
static startRunnerBrowserAuth(runner: string): Promise<import('./types').RunnerBrowserAuthSession>;
|
|
79
|
+
static getRunnerBrowserAuthStatus(sessionId: string): Promise<import('./types').RunnerBrowserAuthSession>;
|
|
80
|
+
static cancelRunnerBrowserAuth(sessionId: string): Promise<void>;
|
|
68
81
|
/**
|
|
69
82
|
* Sign out: clear cached token + device, tear down the P2P client. The
|
|
70
83
|
* SDK stays enabled; the next feedback trigger will re-prompt for login.
|