yaver-feedback-react-native 0.9.2 → 0.9.3

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.
Files changed (103) hide show
  1. package/README.md +102 -1
  2. package/dist/AuthOverlay.d.ts +1 -14
  3. package/dist/AuthOverlay.js +9 -62
  4. package/dist/Discovery.js +0 -6
  5. package/dist/DogfoodRuntime.d.ts +124 -0
  6. package/dist/DogfoodRuntime.js +273 -0
  7. package/dist/FeedbackModal.js +347 -61
  8. package/dist/LoginScreen.d.ts +1 -5
  9. package/dist/LoginScreen.js +2 -6
  10. package/dist/MachinePickerScreen.d.ts +1 -3
  11. package/dist/MachinePickerScreen.js +6 -18
  12. package/dist/P2PClient.d.ts +116 -3
  13. package/dist/P2PClient.js +273 -3
  14. package/dist/P2PDogfoodDriver.d.ts +12 -0
  15. package/dist/P2PDogfoodDriver.js +118 -0
  16. package/dist/PairDeviceModal.d.ts +2 -3
  17. package/dist/VibeChatScreen.d.ts +11 -1
  18. package/dist/VibeChatScreen.js +200 -41
  19. package/dist/YaverFeedback.d.ts +34 -0
  20. package/dist/YaverFeedback.js +124 -19
  21. package/dist/YaverModeBadge.d.ts +22 -0
  22. package/dist/YaverModeBadge.js +219 -0
  23. package/dist/__tests__/AuthDevices.test.js +1 -48
  24. package/dist/__tests__/DogfoodRuntime.test.d.ts +1 -0
  25. package/dist/__tests__/DogfoodRuntime.test.js +116 -0
  26. package/dist/__tests__/P2PDogfoodDriver.test.d.ts +1 -0
  27. package/dist/__tests__/P2PDogfoodDriver.test.js +64 -0
  28. package/dist/__tests__/ReportIdentity.test.d.ts +25 -1
  29. package/dist/__tests__/ReportIdentity.test.js +34 -22
  30. package/dist/__tests__/YaverFeedback.test.js +13 -3
  31. package/dist/__tests__/deviceDogfood.test.d.ts +1 -0
  32. package/dist/__tests__/deviceDogfood.test.js +86 -0
  33. package/dist/__tests__/dogfoodPolicy.test.d.ts +1 -0
  34. package/dist/__tests__/dogfoodPolicy.test.js +33 -0
  35. package/dist/_core/ansi.d.ts +117 -0
  36. package/dist/_core/ansi.js +468 -0
  37. package/dist/_core/ansi.test.d.ts +1 -0
  38. package/dist/_core/ansi.test.js +225 -0
  39. package/dist/_core/buildFeedbackPrompt.d.ts +4 -9
  40. package/dist/_core/buildFeedbackPrompt.js +18 -72
  41. package/dist/_core/constants.d.ts +19 -6
  42. package/dist/_core/constants.js +20 -7
  43. package/dist/_core/device.d.ts +9 -23
  44. package/dist/_core/device.js +13 -28
  45. package/dist/_core/endpoints.d.ts +0 -7
  46. package/dist/_core/endpoints.js +0 -7
  47. package/dist/_core/index.d.ts +4 -0
  48. package/dist/_core/index.js +4 -0
  49. package/dist/_core/remoteless.d.ts +44 -0
  50. package/dist/_core/remoteless.js +75 -0
  51. package/dist/_core/trace.d.ts +47 -0
  52. package/dist/_core/trace.js +38 -0
  53. package/dist/_core/trace.test.d.ts +1 -0
  54. package/dist/_core/trace.test.js +60 -0
  55. package/dist/auth.d.ts +3 -60
  56. package/dist/auth.js +6 -88
  57. package/dist/deviceDogfood.d.ts +53 -0
  58. package/dist/deviceDogfood.js +137 -0
  59. package/dist/dogfoodPolicy.d.ts +25 -0
  60. package/dist/dogfoodPolicy.js +24 -0
  61. package/dist/index.d.ts +13 -4
  62. package/dist/index.js +24 -8
  63. package/dist/reloadActions.js +2 -2
  64. package/dist/types.d.ts +50 -7
  65. package/package.json +12 -3
  66. package/src/AuthOverlay.tsx +20 -106
  67. package/src/Discovery.ts +0 -6
  68. package/src/DogfoodRuntime.ts +373 -0
  69. package/src/FeedbackModal.tsx +445 -67
  70. package/src/LoginScreen.tsx +2 -22
  71. package/src/MachinePickerScreen.tsx +6 -21
  72. package/src/P2PClient.ts +347 -4
  73. package/src/P2PDogfoodDriver.ts +132 -0
  74. package/src/PairDeviceModal.tsx +2 -3
  75. package/src/VibeChatScreen.tsx +232 -42
  76. package/src/YaverFeedback.ts +133 -22
  77. package/src/YaverModeBadge.tsx +234 -0
  78. package/src/__tests__/AuthDevices.test.ts +1 -52
  79. package/src/__tests__/DogfoodRuntime.test.ts +135 -0
  80. package/src/__tests__/P2PDogfoodDriver.test.ts +80 -0
  81. package/src/__tests__/ReportIdentity.test.ts +36 -27
  82. package/src/__tests__/YaverFeedback.test.ts +15 -3
  83. package/src/__tests__/deviceDogfood.test.ts +77 -0
  84. package/src/__tests__/dogfoodPolicy.test.ts +34 -0
  85. package/src/_core/ansi.test.ts +250 -0
  86. package/src/_core/ansi.ts +475 -0
  87. package/src/_core/buildFeedbackPrompt.ts +18 -95
  88. package/src/_core/constants.ts +20 -6
  89. package/src/_core/device.ts +13 -30
  90. package/src/_core/endpoints.ts +0 -7
  91. package/src/_core/index.ts +4 -0
  92. package/src/_core/remoteless.ts +110 -0
  93. package/src/_core/trace.test.ts +58 -0
  94. package/src/_core/trace.ts +75 -0
  95. package/src/auth.ts +7 -156
  96. package/src/deviceDogfood.ts +171 -0
  97. package/src/dogfoodPolicy.ts +40 -0
  98. package/src/index.ts +40 -11
  99. package/src/reloadActions.ts +2 -2
  100. package/src/types.ts +45 -7
  101. package/dist/GuestOnboardingScreen.d.ts +0 -8
  102. package/dist/GuestOnboardingScreen.js +0 -282
  103. package/src/GuestOnboardingScreen.tsx +0 -307
