recker 1.0.15-next.3794a15 → 1.0.15-next.c7370be
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/bench/generator.d.ts.map +1 -1
- package/dist/bench/generator.js +2 -1
- package/dist/bench/stats.d.ts +15 -1
- package/dist/bench/stats.d.ts.map +1 -1
- package/dist/bench/stats.js +84 -5
- package/dist/cli/index.js +21 -0
- package/dist/cli/tui/load-dashboard.d.ts.map +1 -1
- package/dist/cli/tui/load-dashboard.js +62 -8
- package/dist/cli/tui/scroll-buffer.d.ts +43 -0
- package/dist/cli/tui/scroll-buffer.d.ts.map +1 -0
- package/dist/cli/tui/scroll-buffer.js +162 -0
- package/dist/cli/tui/search-panel.d.ts +41 -0
- package/dist/cli/tui/search-panel.d.ts.map +1 -0
- package/dist/cli/tui/search-panel.js +419 -0
- package/dist/cli/tui/shell.d.ts +11 -0
- package/dist/cli/tui/shell.d.ts.map +1 -1
- package/dist/cli/tui/shell.js +189 -30
- package/dist/contract/index.js +3 -2
- package/dist/dns/index.d.ts +1 -0
- package/dist/dns/index.d.ts.map +1 -1
- package/dist/dns/index.js +1 -0
- package/dist/dns/propagation.d.ts +19 -0
- package/dist/dns/propagation.d.ts.map +1 -0
- package/dist/dns/propagation.js +129 -0
- package/dist/mcp/embeddings-loader.d.ts +18 -0
- package/dist/mcp/embeddings-loader.d.ts.map +1 -0
- package/dist/mcp/embeddings-loader.js +152 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/search/hybrid-search.d.ts.map +1 -1
- package/dist/mcp/search/hybrid-search.js +7 -21
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +8 -1
- package/dist/utils/colors.d.ts +16 -0
- package/dist/utils/colors.d.ts.map +1 -1
- package/dist/utils/colors.js +16 -0
- package/package.json +2 -2
- package/dist/mcp/data/embeddings.json +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/bench/generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAa;IACpB,KAAK,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,UAAU;IAKxB,KAAK;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/bench/generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAa;IACpB,KAAK,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,UAAU;IAKxB,KAAK;IAgGX,IAAI;CAGL"}
|
package/dist/bench/generator.js
CHANGED
|
@@ -22,7 +22,7 @@ export class LoadGenerator {
|
|
|
22
22
|
agent: {
|
|
23
23
|
connections: this.config.users,
|
|
24
24
|
pipelining: this.config.mode === 'throughput' ? 2 : 1,
|
|
25
|
-
keepAlive:
|
|
25
|
+
keepAlive: true
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
retry: {
|
|
@@ -41,6 +41,7 @@ export class LoadGenerator {
|
|
|
41
41
|
await new Promise(r => setTimeout(r, 50 + Math.random() * 450));
|
|
42
42
|
}
|
|
43
43
|
const res = await client.get(path, { signal: controller.signal });
|
|
44
|
+
await res.text();
|
|
44
45
|
const duration = performance.now() - start;
|
|
45
46
|
const bytes = Number(res.headers.get('content-length') || 0);
|
|
46
47
|
this.stats.addResult(duration, res.status, bytes);
|
package/dist/bench/stats.d.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
+
export interface ErrorEntry {
|
|
2
|
+
status: number;
|
|
3
|
+
message: string;
|
|
4
|
+
count: number;
|
|
5
|
+
lastSeen: number;
|
|
6
|
+
}
|
|
1
7
|
export declare class LoadStats {
|
|
2
8
|
totalRequests: number;
|
|
3
9
|
successful: number;
|
|
4
10
|
failed: number;
|
|
5
11
|
bytesTransferred: number;
|
|
6
12
|
statusCodes: Record<number, number>;
|
|
7
|
-
|
|
13
|
+
private errorMap;
|
|
14
|
+
private recentErrors;
|
|
15
|
+
private readonly maxRecentErrors;
|
|
8
16
|
latencies: number[];
|
|
9
17
|
private lastSnapshotTime;
|
|
10
18
|
private lastSnapshotRequests;
|
|
11
19
|
activeUsers: number;
|
|
12
20
|
addResult(durationMs: number, status: number, bytes: number, error?: Error): void;
|
|
21
|
+
private trackError;
|
|
22
|
+
private formatErrorMessage;
|
|
23
|
+
private getStatusText;
|
|
24
|
+
getErrors(): ErrorEntry[];
|
|
25
|
+
getRecentErrors(): ErrorEntry[];
|
|
26
|
+
get errors(): Record<string, number>;
|
|
13
27
|
getSnapshot(): {
|
|
14
28
|
rps: number;
|
|
15
29
|
p95: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/bench/stats.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/bench/stats.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,SAAS;IACpB,aAAa,SAAK;IAClB,UAAU,SAAK;IACf,MAAM,SAAK;IACX,gBAAgB,SAAK;IAErB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAKzC,OAAO,CAAC,QAAQ,CAAiC;IAKjD,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAM;IAGtC,SAAS,EAAE,MAAM,EAAE,CAAM;IAGzB,OAAO,CAAC,gBAAgB,CAAc;IACtC,OAAO,CAAC,oBAAoB,CAAK;IAGjC,WAAW,SAAK;IAEhB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;IAoB1E,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,kBAAkB;IAkC1B,OAAO,CAAC,aAAa;IAoBrB,SAAS,IAAI,UAAU,EAAE;IAQzB,eAAe,IAAI,UAAU,EAAE;IAO/B,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOnC;IAED,WAAW;;;;;IAmBX,UAAU;;;;;;;;;;;;;;;;CAmBX"}
|
package/dist/bench/stats.js
CHANGED
|
@@ -4,7 +4,9 @@ export class LoadStats {
|
|
|
4
4
|
failed = 0;
|
|
5
5
|
bytesTransferred = 0;
|
|
6
6
|
statusCodes = {};
|
|
7
|
-
|
|
7
|
+
errorMap = new Map();
|
|
8
|
+
recentErrors = [];
|
|
9
|
+
maxRecentErrors = 10;
|
|
8
10
|
latencies = [];
|
|
9
11
|
lastSnapshotTime = Date.now();
|
|
10
12
|
lastSnapshotRequests = 0;
|
|
@@ -15,10 +17,7 @@ export class LoadStats {
|
|
|
15
17
|
this.latencies.push(durationMs);
|
|
16
18
|
if (error || status >= 400) {
|
|
17
19
|
this.failed++;
|
|
18
|
-
|
|
19
|
-
const msg = error.message || 'Unknown Error';
|
|
20
|
-
this.errors[msg] = (this.errors[msg] || 0) + 1;
|
|
21
|
-
}
|
|
20
|
+
this.trackError(status, error);
|
|
22
21
|
}
|
|
23
22
|
else {
|
|
24
23
|
this.successful++;
|
|
@@ -27,6 +26,86 @@ export class LoadStats {
|
|
|
27
26
|
this.statusCodes[status] = (this.statusCodes[status] || 0) + 1;
|
|
28
27
|
}
|
|
29
28
|
}
|
|
29
|
+
trackError(status, error) {
|
|
30
|
+
const message = this.formatErrorMessage(status, error);
|
|
31
|
+
const key = `${status}:${message}`;
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
const existing = this.errorMap.get(key);
|
|
34
|
+
if (existing) {
|
|
35
|
+
existing.count++;
|
|
36
|
+
existing.lastSeen = now;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const entry = { status, message, count: 1, lastSeen: now };
|
|
40
|
+
this.errorMap.set(key, entry);
|
|
41
|
+
this.recentErrors.push(entry);
|
|
42
|
+
if (this.recentErrors.length > this.maxRecentErrors) {
|
|
43
|
+
this.recentErrors.shift();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
formatErrorMessage(status, error) {
|
|
48
|
+
if (error) {
|
|
49
|
+
let msg = error.message || 'Unknown Error';
|
|
50
|
+
if (msg.includes('ECONNREFUSED'))
|
|
51
|
+
return 'Connection refused';
|
|
52
|
+
if (msg.includes('ECONNRESET'))
|
|
53
|
+
return 'Connection reset';
|
|
54
|
+
if (msg.includes('ETIMEDOUT'))
|
|
55
|
+
return 'Connection timeout';
|
|
56
|
+
if (msg.includes('ENOTFOUND'))
|
|
57
|
+
return 'DNS lookup failed';
|
|
58
|
+
if (msg.includes('UND_ERR_SOCKET'))
|
|
59
|
+
return 'Socket error';
|
|
60
|
+
if (msg.includes('UND_ERR_HEADERS_TIMEOUT'))
|
|
61
|
+
return 'Headers timeout';
|
|
62
|
+
if (msg.includes('UND_ERR_BODY_TIMEOUT'))
|
|
63
|
+
return 'Body timeout';
|
|
64
|
+
if (msg.includes('UND_ERR_CONNECT_TIMEOUT'))
|
|
65
|
+
return 'Connect timeout';
|
|
66
|
+
if (msg.includes('AbortError') || msg.includes('aborted'))
|
|
67
|
+
return 'Request aborted';
|
|
68
|
+
if (msg.length > 50) {
|
|
69
|
+
msg = msg.substring(0, 47) + '...';
|
|
70
|
+
}
|
|
71
|
+
return msg;
|
|
72
|
+
}
|
|
73
|
+
if (status >= 400) {
|
|
74
|
+
return this.getStatusText(status);
|
|
75
|
+
}
|
|
76
|
+
return 'Unknown Error';
|
|
77
|
+
}
|
|
78
|
+
getStatusText(status) {
|
|
79
|
+
const texts = {
|
|
80
|
+
400: 'Bad Request',
|
|
81
|
+
401: 'Unauthorized',
|
|
82
|
+
403: 'Forbidden',
|
|
83
|
+
404: 'Not Found',
|
|
84
|
+
405: 'Method Not Allowed',
|
|
85
|
+
408: 'Request Timeout',
|
|
86
|
+
429: 'Too Many Requests',
|
|
87
|
+
500: 'Internal Server Error',
|
|
88
|
+
502: 'Bad Gateway',
|
|
89
|
+
503: 'Service Unavailable',
|
|
90
|
+
504: 'Gateway Timeout',
|
|
91
|
+
};
|
|
92
|
+
return texts[status] || `HTTP ${status}`;
|
|
93
|
+
}
|
|
94
|
+
getErrors() {
|
|
95
|
+
return Array.from(this.errorMap.values())
|
|
96
|
+
.sort((a, b) => b.count - a.count);
|
|
97
|
+
}
|
|
98
|
+
getRecentErrors() {
|
|
99
|
+
return [...this.recentErrors];
|
|
100
|
+
}
|
|
101
|
+
get errors() {
|
|
102
|
+
const result = {};
|
|
103
|
+
for (const entry of this.errorMap.values()) {
|
|
104
|
+
const key = entry.status > 0 ? `[${entry.status}] ${entry.message}` : entry.message;
|
|
105
|
+
result[key] = entry.count;
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
30
109
|
getSnapshot() {
|
|
31
110
|
const now = Date.now();
|
|
32
111
|
const timeDiff = (now - this.lastSnapshotTime) / 1000;
|
package/dist/cli/index.js
CHANGED
|
@@ -270,6 +270,27 @@ complete -F _rek_completions rek
|
|
|
270
270
|
const shell = new RekShell();
|
|
271
271
|
shell.start();
|
|
272
272
|
});
|
|
273
|
+
program
|
|
274
|
+
.command('docs [query...]')
|
|
275
|
+
.alias('?')
|
|
276
|
+
.description('Search Recker documentation (opens fullscreen panel)')
|
|
277
|
+
.action(async (queryParts) => {
|
|
278
|
+
const query = queryParts.join(' ').trim();
|
|
279
|
+
const { openSearchPanel } = await import('./tui/search-panel.js');
|
|
280
|
+
await openSearchPanel(query || undefined);
|
|
281
|
+
});
|
|
282
|
+
const dns = program.command('dns').description('DNS tools and diagnostics');
|
|
283
|
+
dns
|
|
284
|
+
.command('propagate')
|
|
285
|
+
.description('Check global DNS propagation across multiple providers')
|
|
286
|
+
.argument('<domain>', 'Domain name to check')
|
|
287
|
+
.argument('[type]', 'Record type (A, AAAA, CNAME, MX, NS, TXT)', 'A')
|
|
288
|
+
.action(async (domain, type) => {
|
|
289
|
+
const { checkPropagation, formatPropagationReport } = await import('../dns/propagation.js');
|
|
290
|
+
console.log(pc.gray(`Checking propagation for ${domain} (${type})...`));
|
|
291
|
+
const results = await checkPropagation(domain, type);
|
|
292
|
+
console.log(formatPropagationReport(results, domain, type));
|
|
293
|
+
});
|
|
273
294
|
const bench = program.command('bench').description('Performance benchmarking tools');
|
|
274
295
|
bench
|
|
275
296
|
.command('load')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-dashboard.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/load-dashboard.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,UAAU,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"load-dashboard.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/load-dashboard.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAMrE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,UAAU,iBA4E1D"}
|
|
@@ -72,11 +72,11 @@ function render(config, elapsed, remaining, snapshot, rpsHistory, latencyHistory
|
|
|
72
72
|
console.log(`${pc.white('Time:')} ${pc.green(elapsed + 's')} ` +
|
|
73
73
|
`${pc.gray('/')} ${config.duration}s ` +
|
|
74
74
|
`${pc.gray('(')}${pc.yellow(remaining + 's left')}${pc.gray(')')} ` +
|
|
75
|
-
`${pc.white('Reqs:')} ${pc.bold(stats.totalRequests)}`);
|
|
76
|
-
console.log(`${pc.blue('Users:')} ${pc.bold(snapshot.activeUsers)} ` +
|
|
75
|
+
`${pc.white('Reqs:')} ${pc.bold(String(stats.totalRequests))}`);
|
|
76
|
+
console.log(`${pc.blue('Users:')} ${pc.bold(String(snapshot.activeUsers))} ` +
|
|
77
77
|
`${pc.green('RPS:')} ${pc.bold(snapshot.rps.toFixed(0))} ` +
|
|
78
78
|
`${pc.magenta('Latency (P95):')} ${pc.bold(snapshot.p95.toFixed(0) + 'ms')} ` +
|
|
79
|
-
`${pc.white('Errors:')} ${stats.failed > 0 ? pc.red(stats.failed) : pc.green('0')}`);
|
|
79
|
+
`${pc.white('Errors:')} ${stats.failed > 0 ? pc.red(String(stats.failed)) : pc.green('0')}`);
|
|
80
80
|
console.log(pc.gray('──────────────────────────────────────────────────'));
|
|
81
81
|
console.log(pc.bold(pc.blue('👥 Active Users')));
|
|
82
82
|
console.log(pc.blue(plot(usersHistory, { height: 4 })));
|
|
@@ -86,6 +86,39 @@ function render(config, elapsed, remaining, snapshot, rpsHistory, latencyHistory
|
|
|
86
86
|
console.log('');
|
|
87
87
|
console.log(pc.bold(pc.magenta('⏱️ Latency P95 (ms)')));
|
|
88
88
|
console.log(pc.magenta(plot(latencyHistory, { height: 4 })));
|
|
89
|
+
const recentErrors = stats.getRecentErrors();
|
|
90
|
+
if (recentErrors.length > 0) {
|
|
91
|
+
console.log('');
|
|
92
|
+
console.log(pc.bold(pc.red('⚠️ Recent Errors')));
|
|
93
|
+
renderErrorList(recentErrors, 5);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function formatErrorEntry(entry) {
|
|
97
|
+
const count = pc.gray(`${entry.count}x`);
|
|
98
|
+
if (entry.status === 0) {
|
|
99
|
+
return ` ${count} ${pc.red('NET')} ${entry.message}`;
|
|
100
|
+
}
|
|
101
|
+
const statusBadge = formatStatusBadge(entry.status);
|
|
102
|
+
return ` ${count} ${statusBadge} ${entry.message}`;
|
|
103
|
+
}
|
|
104
|
+
function formatStatusBadge(status) {
|
|
105
|
+
const code = String(status);
|
|
106
|
+
if (status >= 500)
|
|
107
|
+
return pc.bgRed(pc.white(` ${code} `));
|
|
108
|
+
if (status >= 400)
|
|
109
|
+
return pc.bgYellow(pc.black(` ${code} `));
|
|
110
|
+
if (status >= 300)
|
|
111
|
+
return pc.bgCyan(pc.black(` ${code} `));
|
|
112
|
+
return pc.bgGreen(pc.black(` ${code} `));
|
|
113
|
+
}
|
|
114
|
+
function renderErrorList(errors, maxItems = 10) {
|
|
115
|
+
const toShow = errors.slice(-maxItems);
|
|
116
|
+
for (const entry of toShow) {
|
|
117
|
+
console.log(formatErrorEntry(entry));
|
|
118
|
+
}
|
|
119
|
+
if (errors.length > maxItems) {
|
|
120
|
+
console.log(pc.gray(` ... and ${errors.length - maxItems} more`));
|
|
121
|
+
}
|
|
89
122
|
}
|
|
90
123
|
function renderFinalReport(stats, config) {
|
|
91
124
|
const summary = stats.getSummary();
|
|
@@ -97,8 +130,8 @@ function renderFinalReport(stats, config) {
|
|
|
97
130
|
console.log(` Duration: ${config.duration}s`);
|
|
98
131
|
console.log('\n' + pc.bold('Traffic:'));
|
|
99
132
|
console.log(` Total Requests: ${summary.total}`);
|
|
100
|
-
console.log(` Successful: ${pc.green(summary.success)}`);
|
|
101
|
-
console.log(` Failed: ${summary.failed > 0 ? pc.red(summary.failed) : pc.gray(0)}`);
|
|
133
|
+
console.log(` Successful: ${pc.green(String(summary.success))}`);
|
|
134
|
+
console.log(` Failed: ${summary.failed > 0 ? pc.red(String(summary.failed)) : pc.gray('0')}`);
|
|
102
135
|
console.log(` Total Bytes: ${(summary.bytes / 1024 / 1024).toFixed(2)} MB`);
|
|
103
136
|
console.log('\n' + pc.bold('Latency (ms):'));
|
|
104
137
|
console.log(` Avg: ${summary.latency.avg.toFixed(2)}`);
|
|
@@ -108,10 +141,31 @@ function renderFinalReport(stats, config) {
|
|
|
108
141
|
console.log(` Max: ${summary.latency.max.toFixed(0)}`);
|
|
109
142
|
if (Object.keys(summary.codes).length > 0) {
|
|
110
143
|
console.log('\n' + pc.bold('Status Codes:'));
|
|
111
|
-
Object.entries(summary.codes)
|
|
112
|
-
|
|
113
|
-
|
|
144
|
+
Object.entries(summary.codes)
|
|
145
|
+
.sort(([a], [b]) => Number(a) - Number(b))
|
|
146
|
+
.forEach(([code, count]) => {
|
|
147
|
+
const badge = formatStatusBadge(Number(code));
|
|
148
|
+
console.log(` ${badge} ${count}`);
|
|
114
149
|
});
|
|
115
150
|
}
|
|
151
|
+
const allErrors = stats.getErrors();
|
|
152
|
+
if (allErrors.length > 0) {
|
|
153
|
+
console.log('\n' + pc.bold(pc.red('Errors:')));
|
|
154
|
+
renderErrorList(allErrors, 15);
|
|
155
|
+
const networkErrors = allErrors.filter(e => e.status === 0);
|
|
156
|
+
const httpErrors = allErrors.filter(e => e.status > 0);
|
|
157
|
+
if (networkErrors.length > 0 || httpErrors.length > 0) {
|
|
158
|
+
console.log('');
|
|
159
|
+
console.log(pc.gray(' Summary:'));
|
|
160
|
+
if (networkErrors.length > 0) {
|
|
161
|
+
const total = networkErrors.reduce((sum, e) => sum + e.count, 0);
|
|
162
|
+
console.log(pc.gray(` Network errors: ${total} (${networkErrors.length} types)`));
|
|
163
|
+
}
|
|
164
|
+
if (httpErrors.length > 0) {
|
|
165
|
+
const total = httpErrors.reduce((sum, e) => sum + e.count, 0);
|
|
166
|
+
console.log(pc.gray(` HTTP errors: ${total} (${httpErrors.length} types)`));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
116
170
|
console.log('');
|
|
117
171
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
export interface ScrollBufferOptions {
|
|
3
|
+
maxLines?: number;
|
|
4
|
+
viewportHeight?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class ScrollBuffer extends EventEmitter {
|
|
7
|
+
private lines;
|
|
8
|
+
private scrollOffset;
|
|
9
|
+
private maxLines;
|
|
10
|
+
private viewportHeight;
|
|
11
|
+
private isScrollMode;
|
|
12
|
+
private originalWrite;
|
|
13
|
+
private pendingOutput;
|
|
14
|
+
constructor(options?: ScrollBufferOptions);
|
|
15
|
+
write(content: string): void;
|
|
16
|
+
flush(): void;
|
|
17
|
+
get lineCount(): number;
|
|
18
|
+
get position(): number;
|
|
19
|
+
get isScrolledUp(): boolean;
|
|
20
|
+
scrollUp(lines?: number): boolean;
|
|
21
|
+
scrollDown(lines?: number): boolean;
|
|
22
|
+
pageUp(): boolean;
|
|
23
|
+
pageDown(): boolean;
|
|
24
|
+
scrollToTop(): void;
|
|
25
|
+
scrollToBottom(): void;
|
|
26
|
+
getVisibleLines(): string[];
|
|
27
|
+
render(): string;
|
|
28
|
+
updateViewport(height?: number): void;
|
|
29
|
+
enterScrollMode(): void;
|
|
30
|
+
exitScrollMode(): void;
|
|
31
|
+
get inScrollMode(): boolean;
|
|
32
|
+
clear(): void;
|
|
33
|
+
getScrollInfo(): {
|
|
34
|
+
current: number;
|
|
35
|
+
total: number;
|
|
36
|
+
percent: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export declare function parseScrollKey(data: Buffer): 'pageUp' | 'pageDown' | 'scrollUp' | 'scrollDown' | 'home' | 'end' | 'escape' | null;
|
|
40
|
+
export declare function parseMouseScroll(data: Buffer): 'scrollUp' | 'scrollDown' | null;
|
|
41
|
+
export declare function enableMouseReporting(): void;
|
|
42
|
+
export declare function disableMouseReporting(): void;
|
|
43
|
+
//# sourceMappingURL=scroll-buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-buffer.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/scroll-buffer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,aAAa,CAAc;gBAEvB,OAAO,GAAE,mBAAwB;IAS7C,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAuB5B,KAAK,IAAI,IAAI;IAUb,IAAI,SAAS,IAAI,MAAM,CAEtB;IAKD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAKD,IAAI,YAAY,IAAI,OAAO,CAE1B;IAKD,QAAQ,CAAC,KAAK,GAAE,MAAU,GAAG,OAAO;IAcpC,UAAU,CAAC,KAAK,GAAE,MAAU,GAAG,OAAO;IAatC,MAAM,IAAI,OAAO;IAOjB,QAAQ,IAAI,OAAO;IAOnB,WAAW,IAAI,IAAI;IAOnB,cAAc,IAAI,IAAI;IAOtB,eAAe,IAAI,MAAM,EAAE;IAS3B,MAAM,IAAI,MAAM;IAsBhB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAOrC,eAAe,IAAI,IAAI;IAcvB,cAAc,IAAI,IAAI;IAWtB,IAAI,YAAY,IAAI,OAAO,CAE1B;IAKD,KAAK,IAAI,IAAI;IASb,aAAa,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAOrE;AAKD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,IAAI,CAyBjI;AAOD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,IAAI,CAqB/E;AAKD,wBAAgB,oBAAoB,IAAI,IAAI,CAK3C;AAKD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
export class ScrollBuffer extends EventEmitter {
|
|
3
|
+
lines = [];
|
|
4
|
+
scrollOffset = 0;
|
|
5
|
+
maxLines;
|
|
6
|
+
viewportHeight;
|
|
7
|
+
isScrollMode = false;
|
|
8
|
+
originalWrite = null;
|
|
9
|
+
pendingOutput = '';
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
super();
|
|
12
|
+
this.maxLines = options.maxLines || 10000;
|
|
13
|
+
this.viewportHeight = options.viewportHeight || (process.stdout.rows || 24) - 2;
|
|
14
|
+
}
|
|
15
|
+
write(content) {
|
|
16
|
+
const parts = (this.pendingOutput + content).split('\n');
|
|
17
|
+
this.pendingOutput = parts.pop() || '';
|
|
18
|
+
for (const part of parts) {
|
|
19
|
+
this.lines.push(part);
|
|
20
|
+
}
|
|
21
|
+
if (this.lines.length > this.maxLines) {
|
|
22
|
+
this.lines = this.lines.slice(-this.maxLines);
|
|
23
|
+
}
|
|
24
|
+
if (this.scrollOffset === 0 && !this.isScrollMode) {
|
|
25
|
+
this.emit('output', content);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
flush() {
|
|
29
|
+
if (this.pendingOutput) {
|
|
30
|
+
this.lines.push(this.pendingOutput);
|
|
31
|
+
this.pendingOutput = '';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
get lineCount() {
|
|
35
|
+
return this.lines.length;
|
|
36
|
+
}
|
|
37
|
+
get position() {
|
|
38
|
+
return this.scrollOffset;
|
|
39
|
+
}
|
|
40
|
+
get isScrolledUp() {
|
|
41
|
+
return this.scrollOffset > 0;
|
|
42
|
+
}
|
|
43
|
+
scrollUp(lines = 1) {
|
|
44
|
+
const maxScroll = Math.max(0, this.lines.length - this.viewportHeight);
|
|
45
|
+
const newOffset = Math.min(this.scrollOffset + lines, maxScroll);
|
|
46
|
+
if (newOffset !== this.scrollOffset) {
|
|
47
|
+
this.scrollOffset = newOffset;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
scrollDown(lines = 1) {
|
|
53
|
+
const newOffset = Math.max(0, this.scrollOffset - lines);
|
|
54
|
+
if (newOffset !== this.scrollOffset) {
|
|
55
|
+
this.scrollOffset = newOffset;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
pageUp() {
|
|
61
|
+
return this.scrollUp(this.viewportHeight - 1);
|
|
62
|
+
}
|
|
63
|
+
pageDown() {
|
|
64
|
+
return this.scrollDown(this.viewportHeight - 1);
|
|
65
|
+
}
|
|
66
|
+
scrollToTop() {
|
|
67
|
+
this.scrollOffset = Math.max(0, this.lines.length - this.viewportHeight);
|
|
68
|
+
}
|
|
69
|
+
scrollToBottom() {
|
|
70
|
+
this.scrollOffset = 0;
|
|
71
|
+
}
|
|
72
|
+
getVisibleLines() {
|
|
73
|
+
const endIndex = this.lines.length - this.scrollOffset;
|
|
74
|
+
const startIndex = Math.max(0, endIndex - this.viewportHeight);
|
|
75
|
+
return this.lines.slice(startIndex, endIndex);
|
|
76
|
+
}
|
|
77
|
+
render() {
|
|
78
|
+
const visibleLines = this.getVisibleLines();
|
|
79
|
+
let output = '';
|
|
80
|
+
output += '\x1b[2J\x1b[H';
|
|
81
|
+
output += visibleLines.join('\n');
|
|
82
|
+
if (this.scrollOffset > 0) {
|
|
83
|
+
const indicator = `\x1b[7m ↑ ${this.scrollOffset} lines above | Page Down to scroll ↓ \x1b[0m`;
|
|
84
|
+
output += `\n${indicator}`;
|
|
85
|
+
}
|
|
86
|
+
return output;
|
|
87
|
+
}
|
|
88
|
+
updateViewport(height) {
|
|
89
|
+
this.viewportHeight = height || (process.stdout.rows || 24) - 2;
|
|
90
|
+
}
|
|
91
|
+
enterScrollMode() {
|
|
92
|
+
if (this.isScrollMode)
|
|
93
|
+
return;
|
|
94
|
+
this.isScrollMode = true;
|
|
95
|
+
this.flush();
|
|
96
|
+
this.emit('scrollModeStart');
|
|
97
|
+
}
|
|
98
|
+
exitScrollMode() {
|
|
99
|
+
if (!this.isScrollMode)
|
|
100
|
+
return;
|
|
101
|
+
this.isScrollMode = false;
|
|
102
|
+
this.scrollToBottom();
|
|
103
|
+
this.emit('scrollModeEnd');
|
|
104
|
+
}
|
|
105
|
+
get inScrollMode() {
|
|
106
|
+
return this.isScrollMode;
|
|
107
|
+
}
|
|
108
|
+
clear() {
|
|
109
|
+
this.lines = [];
|
|
110
|
+
this.scrollOffset = 0;
|
|
111
|
+
this.pendingOutput = '';
|
|
112
|
+
}
|
|
113
|
+
getScrollInfo() {
|
|
114
|
+
const total = this.lines.length;
|
|
115
|
+
const current = total - this.scrollOffset;
|
|
116
|
+
const percent = total > 0 ? Math.round((current / total) * 100) : 100;
|
|
117
|
+
return { current, total, percent };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export function parseScrollKey(data) {
|
|
121
|
+
const str = data.toString();
|
|
122
|
+
if (str === '\x1b[5~' || str === '\x1bOy')
|
|
123
|
+
return 'pageUp';
|
|
124
|
+
if (str === '\x1b[6~' || str === '\x1bOs')
|
|
125
|
+
return 'pageDown';
|
|
126
|
+
if (str === '\x1b[1;2A')
|
|
127
|
+
return 'scrollUp';
|
|
128
|
+
if (str === '\x1b[1;2B')
|
|
129
|
+
return 'scrollDown';
|
|
130
|
+
if (str === '\x1b[H' || str === '\x1b[1~' || str === '\x1bOH')
|
|
131
|
+
return 'home';
|
|
132
|
+
if (str === '\x1b[F' || str === '\x1b[4~' || str === '\x1bOF')
|
|
133
|
+
return 'end';
|
|
134
|
+
if (str === '\x1b' || str === '\x1b\x1b')
|
|
135
|
+
return 'escape';
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
export function parseMouseScroll(data) {
|
|
139
|
+
const str = data.toString();
|
|
140
|
+
const sgrMatch = str.match(/\x1b\[<(\d+);(\d+);(\d+)[Mm]/);
|
|
141
|
+
if (sgrMatch) {
|
|
142
|
+
const button = parseInt(sgrMatch[1], 10);
|
|
143
|
+
if (button === 64)
|
|
144
|
+
return 'scrollUp';
|
|
145
|
+
if (button === 65)
|
|
146
|
+
return 'scrollDown';
|
|
147
|
+
}
|
|
148
|
+
if (data.length >= 6 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x4d) {
|
|
149
|
+
const button = data[3];
|
|
150
|
+
if (button === 96 || button === 0x60)
|
|
151
|
+
return 'scrollUp';
|
|
152
|
+
if (button === 97 || button === 0x61)
|
|
153
|
+
return 'scrollDown';
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
export function enableMouseReporting() {
|
|
158
|
+
process.stdout.write('\x1b[?1000h\x1b[?1006h');
|
|
159
|
+
}
|
|
160
|
+
export function disableMouseReporting() {
|
|
161
|
+
process.stdout.write('\x1b[?1000l\x1b[?1006l');
|
|
162
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
interface SearchPanelOptions {
|
|
2
|
+
initialQuery?: string;
|
|
3
|
+
docsPath?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class SearchPanel {
|
|
6
|
+
private rl;
|
|
7
|
+
private state;
|
|
8
|
+
private docsPath;
|
|
9
|
+
private running;
|
|
10
|
+
private termWidth;
|
|
11
|
+
private termHeight;
|
|
12
|
+
private leftPanelWidth;
|
|
13
|
+
private rightPanelWidth;
|
|
14
|
+
private contentHeight;
|
|
15
|
+
private previewContent;
|
|
16
|
+
constructor(options?: SearchPanelOptions);
|
|
17
|
+
private findDocsPath;
|
|
18
|
+
open(): Promise<void>;
|
|
19
|
+
close(): void;
|
|
20
|
+
private updateDimensions;
|
|
21
|
+
private handleKeyInput;
|
|
22
|
+
private navigateUp;
|
|
23
|
+
private navigateDown;
|
|
24
|
+
private scrollPreview;
|
|
25
|
+
private performSearch;
|
|
26
|
+
private loadPreview;
|
|
27
|
+
private formatMarkdown;
|
|
28
|
+
private wordWrap;
|
|
29
|
+
private stripAnsi;
|
|
30
|
+
private render;
|
|
31
|
+
private renderHeader;
|
|
32
|
+
private renderSearchInput;
|
|
33
|
+
private renderDivider;
|
|
34
|
+
private renderContent;
|
|
35
|
+
private renderLeftPanelLine;
|
|
36
|
+
private renderRightPanelLine;
|
|
37
|
+
private renderFooter;
|
|
38
|
+
}
|
|
39
|
+
export declare function openSearchPanel(query?: string): Promise<void>;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=search-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-panel.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/search-panel.ts"],"names":[],"mappings":"AAyCA,UAAU,kBAAkB;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAeD,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAgB;gBAE1B,OAAO,GAAE,kBAAuB;IAa5C,OAAO,CAAC,YAAY;IAed,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiD3B,KAAK,IAAI,IAAI;IAsBb,OAAO,CAAC,gBAAgB;YAYV,cAAc;IA8C5B,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,aAAa;YAKP,aAAa;IAgC3B,OAAO,CAAC,WAAW;IAyBnB,OAAO,CAAC,cAAc;IAyCtB,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,MAAM;IA0Bd,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,mBAAmB;IAiC3B,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,YAAY;CA0BrB;AAKD,wBAAsB,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnE"}
|