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.
- package/README.md +102 -1
- package/dist/AuthOverlay.d.ts +1 -14
- package/dist/AuthOverlay.js +9 -62
- package/dist/Discovery.js +0 -6
- package/dist/DogfoodRuntime.d.ts +124 -0
- package/dist/DogfoodRuntime.js +273 -0
- package/dist/FeedbackModal.js +347 -61
- package/dist/LoginScreen.d.ts +1 -5
- package/dist/LoginScreen.js +2 -6
- package/dist/MachinePickerScreen.d.ts +1 -3
- package/dist/MachinePickerScreen.js +6 -18
- package/dist/P2PClient.d.ts +116 -3
- package/dist/P2PClient.js +273 -3
- package/dist/P2PDogfoodDriver.d.ts +12 -0
- package/dist/P2PDogfoodDriver.js +118 -0
- package/dist/PairDeviceModal.d.ts +2 -3
- package/dist/VibeChatScreen.d.ts +11 -1
- package/dist/VibeChatScreen.js +200 -41
- package/dist/YaverFeedback.d.ts +34 -0
- package/dist/YaverFeedback.js +124 -19
- package/dist/YaverModeBadge.d.ts +22 -0
- package/dist/YaverModeBadge.js +219 -0
- package/dist/__tests__/AuthDevices.test.js +1 -48
- package/dist/__tests__/DogfoodRuntime.test.d.ts +1 -0
- package/dist/__tests__/DogfoodRuntime.test.js +116 -0
- package/dist/__tests__/P2PDogfoodDriver.test.d.ts +1 -0
- package/dist/__tests__/P2PDogfoodDriver.test.js +64 -0
- package/dist/__tests__/ReportIdentity.test.d.ts +25 -1
- package/dist/__tests__/ReportIdentity.test.js +34 -22
- package/dist/__tests__/YaverFeedback.test.js +13 -3
- package/dist/__tests__/deviceDogfood.test.d.ts +1 -0
- package/dist/__tests__/deviceDogfood.test.js +86 -0
- package/dist/__tests__/dogfoodPolicy.test.d.ts +1 -0
- package/dist/__tests__/dogfoodPolicy.test.js +33 -0
- package/dist/_core/ansi.d.ts +117 -0
- package/dist/_core/ansi.js +468 -0
- package/dist/_core/ansi.test.d.ts +1 -0
- package/dist/_core/ansi.test.js +225 -0
- package/dist/_core/buildFeedbackPrompt.d.ts +4 -9
- package/dist/_core/buildFeedbackPrompt.js +18 -72
- package/dist/_core/constants.d.ts +19 -6
- package/dist/_core/constants.js +20 -7
- package/dist/_core/device.d.ts +9 -23
- package/dist/_core/device.js +13 -28
- package/dist/_core/endpoints.d.ts +0 -7
- package/dist/_core/endpoints.js +0 -7
- package/dist/_core/index.d.ts +4 -0
- package/dist/_core/index.js +4 -0
- package/dist/_core/remoteless.d.ts +44 -0
- package/dist/_core/remoteless.js +75 -0
- package/dist/_core/trace.d.ts +47 -0
- package/dist/_core/trace.js +38 -0
- package/dist/_core/trace.test.d.ts +1 -0
- package/dist/_core/trace.test.js +60 -0
- package/dist/auth.d.ts +3 -60
- package/dist/auth.js +6 -88
- package/dist/deviceDogfood.d.ts +53 -0
- package/dist/deviceDogfood.js +137 -0
- package/dist/dogfoodPolicy.d.ts +25 -0
- package/dist/dogfoodPolicy.js +24 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.js +24 -8
- package/dist/reloadActions.js +2 -2
- package/dist/types.d.ts +50 -7
- package/package.json +12 -3
- package/src/AuthOverlay.tsx +20 -106
- package/src/Discovery.ts +0 -6
- package/src/DogfoodRuntime.ts +373 -0
- package/src/FeedbackModal.tsx +445 -67
- package/src/LoginScreen.tsx +2 -22
- package/src/MachinePickerScreen.tsx +6 -21
- package/src/P2PClient.ts +347 -4
- package/src/P2PDogfoodDriver.ts +132 -0
- package/src/PairDeviceModal.tsx +2 -3
- package/src/VibeChatScreen.tsx +232 -42
- package/src/YaverFeedback.ts +133 -22
- package/src/YaverModeBadge.tsx +234 -0
- package/src/__tests__/AuthDevices.test.ts +1 -52
- package/src/__tests__/DogfoodRuntime.test.ts +135 -0
- package/src/__tests__/P2PDogfoodDriver.test.ts +80 -0
- package/src/__tests__/ReportIdentity.test.ts +36 -27
- package/src/__tests__/YaverFeedback.test.ts +15 -3
- package/src/__tests__/deviceDogfood.test.ts +77 -0
- package/src/__tests__/dogfoodPolicy.test.ts +34 -0
- package/src/_core/ansi.test.ts +250 -0
- package/src/_core/ansi.ts +475 -0
- package/src/_core/buildFeedbackPrompt.ts +18 -95
- package/src/_core/constants.ts +20 -6
- package/src/_core/device.ts +13 -30
- package/src/_core/endpoints.ts +0 -7
- package/src/_core/index.ts +4 -0
- package/src/_core/remoteless.ts +110 -0
- package/src/_core/trace.test.ts +58 -0
- package/src/_core/trace.ts +75 -0
- package/src/auth.ts +7 -156
- package/src/deviceDogfood.ts +171 -0
- package/src/dogfoodPolicy.ts +40 -0
- package/src/index.ts +40 -11
- package/src/reloadActions.ts +2 -2
- package/src/types.ts +45 -7
- package/dist/GuestOnboardingScreen.d.ts +0 -8
- package/dist/GuestOnboardingScreen.js +0 -282
- 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");
|