@@ -0,0 +1,250 @@
1
+ // AUTO-SYNCED from shared/client-core/src/ansi.test.ts.
2
+ // DO NOT EDIT IN PLACE. Edit the source and re-run
3
+ // scripts/sync-client-core.sh. CI checks drift via `--check`.
4
+
5
+ /**
6
+ * ansi.test.ts — guards for the shared ANSI tokenizer.
7
+ *
8
+ * The bug this exists for (2026-08-09): the dashboard and mobile app both
9
+ * flattened opencode's raw ANSI stream to plain text with stripAnsi, losing
10
+ * every colour the console has, while the xterm Terminal view kept the
11
+ * bytes — two surfaces, two looks, one product. The tokenizer below is the
12
+ * single classifier both chat renderers consume; these tests pin the tokens
13
+ * so a renderer drift (web spans vs mobile nested Text) can never silently
14
+ * change what a user sees.
15
+ *
16
+ * Run: npx tsx shared/client-core/src/ansi.test.ts
17
+ */
18
+ import {
19
+ ANSI_16_RGB,
20
+ tokenizeAnsi,
21
+ ansiToPlain,
22
+ tokenStreamToLines,
23
+ paletteRgb,
24
+ classifyAnsiLine,
25
+ styleAnsiLines,
26
+ summarizeRawConsole,
27
+ } from "./ansi";
28
+
29
+ let failures = 0;
30
+ const eq = (got: unknown, want: unknown, label: string) => {
31
+ if (JSON.stringify(got) === JSON.stringify(want)) console.log(`ok ${label}`);
32
+ else { console.error(`FAIL ${label}:\n got ${JSON.stringify(got)}\n want ${JSON.stringify(want)}`); failures++; }
33
+ };
34
+ const ok = (c: unknown, label: string) => eq(Boolean(c), true, label);
35
+
36
+ // ── plain text passes through untouched ─────────────────────────────────
37
+ {
38
+ const t = tokenizeAnsi("hello world");
39
+ eq(t, [{ text: "hello world" }], "plain text → single unstyled token");
40
+ }
41
+
42
+ // ── SGR colours ──────────────────────────────────────────────────────────
43
+ {
44
+ const t = tokenizeAnsi("\x1b[31mred\x1b[0m plain");
45
+ eq(t, [
46
+ { text: "red", fg: { kind: "named", index: 1 } },
47
+ { text: " plain" },
48
+ ], "red text then reset → two tokens");
49
+ }
50
+ {
51
+ const t = tokenizeAnsi("\x1b[32m+\x1b[0m added");
52
+ eq(t, [
53
+ { text: "+", fg: { kind: "named", index: 2 } },
54
+ { text: " added" },
55
+ ], "green + line (git patch)");
56
+ }
57
+ {
58
+ const t = tokenizeAnsi("\x1b[1m\x1b[33m$ ls\x1b[0m");
59
+ eq(t, [
60
+ { text: "$ ls", fg: { kind: "named", index: 3 }, bold: true },
61
+ ], "bold yellow $ prompt");
62
+ }
63
+
64
+ // ── 256-colour palette ──────────────────────────────────────────────────
65
+ {
66
+ const t = tokenizeAnsi("\x1b[38;5;208morange\x1b[0m");
67
+ eq(t, [
68
+ { text: "orange", fg: { kind: "palette", index: 208 } },
69
+ ], "xterm-256 orange (208)");
70
+ }
71
+ {
72
+ const [r, g, b] = paletteRgb(208);
73
+ ok(r > 200 && g > 80 && g < 160 && b < 60, "palette 208 is orange-ish RGB");
74
+ }
75
+ {
76
+ const [r, g, b] = paletteRgb(2);
77
+ eq([r, g, b], [...ANSI_16_RGB[2]], "palette index <16 maps to the standard 16");
78
+ }
79
+
80
+ // ── truecolor ───────────────────────────────────────────────────────────
81
+ {
82
+ const t = tokenizeAnsi("\x1b[38;2;255;120;50mtrue\x1b[0m");
83
+ eq(t, [
84
+ { text: "true", fg: { kind: "rgb", rgb: [255, 120, 50] } },
85
+ ], "truecolor SGR 38;2;r;g;b");
86
+ }
87
+
88
+ // ── background ──────────────────────────────────────────────────────────
89
+ {
90
+ const t = tokenizeAnsi("\x1b[48;5;236mgray bg\x1b[0m");
91
+ eq(t, [
92
+ { text: "gray bg", bg: { kind: "palette", index: 236 } },
93
+ ], "xterm-256 background (patch gray)");
94
+ }
95
+
96
+ // ── bold / dim / underline / strike ─────────────────────────────────────
97
+ {
98
+ const t = tokenizeAnsi("\x1b[1mbold\x1b[22m \x1b[2mdim\x1b[0m \x1b[4munder\x1b[0m \x1b[9mstrike\x1b[0m");
99
+ eq(t, [
100
+ { text: "bold", bold: true },
101
+ { text: " " },
102
+ { text: "dim", dim: true },
103
+ { text: " " },
104
+ { text: "under", underline: true },
105
+ { text: " " },
106
+ { text: "strike", strike: true },
107
+ ], "bold/dim/underline/strike attributes");
108
+ }
109
+
110
+ // ── bare \x1b[m resets (the opencode banner emits this constantly) ─────
111
+ {
112
+ const t = tokenizeAnsi("\x1b[0m\n> build · deepseek-v4-flash\n\x1b[0m\n");
113
+ eq(t, [
114
+ { text: "\n> build · deepseek-v4-flash\n\n" },
115
+ ], "bare [0m sequences are dropped, text survives");
116
+ }
117
+
118
+ // ── $ prompt lines in a real opencode run ───────────────────────────────
119
+ {
120
+ const raw = "\x1b[0m\n> build · deepseek-v4-flash\n\x1b[0m\n\x1b[0m$ \x1b[0mls -la\n";
121
+ const t = tokenizeAnsi(raw);
122
+ eq(ansiToPlain(raw), "\n> build · deepseek-v4-flash\n\n$ ls -la\n", "plain extraction matches the visible console text");
123
+ const lines = tokenStreamToLines(t);
124
+ ok(lines.length >= 4, "token stream splits into lines at newlines");
125
+ const last = lines[lines.length - 1];
126
+ ok(last.tokens.some((tk) => tk.text.includes("$ ls -la")), "last line carries the $ prompt text");
127
+ }
128
+
129
+ // ── OSC-8 hyperlink extraction ──────────────────────────────────────────
130
+ {
131
+ const t = tokenizeAnsi("\x1b]8;;https://example.com\x07link text\x1b]8;;\x07 rest");
132
+ eq(t[0]?.href, "https://example.com", "OSC-8 link URL carried on the token");
133
+ eq(t[0]?.text, "link text", "OSC-8 link inner text kept");
134
+ eq(t[1]?.text, " rest", "text after link kept unstyled");
135
+ }
136
+
137
+ // ── OSC-8 scheme allowlist (the audit XSS finding, §6.1) ───────────────
138
+ // A prompt-injected \x1b]8;;javascript:…\x07 link used to reach the renderer
139
+ // verbatim and become an <a href> in the dashboard origin. The tokenizer now
140
+ // drops every scheme except http(s):// and mailto:// — the link becomes plain
141
+ // text, never a clickable/executable URL.
142
+ {
143
+ const evil = "\x1b]8;;javascript:alert(1)\x07click\x1b]8;;\x07";
144
+ const t = tokenizeAnsi(evil);
145
+ eq(t[0]?.href, undefined, "javascript: OSC-8 link is not carried as href");
146
+ eq(t[0]?.text, "click", "the inner text survives as plain text");
147
+
148
+ const data = tokenizeAnsi("\x1b]8;;data:text/html,<script>1</script>\x07x\x1b]8;;\x07");
149
+ eq(data[0]?.href, undefined, "data: OSC-8 link is dropped");
150
+
151
+ const proto = tokenizeAnsi("\x1b]8;;//evil.example/path\x07x\x1b]8;;\x07");
152
+ eq(proto[0]?.href, undefined, "protocol-relative OSC-8 link is dropped");
153
+
154
+ const bare = tokenizeAnsi("\x1b]8;;alert(1)\x07x\x1b]8;;\x07");
155
+ eq(bare[0]?.href, undefined, "scheme-less OSC-8 string is dropped");
156
+
157
+ const mail = tokenizeAnsi("\x1b]8;;mailto:a@b.co\x07mail\x1b]8;;\x07");
158
+ eq(mail[0]?.href, "mailto:a@b.co", "mailto: OSC-8 link is allowed");
159
+ }
160
+
161
+ // ── cursor/erase sequences become line breaks (TUI repaint) ─────────────
162
+ {
163
+ const t = tokenizeAnsi("line1\x1b[2Kline2");
164
+ const plain = t.map((x) => x.text).join("");
165
+ ok(plain.includes("\n"), "erase-line (K) leaves a line boundary for a repaint");
166
+ }
167
+
168
+ // ── unknown SGR codes are ignored, text survives ────────────────────────
169
+ {
170
+ const t = tokenizeAnsi("\x1b[999mweird\x1b[0m ok");
171
+ eq(t, [
172
+ { text: "weird ok" },
173
+ ], "unknown SGR code ignored, adjacent runs merge (no visible style change)");
174
+ }
175
+
176
+ if (failures > 0) {
177
+ console.error(`\n${failures} FAILURE(s)`);
178
+ process.exit(1);
179
+ }
180
+
181
+ // ── structural console-line classification ──────────────────────────────
182
+ {
183
+ eq(classifyAnsiLine("> build · deepseek-v4-flash"), "banner", "opencode build banner");
184
+ eq(classifyAnsiLine("> plan · gpt-5.4"), "banner", "opencode plan banner");
185
+ eq(classifyAnsiLine("$ ls -la"), "prompt", "$ prompt");
186
+ eq(classifyAnsiLine("$"), "plain", "lone $ is plain (no command)");
187
+ eq(classifyAnsiLine("diff --git a/x.ts b/x.ts"), "diff-header", "git diff header");
188
+ eq(classifyAnsiLine("+++ b/src/x.ts"), "diff-file", "+++ file line");
189
+ eq(classifyAnsiLine("--- a/src/x.ts"), "diff-file", "--- file line");
190
+ eq(classifyAnsiLine("@@ -1,2 +1,3 @@"), "diff-hunk", "hunk header");
191
+ eq(classifyAnsiLine("+const x = 1;"), "diff-add", "patch add");
192
+ eq(classifyAnsiLine("-const y = 2;"), "diff-del", "patch del");
193
+ eq(classifyAnsiLine(" + indented keeps leading"), "plain", "indented + is not a patch add");
194
+ eq(classifyAnsiLine("⎿ run npx tsc"), "tool-call", "opencode tool tail");
195
+ eq(classifyAnsiLine("✓ 3 files changed"), "tool-call", "checkmark status");
196
+ eq(classifyAnsiLine("normal output line"), "plain", "ordinary line");
197
+ }
198
+ {
199
+ const styled = styleAnsiLines("\x1b[0m\n> build · deepseek-v4-flash\n\x1b[0m\n$ \x1b[0mls\n");
200
+ ok(styled.length >= 4, "styleAnsiLines splits into lines");
201
+ const hints = styled.map((l) => l.hint);
202
+ ok(hints.includes("banner"), "banner line classified through the one-stop helper");
203
+ ok(hints.includes("prompt"), "$ prompt classified through the one-stop helper");
204
+ }
205
+
206
+ // ── summarizeRawConsole (shared noisy-console reducer) ──────────────────
207
+ {
208
+ const raw = [
209
+ "> build · deepseek-v4-flash", // banner — kept
210
+ "$ npm run build", // prompt echo — dropped
211
+ "workdir: /repo", // runner config banner — dropped
212
+ "compiled 42 files", // real output — kept
213
+ "compiled 42 files", // repeat
214
+ "compiled 42 files", // 3rd repeat — collapsed
215
+ "compiled 42 files", // 4th — dropped
216
+ "diff --git a/x.ts b/x.ts", // diff hunk — dropped
217
+ "──────────", // TUI redraw edge — dropped
218
+ "✓ done", // status — kept
219
+ ].join("\n");
220
+ const out = summarizeRawConsole(raw, false);
221
+ ok(out.includes("> build · deepseek-v4-flash"), "banner survives");
222
+ ok(!out.includes("$ npm run build"), "$ echo dropped");
223
+ ok(!out.includes("workdir:"), "config banner dropped");
224
+ ok(out.includes("compiled 42 files"), "real output kept");
225
+ ok(!out.includes("diff --git"), "diff hunk dropped");
226
+ ok(!out.includes("──────────"), "TUI redraw edge dropped");
227
+ ok(out.includes("✓ done"), "status kept");
228
+ ok(out.includes("noisy lines collapsed"), "collapse count reported");
229
+ // Running budget is tighter than finished.
230
+ const big = Array.from({ length: 200 }, (_, i) => `line ${i}`).join("\n");
231
+ const running = summarizeRawConsole(big, true).split("\n").length;
232
+ const finished = summarizeRawConsole(big, false).split("\n").length;
233
+ ok(running < finished, "running budget tighter than finished budget");
234
+ // KEEP THE TAIL (2026-08-13): the summarizer evicts the OLDEST kept line
235
+ // when the budget is full, so a live console follows the newest output —
236
+ // never drops it. The old head-keeping bug froze the web task console at
237
+ // the START of a long run while the header claimed it was live.
238
+ const tail = summarizeRawConsole(big, true).split("\n");
239
+ ok(tail.some((l) => l.startsWith("line 199")), "newest output survives the budget (tail kept)");
240
+ ok(!tail.some((l) => l.startsWith("line 0")), "oldest output evicted to make room");
241
+ // The tail must still be recognizably the END of the stream, and the
242
+ // collapse marker must not masquerade as a kept line.
243
+ const noMarker = tail.filter((l) => !l.startsWith("… "));
244
+ ok(noMarker[0].startsWith("line 160"), "first kept line is the oldest evicted window start (budget 40 of 200)");
245
+ // ANSI on kept lines survives (so AnsiConsoleText can still paint them).
246
+ const ansiLine = "\x1b[31mred text\x1b[0m";
247
+ ok(summarizeRawConsole(ansiLine, false).includes("\x1b[31m"), "kept lines keep their escapes");
248
+ }
249
+
250
+ console.log("\nall ansi tests pass");