recker 1.0.23 → 1.0.26
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/cli/handler.js +3 -5
- package/dist/cli/tui/search-panel.d.ts +2 -0
- package/dist/cli/tui/search-panel.js +15 -5
- package/dist/cli/tui/shell-search.d.ts +6 -0
- package/dist/cli/tui/shell-search.js +102 -37
- package/dist/cli/tui/shell.js +4 -4
- package/dist/cli/tui/spinner.d.ts +16 -0
- package/dist/cli/tui/spinner.js +97 -0
- package/package.json +1 -3
package/dist/cli/handler.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { createClient } from '../core/client.js';
|
|
2
2
|
import { requireOptional } from '../utils/optional-require.js';
|
|
3
3
|
import colors from '../utils/colors.js';
|
|
4
|
-
import
|
|
4
|
+
import { createSpinner } from './tui/spinner.js';
|
|
5
5
|
let highlight;
|
|
6
|
-
const ora = oraImport;
|
|
7
6
|
async function initDependencies() {
|
|
8
7
|
if (!highlight) {
|
|
9
8
|
try {
|
|
@@ -24,10 +23,9 @@ export async function handleRequest(options) {
|
|
|
24
23
|
}
|
|
25
24
|
});
|
|
26
25
|
}
|
|
27
|
-
const spinner = options.quiet ? null :
|
|
26
|
+
const spinner = options.quiet ? null : createSpinner({
|
|
28
27
|
text: `${colors.bold(options.method)} ${colors.cyan(options.url)}`,
|
|
29
|
-
color: 'cyan'
|
|
30
|
-
spinner: 'dots'
|
|
28
|
+
color: 'cyan'
|
|
31
29
|
}).start();
|
|
32
30
|
const start = performance.now();
|
|
33
31
|
try {
|
|
@@ -13,6 +13,8 @@ export declare class SearchPanel {
|
|
|
13
13
|
private rightPanelWidth;
|
|
14
14
|
private contentHeight;
|
|
15
15
|
private previewContent;
|
|
16
|
+
private keyHandler;
|
|
17
|
+
private resizeHandler;
|
|
16
18
|
constructor(options?: SearchPanelOptions);
|
|
17
19
|
private findDocsPath;
|
|
18
20
|
open(): Promise<void>;
|
|
@@ -34,6 +34,8 @@ export class SearchPanel {
|
|
|
34
34
|
rightPanelWidth = 0;
|
|
35
35
|
contentHeight = 0;
|
|
36
36
|
previewContent = [];
|
|
37
|
+
keyHandler = null;
|
|
38
|
+
resizeHandler = null;
|
|
37
39
|
constructor(options = {}) {
|
|
38
40
|
this.state = {
|
|
39
41
|
query: options.initialQuery || '',
|
|
@@ -69,11 +71,13 @@ export class SearchPanel {
|
|
|
69
71
|
if (process.stdin.isTTY) {
|
|
70
72
|
process.stdin.setRawMode(true);
|
|
71
73
|
}
|
|
72
|
-
|
|
74
|
+
this.resizeHandler = () => {
|
|
73
75
|
this.updateDimensions();
|
|
74
76
|
this.render();
|
|
75
|
-
}
|
|
76
|
-
process.
|
|
77
|
+
};
|
|
78
|
+
process.stdout.on('resize', this.resizeHandler);
|
|
79
|
+
this.keyHandler = this.handleKeyInput.bind(this);
|
|
80
|
+
process.stdin.on('data', this.keyHandler);
|
|
77
81
|
if (this.state.query) {
|
|
78
82
|
await this.performSearch();
|
|
79
83
|
}
|
|
@@ -99,8 +103,14 @@ export class SearchPanel {
|
|
|
99
103
|
this.rl.close();
|
|
100
104
|
this.rl = null;
|
|
101
105
|
}
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
if (this.keyHandler) {
|
|
107
|
+
process.stdin.removeListener('data', this.keyHandler);
|
|
108
|
+
this.keyHandler = null;
|
|
109
|
+
}
|
|
110
|
+
if (this.resizeHandler) {
|
|
111
|
+
process.stdout.removeListener('resize', this.resizeHandler);
|
|
112
|
+
this.resizeHandler = null;
|
|
113
|
+
}
|
|
104
114
|
}
|
|
105
115
|
updateDimensions() {
|
|
106
116
|
this.termWidth = process.stdout.columns || 80;
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import type { SearchResult } from '../../mcp/search/types.js';
|
|
2
|
+
export type ProgressCallback = (stage: string, percent?: number) => void;
|
|
2
3
|
export declare class ShellSearch {
|
|
3
4
|
private hybridSearch;
|
|
4
5
|
private docsIndex;
|
|
5
6
|
private codeExamples;
|
|
6
7
|
private typeDefinitions;
|
|
7
8
|
private initialized;
|
|
9
|
+
private initializing;
|
|
8
10
|
private idleTimer;
|
|
9
11
|
private docsPath;
|
|
10
12
|
private examplesPath;
|
|
11
13
|
private srcPath;
|
|
14
|
+
private spinner;
|
|
15
|
+
private hasSemanticSearch;
|
|
12
16
|
constructor();
|
|
17
|
+
private updateSpinner;
|
|
13
18
|
private ensureInitialized;
|
|
14
19
|
private resetIdleTimer;
|
|
15
20
|
private unload;
|
|
16
21
|
search(query: string, options?: {
|
|
17
22
|
limit?: number;
|
|
18
23
|
category?: string;
|
|
24
|
+
silent?: boolean;
|
|
19
25
|
}): Promise<SearchResult[]>;
|
|
20
26
|
suggest(useCase: string): Promise<string>;
|
|
21
27
|
getExamples(feature: string, options?: {
|
|
@@ -2,6 +2,7 @@ import { createHybridSearch, createEmbedder, isFastembedAvailable } from '../../
|
|
|
2
2
|
import { readFileSync, readdirSync, statSync, existsSync } from 'fs';
|
|
3
3
|
import { join, relative, extname, basename, dirname } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
|
+
import { createSpinner } from './spinner.js';
|
|
5
6
|
const IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
6
7
|
export class ShellSearch {
|
|
7
8
|
hybridSearch = null;
|
|
@@ -9,27 +10,61 @@ export class ShellSearch {
|
|
|
9
10
|
codeExamples = [];
|
|
10
11
|
typeDefinitions = [];
|
|
11
12
|
initialized = false;
|
|
13
|
+
initializing = false;
|
|
12
14
|
idleTimer = null;
|
|
13
15
|
docsPath;
|
|
14
16
|
examplesPath;
|
|
15
17
|
srcPath;
|
|
18
|
+
spinner = null;
|
|
19
|
+
hasSemanticSearch = false;
|
|
16
20
|
constructor() {
|
|
17
21
|
this.docsPath = this.findDocsPath();
|
|
18
22
|
this.examplesPath = this.findExamplesPath();
|
|
19
23
|
this.srcPath = this.findSrcPath();
|
|
20
24
|
}
|
|
25
|
+
updateSpinner(text) {
|
|
26
|
+
if (this.spinner) {
|
|
27
|
+
this.spinner.text = text;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
21
30
|
async ensureInitialized() {
|
|
22
31
|
this.resetIdleTimer();
|
|
23
32
|
if (this.initialized && this.hybridSearch) {
|
|
24
33
|
return;
|
|
25
34
|
}
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
if (this.initializing) {
|
|
36
|
+
while (this.initializing) {
|
|
37
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.initializing = true;
|
|
42
|
+
this.spinner = createSpinner({ text: 'Initializing search...' }).start();
|
|
43
|
+
try {
|
|
44
|
+
this.updateSpinner('Creating search index...');
|
|
45
|
+
this.hybridSearch = createHybridSearch({ debug: false });
|
|
46
|
+
this.updateSpinner('Checking semantic search availability...');
|
|
47
|
+
const fastembedAvailable = await isFastembedAvailable();
|
|
48
|
+
if (fastembedAvailable) {
|
|
49
|
+
this.updateSpinner('Loading AI embedding model (first time may take a while)...');
|
|
50
|
+
this.hybridSearch.setEmbedder(createEmbedder());
|
|
51
|
+
this.hasSemanticSearch = true;
|
|
52
|
+
}
|
|
53
|
+
this.updateSpinner('Indexing documentation...');
|
|
54
|
+
this.buildIndex();
|
|
55
|
+
this.updateSpinner('Finalizing search index...');
|
|
56
|
+
await this.hybridSearch.initialize(this.docsIndex);
|
|
57
|
+
this.initialized = true;
|
|
58
|
+
this.spinner.succeed(`Search ready (${this.docsIndex.length} docs${this.hasSemanticSearch ? ', semantic enabled' : ''})`);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
this.spinner.fail(`Search initialization failed: ${error}`);
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
this.initializing = false;
|
|
66
|
+
this.spinner = null;
|
|
29
67
|
}
|
|
30
|
-
this.buildIndex();
|
|
31
|
-
await this.hybridSearch.initialize(this.docsIndex);
|
|
32
|
-
this.initialized = true;
|
|
33
68
|
}
|
|
34
69
|
resetIdleTimer() {
|
|
35
70
|
if (this.idleTimer) {
|
|
@@ -48,22 +83,46 @@ export class ShellSearch {
|
|
|
48
83
|
}
|
|
49
84
|
async search(query, options = {}) {
|
|
50
85
|
await this.ensureInitialized();
|
|
51
|
-
const { limit = 5, category } = options;
|
|
86
|
+
const { limit = 5, category, silent = false } = options;
|
|
52
87
|
if (!this.hybridSearch) {
|
|
53
88
|
return [];
|
|
54
89
|
}
|
|
55
|
-
|
|
90
|
+
let searchSpinner = null;
|
|
91
|
+
if (!silent && this.hasSemanticSearch) {
|
|
92
|
+
searchSpinner = createSpinner({ text: 'Generating query embedding...' }).start();
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
if (searchSpinner) {
|
|
96
|
+
searchSpinner.text = 'Searching documentation...';
|
|
97
|
+
}
|
|
98
|
+
const results = await this.hybridSearch.search(query, { limit, category, mode: 'hybrid' });
|
|
99
|
+
if (searchSpinner) {
|
|
100
|
+
searchSpinner.succeed(`Found ${results.length} result${results.length !== 1 ? 's' : ''}`);
|
|
101
|
+
}
|
|
102
|
+
return results;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (searchSpinner) {
|
|
106
|
+
searchSpinner.fail(`Search failed: ${error}`);
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
56
110
|
}
|
|
57
111
|
async suggest(useCase) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
112
|
+
const spinner = createSpinner({ text: 'Generating suggestions...' }).start();
|
|
113
|
+
try {
|
|
114
|
+
await this.ensureInitialized();
|
|
115
|
+
spinner.text = 'Finding relevant documentation...';
|
|
116
|
+
const results = await this.search(useCase, { limit: 3, silent: true });
|
|
117
|
+
if (results.length === 0) {
|
|
118
|
+
spinner.info('No suggestions found');
|
|
119
|
+
return `No suggestions found for: "${useCase}"\n\nTry searching for specific features like:\n - retry\n - cache\n - streaming\n - websocket\n - pagination`;
|
|
120
|
+
}
|
|
121
|
+
spinner.text = 'Building suggestions...';
|
|
122
|
+
const useCaseLower = useCase.toLowerCase();
|
|
123
|
+
const suggestions = [];
|
|
124
|
+
if (useCaseLower.includes('retry') || useCaseLower.includes('fail') || useCaseLower.includes('error')) {
|
|
125
|
+
suggestions.push(`\n**Retry Configuration:**
|
|
67
126
|
\`\`\`typescript
|
|
68
127
|
import { createClient } from 'recker';
|
|
69
128
|
|
|
@@ -77,9 +136,9 @@ const client = createClient({
|
|
|
77
136
|
}
|
|
78
137
|
});
|
|
79
138
|
\`\`\``);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
139
|
+
}
|
|
140
|
+
if (useCaseLower.includes('cache') || useCaseLower.includes('storage')) {
|
|
141
|
+
suggestions.push(`\n**Cache Configuration:**
|
|
83
142
|
\`\`\`typescript
|
|
84
143
|
import { createClient } from 'recker';
|
|
85
144
|
|
|
@@ -92,9 +151,9 @@ const client = createClient({
|
|
|
92
151
|
}
|
|
93
152
|
});
|
|
94
153
|
\`\`\``);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
154
|
+
}
|
|
155
|
+
if (useCaseLower.includes('stream') || useCaseLower.includes('sse') || useCaseLower.includes('ai') || useCaseLower.includes('openai')) {
|
|
156
|
+
suggestions.push(`\n**Streaming Configuration:**
|
|
98
157
|
\`\`\`typescript
|
|
99
158
|
import { createClient } from 'recker';
|
|
100
159
|
|
|
@@ -105,9 +164,9 @@ for await (const event of client.post('/v1/chat/completions', { body, stream: tr
|
|
|
105
164
|
console.log(event.data);
|
|
106
165
|
}
|
|
107
166
|
\`\`\``);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
167
|
+
}
|
|
168
|
+
if (useCaseLower.includes('parallel') || useCaseLower.includes('batch') || useCaseLower.includes('concurrent')) {
|
|
169
|
+
suggestions.push(`\n**Batch/Parallel Requests:**
|
|
111
170
|
\`\`\`typescript
|
|
112
171
|
import { createClient } from 'recker';
|
|
113
172
|
|
|
@@ -122,19 +181,25 @@ const { results, stats } = await client.batch([
|
|
|
122
181
|
{ path: '/users/3' }
|
|
123
182
|
], { mapResponse: r => r.json() });
|
|
124
183
|
\`\`\``);
|
|
125
|
-
}
|
|
126
|
-
let output = `**Suggestion for: "${useCase}"**\n`;
|
|
127
|
-
if (suggestions.length > 0) {
|
|
128
|
-
output += suggestions.join('\n');
|
|
129
|
-
}
|
|
130
|
-
output += `\n\n**Related Documentation:**\n`;
|
|
131
|
-
for (const result of results) {
|
|
132
|
-
output += ` - ${result.title} (${result.path})\n`;
|
|
133
|
-
if (result.snippet) {
|
|
134
|
-
output += ` ${result.snippet.slice(0, 100)}...\n`;
|
|
135
184
|
}
|
|
185
|
+
let output = `**Suggestion for: "${useCase}"**\n`;
|
|
186
|
+
if (suggestions.length > 0) {
|
|
187
|
+
output += suggestions.join('\n');
|
|
188
|
+
}
|
|
189
|
+
output += `\n\n**Related Documentation:**\n`;
|
|
190
|
+
for (const result of results) {
|
|
191
|
+
output += ` - ${result.title} (${result.path})\n`;
|
|
192
|
+
if (result.snippet) {
|
|
193
|
+
output += ` ${result.snippet.slice(0, 100)}...\n`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
spinner.succeed('Suggestions ready');
|
|
197
|
+
return output;
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
spinner.fail(`Failed to generate suggestions: ${error}`);
|
|
201
|
+
throw error;
|
|
136
202
|
}
|
|
137
|
-
return output;
|
|
138
203
|
}
|
|
139
204
|
async getExamples(feature, options = {}) {
|
|
140
205
|
await this.ensureInitialized();
|
package/dist/cli/tui/shell.js
CHANGED
|
@@ -13,7 +13,7 @@ import { ScrapeDocument } from '../../scrape/document.js';
|
|
|
13
13
|
import colors from '../../utils/colors.js';
|
|
14
14
|
import { getShellSearch } from './shell-search.js';
|
|
15
15
|
import { openSearchPanel } from './search-panel.js';
|
|
16
|
-
import { ScrollBuffer, parseScrollKey, parseMouseScroll,
|
|
16
|
+
import { ScrollBuffer, parseScrollKey, parseMouseScroll, disableMouseReporting } from './scroll-buffer.js';
|
|
17
17
|
let highlight;
|
|
18
18
|
async function initDependencies() {
|
|
19
19
|
if (!highlight) {
|
|
@@ -106,7 +106,7 @@ export class RekShell {
|
|
|
106
106
|
console.clear();
|
|
107
107
|
console.log(colors.bold(colors.cyan('Rek Console')));
|
|
108
108
|
console.log(colors.gray('Chat with your APIs. Type "help" for magic.'));
|
|
109
|
-
console.log(colors.gray('Page Up/Down
|
|
109
|
+
console.log(colors.gray('Use Page Up/Down to view history.'));
|
|
110
110
|
console.log(colors.gray('--------------------------------------------\n'));
|
|
111
111
|
this.prompt();
|
|
112
112
|
this.rl.on('line', async (line) => {
|
|
@@ -150,7 +150,6 @@ export class RekShell {
|
|
|
150
150
|
disableMouseReporting();
|
|
151
151
|
}
|
|
152
152
|
setupScrollKeyHandler() {
|
|
153
|
-
enableMouseReporting();
|
|
154
153
|
if (process.stdin.isTTY) {
|
|
155
154
|
const originalEmit = process.stdin.emit.bind(process.stdin);
|
|
156
155
|
const self = this;
|
|
@@ -2060,10 +2059,12 @@ ${colors.bold('Network:')}
|
|
|
2060
2059
|
this.rl.pause();
|
|
2061
2060
|
await openSearchPanel(query.trim() || undefined);
|
|
2062
2061
|
this.rl.resume();
|
|
2062
|
+
this.prompt();
|
|
2063
2063
|
}
|
|
2064
2064
|
catch (error) {
|
|
2065
2065
|
console.error(colors.red(`Search failed: ${error.message}`));
|
|
2066
2066
|
this.rl.resume();
|
|
2067
|
+
this.prompt();
|
|
2067
2068
|
}
|
|
2068
2069
|
}
|
|
2069
2070
|
async runSuggest(useCase) {
|
|
@@ -2209,7 +2210,6 @@ ${colors.bold('Network:')}
|
|
|
2209
2210
|
${colors.bold('Navigation:')}
|
|
2210
2211
|
${colors.green('Page Up/Down')} Scroll through command history.
|
|
2211
2212
|
${colors.green('Home/End')} Jump to top/bottom of history.
|
|
2212
|
-
${colors.green('Mouse Scroll')} Scroll with mouse wheel.
|
|
2213
2213
|
${colors.green('Escape')} Exit scroll mode.
|
|
2214
2214
|
|
|
2215
2215
|
${colors.bold('Examples:')}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SpinnerOptions {
|
|
2
|
+
text?: string;
|
|
3
|
+
color?: 'cyan' | 'green' | 'yellow' | 'red' | 'blue' | 'magenta' | 'white' | 'gray';
|
|
4
|
+
}
|
|
5
|
+
export interface Spinner {
|
|
6
|
+
text: string;
|
|
7
|
+
start(): Spinner;
|
|
8
|
+
stop(): Spinner;
|
|
9
|
+
succeed(text?: string): Spinner;
|
|
10
|
+
fail(text?: string): Spinner;
|
|
11
|
+
info(text?: string): Spinner;
|
|
12
|
+
warn(text?: string): Spinner;
|
|
13
|
+
isSpinning: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function createSpinner(options?: SpinnerOptions | string): Spinner;
|
|
16
|
+
export default createSpinner;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import colors from '../../utils/colors.js';
|
|
2
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
3
|
+
const FRAME_INTERVAL = 80;
|
|
4
|
+
class TerminalSpinner {
|
|
5
|
+
_text;
|
|
6
|
+
color;
|
|
7
|
+
frameIndex = 0;
|
|
8
|
+
intervalId = null;
|
|
9
|
+
stream = process.stderr;
|
|
10
|
+
isSpinning = false;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this._text = options.text || '';
|
|
13
|
+
this.color = this.getColorFn(options.color || 'cyan');
|
|
14
|
+
}
|
|
15
|
+
getColorFn(color) {
|
|
16
|
+
switch (color) {
|
|
17
|
+
case 'green': return colors.green;
|
|
18
|
+
case 'yellow': return colors.yellow;
|
|
19
|
+
case 'red': return colors.red;
|
|
20
|
+
case 'blue': return colors.blue;
|
|
21
|
+
case 'magenta': return colors.magenta;
|
|
22
|
+
case 'gray': return colors.gray;
|
|
23
|
+
case 'white': return (s) => s;
|
|
24
|
+
case 'cyan':
|
|
25
|
+
default: return colors.cyan;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
get text() {
|
|
29
|
+
return this._text;
|
|
30
|
+
}
|
|
31
|
+
set text(value) {
|
|
32
|
+
this._text = value;
|
|
33
|
+
}
|
|
34
|
+
start() {
|
|
35
|
+
if (this.isSpinning)
|
|
36
|
+
return this;
|
|
37
|
+
this.isSpinning = true;
|
|
38
|
+
this.frameIndex = 0;
|
|
39
|
+
this.stream.write('\x1b[?25l');
|
|
40
|
+
this.intervalId = setInterval(() => {
|
|
41
|
+
this.render();
|
|
42
|
+
this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
|
|
43
|
+
}, FRAME_INTERVAL);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
render() {
|
|
47
|
+
const frame = SPINNER_FRAMES[this.frameIndex];
|
|
48
|
+
const line = `${this.color(frame)} ${this._text}`;
|
|
49
|
+
this.stream.write(`\r\x1b[K${line}`);
|
|
50
|
+
}
|
|
51
|
+
clearLine() {
|
|
52
|
+
this.stream.write('\r\x1b[K');
|
|
53
|
+
}
|
|
54
|
+
stop() {
|
|
55
|
+
if (!this.isSpinning)
|
|
56
|
+
return this;
|
|
57
|
+
if (this.intervalId) {
|
|
58
|
+
clearInterval(this.intervalId);
|
|
59
|
+
this.intervalId = null;
|
|
60
|
+
}
|
|
61
|
+
this.isSpinning = false;
|
|
62
|
+
this.clearLine();
|
|
63
|
+
this.stream.write('\x1b[?25h');
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
succeed(text) {
|
|
67
|
+
this.stop();
|
|
68
|
+
const finalText = text ?? this._text;
|
|
69
|
+
console.log(`${colors.green('✔')} ${finalText}`);
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
fail(text) {
|
|
73
|
+
this.stop();
|
|
74
|
+
const finalText = text ?? this._text;
|
|
75
|
+
console.log(`${colors.red('✖')} ${finalText}`);
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
info(text) {
|
|
79
|
+
this.stop();
|
|
80
|
+
const finalText = text ?? this._text;
|
|
81
|
+
console.log(`${colors.blue('ℹ')} ${finalText}`);
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
warn(text) {
|
|
85
|
+
this.stop();
|
|
86
|
+
const finalText = text ?? this._text;
|
|
87
|
+
console.log(`${colors.yellow('⚠')} ${finalText}`);
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function createSpinner(options) {
|
|
92
|
+
if (typeof options === 'string') {
|
|
93
|
+
return new TerminalSpinner({ text: options });
|
|
94
|
+
}
|
|
95
|
+
return new TerminalSpinner(options);
|
|
96
|
+
}
|
|
97
|
+
export default createSpinner;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "recker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "AI & DevX focused HTTP client for Node.js 18+",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -131,7 +131,6 @@
|
|
|
131
131
|
"cheerio": "^1.0.0",
|
|
132
132
|
"commander": "^14.0.2",
|
|
133
133
|
"fuse.js": "^7.1.0",
|
|
134
|
-
"ora": "^9.0.0",
|
|
135
134
|
"undici": "^7.16.0",
|
|
136
135
|
"zod": "^4.1.13"
|
|
137
136
|
},
|
|
@@ -174,7 +173,6 @@
|
|
|
174
173
|
"mitata": "^1.0.34",
|
|
175
174
|
"needle": "^3.3.1",
|
|
176
175
|
"node-fetch": "^3.3.2",
|
|
177
|
-
"ora": "^9.0.0",
|
|
178
176
|
"picocolors": "^1.1.1",
|
|
179
177
|
"popsicle": "^12.1.2",
|
|
180
178
|
"serve": "^14.2.5",
|