@cnrai/pave 0.3.35 → 0.3.51
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/LICENSE +21 -0
- package/README.md +21 -218
- package/package.json +32 -35
- package/pave.js +3 -0
- package/sandbox/SandboxRunner.js +1 -0
- package/sandbox/pave-run.js +2 -0
- package/sandbox/permission.js +1 -0
- package/sandbox/utils/yaml.js +1 -0
- package/MARKETPLACE.md +0 -406
- package/build-binary.js +0 -591
- package/build-npm.js +0 -537
- package/build.js +0 -230
- package/check-binary.js +0 -26
- package/deploy.sh +0 -95
- package/index.js +0 -5776
- package/lib/agent-registry.js +0 -1037
- package/lib/args-parser.js +0 -837
- package/lib/blessed-widget-patched.js +0 -93
- package/lib/cli-markdown.js +0 -590
- package/lib/compaction.js +0 -153
- package/lib/duration.js +0 -94
- package/lib/hash.js +0 -22
- package/lib/marketplace.js +0 -866
- package/lib/memory-config.js +0 -166
- package/lib/skill-manager.js +0 -891
- package/lib/soul.js +0 -31
- package/lib/tool-output-formatter.js +0 -180
- package/start-pave.sh +0 -149
- package/status.js +0 -271
- package/test/abort-stream.test.js +0 -445
- package/test/agent-auto-compaction.test.js +0 -552
- package/test/agent-comm-abort.test.js +0 -95
- package/test/agent-comm.test.js +0 -598
- package/test/agent-inbox.test.js +0 -576
- package/test/agent-init.test.js +0 -264
- package/test/agent-interrupt.test.js +0 -314
- package/test/agent-lifecycle.test.js +0 -520
- package/test/agent-log-files.test.js +0 -349
- package/test/agent-mode.manual-test.js +0 -392
- package/test/agent-parsing.test.js +0 -228
- package/test/agent-post-stream-idle.test.js +0 -762
- package/test/agent-registry.test.js +0 -359
- package/test/agent-rm.test.js +0 -442
- package/test/agent-spawn.test.js +0 -933
- package/test/agent-status-api.test.js +0 -624
- package/test/agent-update.test.js +0 -435
- package/test/args-parser.test.js +0 -391
- package/test/auto-compaction-chat.manual-test.js +0 -227
- package/test/auto-compaction.test.js +0 -941
- package/test/build-config.test.js +0 -120
- package/test/build-npm.test.js +0 -388
- package/test/chat-command.test.js +0 -137
- package/test/chat-leading-lines.test.js +0 -159
- package/test/config-flag.test.js +0 -272
- package/test/cursor-drift.test.js +0 -135
- package/test/debug-require.js +0 -23
- package/test/dir-migration.test.js +0 -323
- package/test/duration.test.js +0 -229
- package/test/ghostty-term.test.js +0 -202
- package/test/http500-backoff.test.js +0 -854
- package/test/integration.test.js +0 -86
- package/test/memory-guard-env.test.js +0 -220
- package/test/pr233-fixes.test.js +0 -259
- package/test/run-agent-init.js +0 -297
- package/test/run-all.js +0 -64
- package/test/run-config-flag.js +0 -159
- package/test/run-cursor-drift.js +0 -82
- package/test/run-session-path.js +0 -154
- package/test/run-tests.js +0 -643
- package/test/sandbox-redirect.test.js +0 -202
- package/test/session-path.test.js +0 -132
- package/test/shebang-strip.test.js +0 -241
- package/test/soul-reinject.test.js +0 -1027
- package/test/soul-reread.test.js +0 -281
- package/test/tool-output-formatter.test.js +0 -486
- package/test/tool-output-gating.test.js +0 -143
- package/test/tool-states.test.js +0 -167
- package/test/tools-flag.test.js +0 -65
- package/test/tui-attach.test.js +0 -1255
- package/test/tui-compaction.test.js +0 -354
- package/test/tui-wrap.test.js +0 -568
- package/test-binary.js +0 -52
- package/test-binary2.js +0 -36
package/test/tui-wrap.test.js
DELETED
|
@@ -1,568 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
// Test TUI word-wrap helper functions
|
|
6
|
-
// Verifies fixes for issues #147 (tui wrap algorithms) and #150 (wrap width mismatch)
|
|
7
|
-
//
|
|
8
|
-
// NOTE: The TUI helpers are attached to blessed input widgets at runtime
|
|
9
|
-
// and cannot be directly imported without a full blessed/screen setup.
|
|
10
|
-
// We replicate the helper logic here with a mock input to test the
|
|
11
|
-
// algorithms in isolation. If the production implementations diverge,
|
|
12
|
-
// integration tests with a real TUI instance should be added.
|
|
13
|
-
|
|
14
|
-
function runTest(name, fn) {
|
|
15
|
-
try {
|
|
16
|
-
fn();
|
|
17
|
-
console.log('\u2705 ' + name);
|
|
18
|
-
} catch (e) {
|
|
19
|
-
console.log('\u274C ' + name + ': ' + e.message);
|
|
20
|
-
process.exitCode = 1;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function assertEqual(actual, expected, msg) {
|
|
25
|
-
if (actual !== expected) {
|
|
26
|
-
throw new Error((msg || '') + ' expected ' + JSON.stringify(expected) + ', got ' + JSON.stringify(actual));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Helper: detect valid surrogate pair at index i
|
|
31
|
-
function isSurrogatePair(str, i) {
|
|
32
|
-
const code = str.charCodeAt(i);
|
|
33
|
-
if (code >= 0xD800 && code <= 0xDBFF && i + 1 < str.length) {
|
|
34
|
-
const next = str.charCodeAt(i + 1);
|
|
35
|
-
return next >= 0xDC00 && next <= 0xDFFF;
|
|
36
|
-
}
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ---- Helper functions replicated from tui/index.js ----
|
|
41
|
-
// These match the production implementations after the #147 fixes.
|
|
42
|
-
|
|
43
|
-
function createMockInput(strWidthFn, lposWidth) {
|
|
44
|
-
const input = {
|
|
45
|
-
value: '',
|
|
46
|
-
strWidth: strWidthFn || function (s) { return s ? s.length : 0; },
|
|
47
|
-
iwidth: 0,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// Mock lpos for _getWrapWidth. lposWidth is the raw widget width
|
|
51
|
-
// (xl - xi). If not provided, _getWrapWidth uses the fallback of 79.
|
|
52
|
-
if (lposWidth !== undefined) {
|
|
53
|
-
input.lpos = { xl: lposWidth, xi: 0 };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Blessed subtracts 1 from textarea width in _wrapContent (the "textarea
|
|
57
|
-
// margin" for the cursor column). See #112, #150.
|
|
58
|
-
input._getWrapWidth = function (lpos) {
|
|
59
|
-
if (!lpos) lpos = input.lpos;
|
|
60
|
-
const w = lpos ? (lpos.xl - lpos.xi) - input.iwidth - 1 : 79;
|
|
61
|
-
return w < 1 ? 1 : w;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
input._wordWrapLine = function (line, widgetWidth) {
|
|
65
|
-
if (widgetWidth < 1) widgetWidth = 1;
|
|
66
|
-
const segs = [];
|
|
67
|
-
let offset = 0;
|
|
68
|
-
while (line.length > 0) {
|
|
69
|
-
if (input.strWidth(line) <= widgetWidth) {
|
|
70
|
-
segs.push({ text: line, startIdx: offset });
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
let i = 0;
|
|
74
|
-
let w = 0;
|
|
75
|
-
while (i < line.length) {
|
|
76
|
-
let step = 1;
|
|
77
|
-
if (isSurrogatePair(line, i)) {
|
|
78
|
-
step = 2;
|
|
79
|
-
}
|
|
80
|
-
const cw = input.strWidth(line.substring(i, i + step));
|
|
81
|
-
if (w + cw > widgetWidth) break;
|
|
82
|
-
w += cw;
|
|
83
|
-
i += step;
|
|
84
|
-
}
|
|
85
|
-
if (i < 1) {
|
|
86
|
-
// Always advance at least one full codepoint
|
|
87
|
-
let minStep = 1;
|
|
88
|
-
if (isSurrogatePair(line, 0)) {
|
|
89
|
-
minStep = 2;
|
|
90
|
-
}
|
|
91
|
-
i = minStep;
|
|
92
|
-
}
|
|
93
|
-
let j = i;
|
|
94
|
-
while (j > i - 10 && j > 0 && line[--j] !== ' ');
|
|
95
|
-
if (line[j] === ' ') i = j + 1;
|
|
96
|
-
const part = line.substring(0, i);
|
|
97
|
-
segs.push({ text: part, startIdx: offset });
|
|
98
|
-
offset += part.length;
|
|
99
|
-
line = line.substring(i);
|
|
100
|
-
if (line === '') break;
|
|
101
|
-
}
|
|
102
|
-
if (segs.length === 0) {
|
|
103
|
-
segs.push({ text: '', startIdx: 0 });
|
|
104
|
-
}
|
|
105
|
-
return segs;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
input._getVisualPos = function (charPos, widgetWidth) {
|
|
109
|
-
const value = input.value || '';
|
|
110
|
-
const logicalLines = value.split('\n');
|
|
111
|
-
let visualRow = 0;
|
|
112
|
-
let consumed = 0;
|
|
113
|
-
for (let li = 0; li < logicalLines.length; li++) {
|
|
114
|
-
const logLine = logicalLines[li];
|
|
115
|
-
const segs = input._wordWrapLine(logLine, widgetWidth);
|
|
116
|
-
for (let si = 0; si < segs.length; si++) {
|
|
117
|
-
const segStart = consumed + segs[si].startIdx;
|
|
118
|
-
const segLen = segs[si].text.length;
|
|
119
|
-
const segEnd = segStart + segLen;
|
|
120
|
-
if (charPos >= segStart && (charPos < segEnd || (si === segs.length - 1 && charPos === segEnd))) {
|
|
121
|
-
const col = input.strWidth(value.substring(segStart, charPos));
|
|
122
|
-
return { row: visualRow, col };
|
|
123
|
-
}
|
|
124
|
-
visualRow++;
|
|
125
|
-
}
|
|
126
|
-
consumed += logLine.length + 1;
|
|
127
|
-
}
|
|
128
|
-
return { row: Math.max(0, visualRow - 1), col: 0 };
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
input._countVisualLines = function (widgetWidth) {
|
|
132
|
-
const value = input.value || '';
|
|
133
|
-
if (!value) return 1;
|
|
134
|
-
const logicalLines = value.split('\n');
|
|
135
|
-
let total = 0;
|
|
136
|
-
for (let li = 0; li < logicalLines.length; li++) {
|
|
137
|
-
total += input._wordWrapLine(logicalLines[li], widgetWidth).length;
|
|
138
|
-
}
|
|
139
|
-
return total;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
input._buildCharToVisual = function (widgetWidth) {
|
|
143
|
-
const value = input.value || '';
|
|
144
|
-
const map = [];
|
|
145
|
-
const logicalLines = value.split('\n');
|
|
146
|
-
let visualRow = 0;
|
|
147
|
-
for (let li = 0; li < logicalLines.length; li++) {
|
|
148
|
-
const logLine = logicalLines[li];
|
|
149
|
-
const segs = input._wordWrapLine(logLine, widgetWidth);
|
|
150
|
-
for (let si = 0; si < segs.length; si++) {
|
|
151
|
-
const seg = segs[si];
|
|
152
|
-
let runCol = 0;
|
|
153
|
-
for (let ci = 0; ci < seg.text.length; ci++) {
|
|
154
|
-
map.push({ row: visualRow, col: runCol });
|
|
155
|
-
let step = 1;
|
|
156
|
-
if (isSurrogatePair(seg.text, ci)) {
|
|
157
|
-
step = 2;
|
|
158
|
-
}
|
|
159
|
-
const charWidth = input.strWidth(seg.text.substring(ci, ci + step));
|
|
160
|
-
runCol += charWidth;
|
|
161
|
-
if (step === 2) {
|
|
162
|
-
map.push({ row: visualRow, col: runCol - charWidth });
|
|
163
|
-
ci++;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (si < segs.length - 1) {
|
|
167
|
-
visualRow++;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (li < logicalLines.length - 1) {
|
|
171
|
-
map.push({ row: visualRow, col: map.length > 0 ? input.strWidth(segs[segs.length - 1].text) : 0 });
|
|
172
|
-
visualRow++;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
const lastSeg = input._wordWrapLine(logicalLines[logicalLines.length - 1], widgetWidth);
|
|
176
|
-
const lastSegText = lastSeg[lastSeg.length - 1].text;
|
|
177
|
-
let endCol = input.strWidth(lastSegText);
|
|
178
|
-
if (endCol > widgetWidth) {
|
|
179
|
-
visualRow++;
|
|
180
|
-
endCol = 0;
|
|
181
|
-
}
|
|
182
|
-
map.push({ row: visualRow, col: endCol });
|
|
183
|
-
return map;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
input._charPosFromVisualRow = function (targetRow, targetCol, widgetWidth) {
|
|
187
|
-
const value = input.value || '';
|
|
188
|
-
const logicalLines = value.split('\n');
|
|
189
|
-
let visualRow = 0;
|
|
190
|
-
let consumed = 0;
|
|
191
|
-
for (let li = 0; li < logicalLines.length; li++) {
|
|
192
|
-
const logLine = logicalLines[li];
|
|
193
|
-
const segs = input._wordWrapLine(logLine, widgetWidth);
|
|
194
|
-
for (let si = 0; si < segs.length; si++) {
|
|
195
|
-
if (visualRow === targetRow) {
|
|
196
|
-
const segStart = consumed + segs[si].startIdx;
|
|
197
|
-
const segText = segs[si].text;
|
|
198
|
-
let runW = 0;
|
|
199
|
-
for (let ci = 0; ci < segText.length; ci++) {
|
|
200
|
-
let step = 1;
|
|
201
|
-
if (isSurrogatePair(segText, ci)) {
|
|
202
|
-
step = 2;
|
|
203
|
-
}
|
|
204
|
-
const cw = input.strWidth(segText.substring(ci, ci + step));
|
|
205
|
-
if (runW + cw > targetCol) {
|
|
206
|
-
return segStart + ci;
|
|
207
|
-
}
|
|
208
|
-
runW += cw;
|
|
209
|
-
if (step === 2) ci++;
|
|
210
|
-
}
|
|
211
|
-
return segStart + segText.length;
|
|
212
|
-
}
|
|
213
|
-
visualRow++;
|
|
214
|
-
}
|
|
215
|
-
consumed += logLine.length + 1;
|
|
216
|
-
}
|
|
217
|
-
return value.length;
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
return input;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Surrogate-pair-aware strWidth for tests
|
|
224
|
-
function surrogateStrWidth(s) {
|
|
225
|
-
if (!s) return 0;
|
|
226
|
-
let w = 0;
|
|
227
|
-
for (let i = 0; i < s.length; i++) {
|
|
228
|
-
const code = s.charCodeAt(i);
|
|
229
|
-
if (code >= 0xD800 && code <= 0xDBFF) {
|
|
230
|
-
w += 2; // surrogate pair = 2 visual width
|
|
231
|
-
i++; // skip low surrogate
|
|
232
|
-
} else if (code >= 0xDC00 && code <= 0xDFFF) {
|
|
233
|
-
w += 1; // lone low surrogate
|
|
234
|
-
} else {
|
|
235
|
-
w += 1;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return w;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// ========== _wordWrapLine tests ==========
|
|
242
|
-
|
|
243
|
-
runTest('wordWrap: short line fits without wrapping', () => {
|
|
244
|
-
const input = createMockInput();
|
|
245
|
-
const segs = input._wordWrapLine('hello', 10);
|
|
246
|
-
assertEqual(segs.length, 1);
|
|
247
|
-
assertEqual(segs[0].text, 'hello');
|
|
248
|
-
assertEqual(segs[0].startIdx, 0);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
runTest('wordWrap: exact width line does not wrap', () => {
|
|
252
|
-
const input = createMockInput();
|
|
253
|
-
const segs = input._wordWrapLine('1234567890', 10);
|
|
254
|
-
assertEqual(segs.length, 1);
|
|
255
|
-
assertEqual(segs[0].text, '1234567890');
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
runTest('wordWrap: wraps at word boundary within 10 chars of width', () => {
|
|
259
|
-
const input = createMockInput();
|
|
260
|
-
const segs = input._wordWrapLine('hello world', 10);
|
|
261
|
-
assertEqual(segs.length, 2);
|
|
262
|
-
assertEqual(segs[0].text, 'hello ');
|
|
263
|
-
assertEqual(segs[1].text, 'world');
|
|
264
|
-
assertEqual(segs[1].startIdx, 6);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
runTest('wordWrap: wraps long word without spaces at width', () => {
|
|
268
|
-
const input = createMockInput();
|
|
269
|
-
const segs = input._wordWrapLine('abcdefghijklmnop', 10);
|
|
270
|
-
assertEqual(segs.length, 2);
|
|
271
|
-
assertEqual(segs[0].text, 'abcdefghij');
|
|
272
|
-
assertEqual(segs[1].text, 'klmnop');
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
runTest('wordWrap: empty line returns single empty segment', () => {
|
|
276
|
-
const input = createMockInput();
|
|
277
|
-
const segs = input._wordWrapLine('', 10);
|
|
278
|
-
assertEqual(segs.length, 1);
|
|
279
|
-
assertEqual(segs[0].text, '');
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
runTest('wordWrap: widgetWidth < 1 treated as 1', () => {
|
|
283
|
-
const input = createMockInput();
|
|
284
|
-
const segs = input._wordWrapLine('abc', 0);
|
|
285
|
-
if (segs.length < 1) throw new Error('Expected at least 1 segment');
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
runTest('wordWrap: multiple wraps', () => {
|
|
289
|
-
const input = createMockInput();
|
|
290
|
-
const segs = input._wordWrapLine('aaa bbb ccc ddd eee fff ggg h', 10);
|
|
291
|
-
if (segs.length < 3) throw new Error('Expected at least 3 segments, got ' + segs.length);
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
runTest('wordWrap: does not split surrogate pairs', () => {
|
|
295
|
-
const input = createMockInput(surrogateStrWidth);
|
|
296
|
-
// 'aaa' + U+1F600 (😀) + 'bbb' = visual 3+2+3 = 8
|
|
297
|
-
const line = 'aaa\uD83D\uDE00bbb';
|
|
298
|
-
const segs = input._wordWrapLine(line, 5);
|
|
299
|
-
assertEqual(segs.length, 2);
|
|
300
|
-
assertEqual(segs[0].text, 'aaa\uD83D\uDE00');
|
|
301
|
-
assertEqual(segs[1].text, 'bbb');
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
runTest('wordWrap: widgetWidth=1 with wide surrogate pair does not split pair', () => {
|
|
305
|
-
const input = createMockInput(surrogateStrWidth);
|
|
306
|
-
// Emoji (😀) has visual width 2, but widgetWidth is 1.
|
|
307
|
-
// Must keep the surrogate pair intact, not split mid-pair.
|
|
308
|
-
const line = '\uD83D\uDE00b';
|
|
309
|
-
const segs = input._wordWrapLine(line, 1);
|
|
310
|
-
if (segs.length < 1) throw new Error('Expected at least 1 segment');
|
|
311
|
-
// The first segment should be the full emoji (never a lone high surrogate)
|
|
312
|
-
assertEqual(segs[0].text, '\uD83D\uDE00');
|
|
313
|
-
if (segs.length > 1) {
|
|
314
|
-
assertEqual(segs[1].text.charAt(0), 'b');
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// ========== _getVisualPos tests ==========
|
|
319
|
-
|
|
320
|
-
runTest('visualPos: cursor at start of single line', () => {
|
|
321
|
-
const input = createMockInput();
|
|
322
|
-
input.value = 'hello';
|
|
323
|
-
const pos = input._getVisualPos(0, 10);
|
|
324
|
-
assertEqual(pos.row, 0);
|
|
325
|
-
assertEqual(pos.col, 0);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
runTest('visualPos: cursor at end of single line', () => {
|
|
329
|
-
const input = createMockInput();
|
|
330
|
-
input.value = 'hello';
|
|
331
|
-
const pos = input._getVisualPos(5, 10);
|
|
332
|
-
assertEqual(pos.row, 0);
|
|
333
|
-
assertEqual(pos.col, 5);
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
runTest('visualPos: cursor on second visual line after wrap', () => {
|
|
337
|
-
const input = createMockInput();
|
|
338
|
-
input.value = 'hello world';
|
|
339
|
-
const pos = input._getVisualPos(6, 10);
|
|
340
|
-
assertEqual(pos.row, 1);
|
|
341
|
-
assertEqual(pos.col, 0);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
runTest('visualPos: cursor with newline', () => {
|
|
345
|
-
const input = createMockInput();
|
|
346
|
-
input.value = 'abc\ndef';
|
|
347
|
-
const pos = input._getVisualPos(4, 10);
|
|
348
|
-
assertEqual(pos.row, 1);
|
|
349
|
-
assertEqual(pos.col, 0);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
// ========== _countVisualLines tests ==========
|
|
353
|
-
|
|
354
|
-
runTest('countLines: empty value returns 1', () => {
|
|
355
|
-
const input = createMockInput();
|
|
356
|
-
input.value = '';
|
|
357
|
-
assertEqual(input._countVisualLines(10), 1);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
runTest('countLines: single short line returns 1', () => {
|
|
361
|
-
const input = createMockInput();
|
|
362
|
-
input.value = 'hello';
|
|
363
|
-
assertEqual(input._countVisualLines(10), 1);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
runTest('countLines: wrapping line adds visual lines', () => {
|
|
367
|
-
const input = createMockInput();
|
|
368
|
-
input.value = 'hello world';
|
|
369
|
-
assertEqual(input._countVisualLines(10), 2);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
runTest('countLines: newline adds visual line', () => {
|
|
373
|
-
const input = createMockInput();
|
|
374
|
-
input.value = 'abc\ndef';
|
|
375
|
-
assertEqual(input._countVisualLines(10), 2);
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
// ========== _buildCharToVisual tests ==========
|
|
379
|
-
|
|
380
|
-
runTest('charMap: simple line maps correctly', () => {
|
|
381
|
-
const input = createMockInput();
|
|
382
|
-
input.value = 'abc';
|
|
383
|
-
const map = input._buildCharToVisual(10);
|
|
384
|
-
assertEqual(map.length, 4);
|
|
385
|
-
assertEqual(map[0].row, 0);
|
|
386
|
-
assertEqual(map[0].col, 0);
|
|
387
|
-
assertEqual(map[1].col, 1);
|
|
388
|
-
assertEqual(map[2].col, 2);
|
|
389
|
-
assertEqual(map[3].col, 3);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
runTest('charMap: exact width line end stays on same row', () => {
|
|
393
|
-
const input = createMockInput();
|
|
394
|
-
input.value = '1234567890';
|
|
395
|
-
const map = input._buildCharToVisual(10);
|
|
396
|
-
assertEqual(map[10].row, 0);
|
|
397
|
-
assertEqual(map[10].col, 10);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
runTest('charMap: line exceeding width wraps end to next row', () => {
|
|
401
|
-
const input = createMockInput();
|
|
402
|
-
input.value = '12345678901';
|
|
403
|
-
const map = input._buildCharToVisual(10);
|
|
404
|
-
assertEqual(map[10].row, 1);
|
|
405
|
-
assertEqual(map[10].col, 0);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
runTest('charMap: newline maps to end of visual row', () => {
|
|
409
|
-
const input = createMockInput();
|
|
410
|
-
input.value = 'ab\ncd';
|
|
411
|
-
const map = input._buildCharToVisual(10);
|
|
412
|
-
assertEqual(map.length, 6);
|
|
413
|
-
assertEqual(map[2].row, 0);
|
|
414
|
-
assertEqual(map[3].row, 1);
|
|
415
|
-
assertEqual(map[3].col, 0);
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
runTest('charMap: uses input.value not fullValue (no ReferenceError)', () => {
|
|
419
|
-
const input = createMockInput();
|
|
420
|
-
input.value = 'hello\nworld';
|
|
421
|
-
const map = input._buildCharToVisual(10);
|
|
422
|
-
assertEqual(map.length, 12);
|
|
423
|
-
assertEqual((input.value || '')[5], '\n');
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
// ========== _charPosFromVisualRow tests ==========
|
|
427
|
-
|
|
428
|
-
runTest('charFromRow: row 0 col 0 returns 0', () => {
|
|
429
|
-
const input = createMockInput();
|
|
430
|
-
input.value = 'hello';
|
|
431
|
-
assertEqual(input._charPosFromVisualRow(0, 0, 10), 0);
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
runTest('charFromRow: row 0 col 3 returns 3', () => {
|
|
435
|
-
const input = createMockInput();
|
|
436
|
-
input.value = 'hello';
|
|
437
|
-
assertEqual(input._charPosFromVisualRow(0, 3, 10), 3);
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
runTest('charFromRow: col beyond line length clamps to end', () => {
|
|
441
|
-
const input = createMockInput();
|
|
442
|
-
input.value = 'hello';
|
|
443
|
-
assertEqual(input._charPosFromVisualRow(0, 999, 10), 5);
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
runTest('charFromRow: navigates to second visual line after wrap', () => {
|
|
447
|
-
const input = createMockInput();
|
|
448
|
-
input.value = 'hello world';
|
|
449
|
-
const pos = input._charPosFromVisualRow(1, 0, 10);
|
|
450
|
-
assertEqual(pos, 6);
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
runTest('charFromRow: past last row returns value length', () => {
|
|
454
|
-
const input = createMockInput();
|
|
455
|
-
input.value = 'hello';
|
|
456
|
-
assertEqual(input._charPosFromVisualRow(99, 0, 10), 5);
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
// ========== Double-width character tests ==========
|
|
460
|
-
|
|
461
|
-
runTest('double-width: _wordWrapLine handles double-width chars', () => {
|
|
462
|
-
const input = createMockInput((s) => { return s ? s.length * 2 : 0; });
|
|
463
|
-
let segs = input._wordWrapLine('abcde', 10);
|
|
464
|
-
assertEqual(segs.length, 1);
|
|
465
|
-
segs = input._wordWrapLine('abcdef', 10);
|
|
466
|
-
assertEqual(segs.length, 2);
|
|
467
|
-
assertEqual(segs[0].text, 'abcde');
|
|
468
|
-
assertEqual(segs[1].text, 'f');
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
runTest('double-width: _getVisualPos with double-width chars', () => {
|
|
472
|
-
const input = createMockInput((s) => { return s ? s.length * 2 : 0; });
|
|
473
|
-
input.value = 'abcdef';
|
|
474
|
-
const pos = input._getVisualPos(5, 10);
|
|
475
|
-
assertEqual(pos.row, 1);
|
|
476
|
-
assertEqual(pos.col, 0);
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
runTest('double-width: _buildCharToVisual maps columns correctly', () => {
|
|
480
|
-
const input = createMockInput((s) => { return s ? s.length * 2 : 0; });
|
|
481
|
-
input.value = 'abc';
|
|
482
|
-
const map = input._buildCharToVisual(10);
|
|
483
|
-
assertEqual(map[0].col, 0);
|
|
484
|
-
assertEqual(map[1].col, 2);
|
|
485
|
-
assertEqual(map[2].col, 4);
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
runTest('double-width: _charPosFromVisualRow with double-width', () => {
|
|
489
|
-
const input = createMockInput((s) => { return s ? s.length * 2 : 0; });
|
|
490
|
-
input.value = 'abcdef';
|
|
491
|
-
const pos = input._charPosFromVisualRow(0, 4, 10);
|
|
492
|
-
assertEqual(pos, 2);
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
// ========== _getWrapWidth tests (issue #150) ==========
|
|
496
|
-
|
|
497
|
-
runTest('wrapWidth: subtracts 1 from raw widget width (textarea margin)', () => {
|
|
498
|
-
// Widget width = 50 (xl=50, xi=0), iwidth=0 => wrapWidth = 50 - 0 - 1 = 49
|
|
499
|
-
const input = createMockInput(undefined, 50);
|
|
500
|
-
assertEqual(input._getWrapWidth(), 49);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
runTest('wrapWidth: accounts for iwidth', () => {
|
|
504
|
-
const input = createMockInput(undefined, 50);
|
|
505
|
-
input.iwidth = 2;
|
|
506
|
-
// 50 - 2 - 1 = 47
|
|
507
|
-
assertEqual(input._getWrapWidth(), 47);
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
runTest('wrapWidth: returns 79 when no lpos available', () => {
|
|
511
|
-
const input = createMockInput();
|
|
512
|
-
// No lpos set, so fallback to 79
|
|
513
|
-
assertEqual(input._getWrapWidth(), 79);
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
runTest('wrapWidth: clamps to 1 for very narrow widget', () => {
|
|
517
|
-
const input = createMockInput(undefined, 2);
|
|
518
|
-
// 2 - 0 - 1 = 1
|
|
519
|
-
assertEqual(input._getWrapWidth(), 1);
|
|
520
|
-
input.lpos = { xl: 1, xi: 0 };
|
|
521
|
-
// 1 - 0 - 1 = 0 => clamped to 1
|
|
522
|
-
assertEqual(input._getWrapWidth(), 1);
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
runTest('wrapWidth: accepts optional lpos parameter to avoid redundant _getCoords', () => {
|
|
526
|
-
const input = createMockInput(undefined, 20);
|
|
527
|
-
// Default: uses input.lpos (xl=20, xi=0) => 20 - 0 - 1 = 19
|
|
528
|
-
assertEqual(input._getWrapWidth(), 19);
|
|
529
|
-
// Passing a different lpos overrides the default
|
|
530
|
-
const customLpos = { xl: 15, xi: 0 };
|
|
531
|
-
assertEqual(input._getWrapWidth(customLpos), 14);
|
|
532
|
-
// Passing lpos avoids using input.lpos
|
|
533
|
-
input.lpos = null;
|
|
534
|
-
assertEqual(input._getWrapWidth(customLpos), 14);
|
|
535
|
-
// Without lpos param and no input.lpos, falls back to 79
|
|
536
|
-
assertEqual(input._getWrapWidth(), 79);
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
runTest('wrapWidth: word wrap uses correct width with textarea margin', () => {
|
|
540
|
-
// Simulate a widget that is 11 chars wide. Blessed's effective wrap
|
|
541
|
-
// width is 11 - 1 = 10. "hello world" (11 chars) should wrap.
|
|
542
|
-
const input = createMockInput(undefined, 11);
|
|
543
|
-
input.value = 'hello world';
|
|
544
|
-
const ww = input._getWrapWidth(); // 10
|
|
545
|
-
assertEqual(ww, 10);
|
|
546
|
-
const segs = input._wordWrapLine('hello world', ww);
|
|
547
|
-
assertEqual(segs.length, 2);
|
|
548
|
-
assertEqual(segs[0].text, 'hello ');
|
|
549
|
-
assertEqual(segs[1].text, 'world');
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
runTest('wrapWidth: cursor position matches blessed wrap at boundary', () => {
|
|
553
|
-
// Widget width 11, effective wrap width 10.
|
|
554
|
-
// "hello worl" = 10 chars, fits on line 1.
|
|
555
|
-
// Adding "d" makes "hello world" = 11 chars, wraps "world" to line 2.
|
|
556
|
-
const input = createMockInput(undefined, 11);
|
|
557
|
-
input.value = 'hello world';
|
|
558
|
-
const ww = input._getWrapWidth(); // 10
|
|
559
|
-
// Insertion position 10 (just before 'd') should be on row 1
|
|
560
|
-
let vpos = input._getVisualPos(10, ww);
|
|
561
|
-
assertEqual(vpos.row, 1);
|
|
562
|
-
// Insertion position 6 (just before the 'w' of 'world') should also be on row 1
|
|
563
|
-
vpos = input._getVisualPos(6, ww);
|
|
564
|
-
assertEqual(vpos.row, 1);
|
|
565
|
-
assertEqual(vpos.col, 0);
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
console.log('\nAll tui-wrap tests complete.');
|
package/test-binary.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
const binaryPath = '/Users/raymond/pave-apps/openpave/src/packages/pave/dist/bin/pave-darwin-arm64';
|
|
5
|
-
const binary = fs.readFileSync(binaryPath);
|
|
6
|
-
const str = binary.toString('utf8');
|
|
7
|
-
|
|
8
|
-
console.log('='.repeat(60));
|
|
9
|
-
console.log('PAVE Binary Analysis');
|
|
10
|
-
console.log('='.repeat(60));
|
|
11
|
-
console.log('Binary size:', (binary.length / 1024 / 1024).toFixed(1), 'MB');
|
|
12
|
-
console.log('');
|
|
13
|
-
console.log('Searching for sensitive sandbox strings...');
|
|
14
|
-
console.log('');
|
|
15
|
-
|
|
16
|
-
const sensitivePatterns = [
|
|
17
|
-
'newGlobal',
|
|
18
|
-
'os.system',
|
|
19
|
-
'SandboxRunner',
|
|
20
|
-
'__ipc__',
|
|
21
|
-
'OPENCODE_TOKENS',
|
|
22
|
-
'deleteModule',
|
|
23
|
-
'newCompartment',
|
|
24
|
-
'drainJobQueue',
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
sensitivePatterns.forEach((pattern) => {
|
|
28
|
-
const found = str.includes(pattern);
|
|
29
|
-
const status = found ? '⚠️ FOUND' : '✅ NOT FOUND';
|
|
30
|
-
console.log(` ${pattern}: ${status}`);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
console.log('');
|
|
34
|
-
console.log('='.repeat(60));
|
|
35
|
-
|
|
36
|
-
// Compare with original JS bundle
|
|
37
|
-
const jsPath = '/Users/raymond/pave-apps/openpave/src/packages/pave/dist/pave.js';
|
|
38
|
-
const js = fs.readFileSync(jsPath, 'utf8');
|
|
39
|
-
|
|
40
|
-
console.log('Comparison with original pave.js:');
|
|
41
|
-
console.log('');
|
|
42
|
-
|
|
43
|
-
sensitivePatterns.forEach((pattern) => {
|
|
44
|
-
const inBinary = str.includes(pattern);
|
|
45
|
-
const inJS = js.includes(pattern);
|
|
46
|
-
console.log(` ${pattern}:`);
|
|
47
|
-
console.log(` In JS bundle: ${inJS ? 'YES' : 'NO'}`);
|
|
48
|
-
console.log(` In binary: ${inBinary ? 'YES' : 'NO'}`);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
console.log('');
|
|
52
|
-
console.log('='.repeat(60));
|
package/test-binary2.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
const binaryPath = '/Users/raymond/pave-apps/openpave/src/packages/pave/dist/bin/pave-darwin-arm64';
|
|
5
|
-
const binary = fs.readFileSync(binaryPath);
|
|
6
|
-
const str = binary.toString('utf8');
|
|
7
|
-
|
|
8
|
-
// Try to find and extract readable code chunks
|
|
9
|
-
console.log('='.repeat(60));
|
|
10
|
-
console.log('Checking if source code is readable in binary...');
|
|
11
|
-
console.log('='.repeat(60));
|
|
12
|
-
|
|
13
|
-
// Search for newGlobal context
|
|
14
|
-
const newGlobalIndex = str.indexOf('newGlobal');
|
|
15
|
-
if (newGlobalIndex !== -1) {
|
|
16
|
-
console.log('\nContext around "newGlobal":');
|
|
17
|
-
const start = Math.max(0, newGlobalIndex - 100);
|
|
18
|
-
const end = Math.min(str.length, newGlobalIndex + 200);
|
|
19
|
-
const context = str.slice(start, end);
|
|
20
|
-
// Show only printable characters
|
|
21
|
-
const printable = context.replace(/[^\x20-\x7E\n]/g, '.');
|
|
22
|
-
console.log(printable);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Search for os.system context
|
|
26
|
-
const osSystemIndex = str.indexOf('os.system');
|
|
27
|
-
if (osSystemIndex !== -1) {
|
|
28
|
-
console.log('\n\nContext around "os.system":');
|
|
29
|
-
const start = Math.max(0, osSystemIndex - 100);
|
|
30
|
-
const end = Math.min(str.length, osSystemIndex + 200);
|
|
31
|
-
const context = str.slice(start, end);
|
|
32
|
-
const printable = context.replace(/[^\x20-\x7E\n]/g, '.');
|
|
33
|
-
console.log(printable);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log('\n' + '='.repeat(60));
|