felo-ai 0.2.2 → 0.2.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/package.json +1 -1
- package/src/cli.js +40 -12
- package/src/search.js +2 -2
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -9,6 +9,31 @@ import * as config from './config.js';
|
|
|
9
9
|
const require = createRequire(import.meta.url);
|
|
10
10
|
const pkg = require('../package.json');
|
|
11
11
|
|
|
12
|
+
/** Delay (ms) before process.exit to let Windows libuv finish handle cleanup. */
|
|
13
|
+
const EXIT_DELAY_MS = 50;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Flush stdout then stderr, then exit after a short delay. Avoids Node.js
|
|
17
|
+
* Windows UV_HANDLE_CLOSING assertion when process.exit() runs while streams
|
|
18
|
+
* or other handles are still closing.
|
|
19
|
+
* @param {number} code - Exit code.
|
|
20
|
+
*/
|
|
21
|
+
function flushStdioThenExit(code) {
|
|
22
|
+
const doExit = () => setTimeout(() => process.exit(code), EXIT_DELAY_MS);
|
|
23
|
+
const flushStderr = () => {
|
|
24
|
+
if (process.stderr?.writable && !process.stderr.destroyed) {
|
|
25
|
+
process.stderr.write('', () => doExit());
|
|
26
|
+
} else {
|
|
27
|
+
doExit();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
if (process.stdout?.writable && !process.stdout.destroyed) {
|
|
31
|
+
process.stdout.write('', () => flushStderr());
|
|
32
|
+
} else {
|
|
33
|
+
flushStderr();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
12
37
|
const program = new Command();
|
|
13
38
|
|
|
14
39
|
program
|
|
@@ -31,8 +56,7 @@ program
|
|
|
31
56
|
timeoutMs: Number.isNaN(timeoutMs) ? 60000 : timeoutMs,
|
|
32
57
|
});
|
|
33
58
|
process.exitCode = code;
|
|
34
|
-
|
|
35
|
-
setTimeout(() => process.exit(code), 0);
|
|
59
|
+
flushStdioThenExit(code);
|
|
36
60
|
});
|
|
37
61
|
|
|
38
62
|
program
|
|
@@ -53,8 +77,7 @@ program
|
|
|
53
77
|
pollTimeoutMs: Number.isNaN(pollTimeoutMs) ? 1_200_000 : pollTimeoutMs,
|
|
54
78
|
});
|
|
55
79
|
process.exitCode = code;
|
|
56
|
-
|
|
57
|
-
setTimeout(() => process.exit(code), 0);
|
|
80
|
+
flushStdioThenExit(code);
|
|
58
81
|
});
|
|
59
82
|
|
|
60
83
|
const configCmd = program
|
|
@@ -68,9 +91,10 @@ configCmd
|
|
|
68
91
|
try {
|
|
69
92
|
await config.setConfig(key, value);
|
|
70
93
|
console.log(`Set ${key}`);
|
|
94
|
+
flushStdioThenExit(0);
|
|
71
95
|
} catch (e) {
|
|
72
96
|
console.error('Error:', e.message);
|
|
73
|
-
|
|
97
|
+
flushStdioThenExit(1);
|
|
74
98
|
}
|
|
75
99
|
});
|
|
76
100
|
|
|
@@ -85,9 +109,10 @@ configCmd
|
|
|
85
109
|
} else {
|
|
86
110
|
console.log(config.maskValueForDisplay(key, value));
|
|
87
111
|
}
|
|
112
|
+
flushStdioThenExit(0);
|
|
88
113
|
} catch (e) {
|
|
89
114
|
console.error('Error:', e.message);
|
|
90
|
-
|
|
115
|
+
flushStdioThenExit(1);
|
|
91
116
|
}
|
|
92
117
|
});
|
|
93
118
|
|
|
@@ -100,12 +125,13 @@ configCmd
|
|
|
100
125
|
const keys = Object.keys(c);
|
|
101
126
|
if (keys.length === 0) {
|
|
102
127
|
console.log('No config set. Use: felo config set FELO_API_KEY <key>');
|
|
103
|
-
|
|
128
|
+
} else {
|
|
129
|
+
keys.forEach((k) => console.log(k));
|
|
104
130
|
}
|
|
105
|
-
|
|
131
|
+
flushStdioThenExit(0);
|
|
106
132
|
} catch (e) {
|
|
107
133
|
console.error('Error:', e.message);
|
|
108
|
-
|
|
134
|
+
flushStdioThenExit(1);
|
|
109
135
|
}
|
|
110
136
|
});
|
|
111
137
|
|
|
@@ -116,9 +142,10 @@ configCmd
|
|
|
116
142
|
try {
|
|
117
143
|
await config.unsetConfig(key);
|
|
118
144
|
console.log(`Unset ${key}`);
|
|
145
|
+
flushStdioThenExit(0);
|
|
119
146
|
} catch (e) {
|
|
120
147
|
console.error('Error:', e.message);
|
|
121
|
-
|
|
148
|
+
flushStdioThenExit(1);
|
|
122
149
|
}
|
|
123
150
|
});
|
|
124
151
|
|
|
@@ -127,6 +154,7 @@ configCmd
|
|
|
127
154
|
.description('Show config file path')
|
|
128
155
|
.action(() => {
|
|
129
156
|
console.log(config.getConfigPath());
|
|
157
|
+
flushStdioThenExit(0);
|
|
130
158
|
});
|
|
131
159
|
|
|
132
160
|
program
|
|
@@ -135,7 +163,7 @@ program
|
|
|
135
163
|
.argument('[input]', 'text or URL to summarize')
|
|
136
164
|
.action(() => {
|
|
137
165
|
console.error('summarize: not yet implemented. Use felo search for now.');
|
|
138
|
-
|
|
166
|
+
flushStdioThenExit(1);
|
|
139
167
|
});
|
|
140
168
|
|
|
141
169
|
program
|
|
@@ -144,7 +172,7 @@ program
|
|
|
144
172
|
.argument('[text]', 'text to translate')
|
|
145
173
|
.action(() => {
|
|
146
174
|
console.error('translate: not yet implemented. Use felo search for now.');
|
|
147
|
-
|
|
175
|
+
flushStdioThenExit(1);
|
|
148
176
|
});
|
|
149
177
|
|
|
150
178
|
program.parse();
|
package/src/search.js
CHANGED
|
@@ -38,7 +38,6 @@ async function fetchWithTimeoutAndRetry(url, options, timeoutMs = DEFAULT_TIMEOU
|
|
|
38
38
|
...options,
|
|
39
39
|
signal: controller.signal,
|
|
40
40
|
});
|
|
41
|
-
clearTimeout(timeoutId);
|
|
42
41
|
// Retry on 5xx (server errors)
|
|
43
42
|
if (res.status >= 500 && attempt < MAX_RETRIES) {
|
|
44
43
|
const delay = RETRY_BASE_MS * Math.pow(2, attempt);
|
|
@@ -47,7 +46,6 @@ async function fetchWithTimeoutAndRetry(url, options, timeoutMs = DEFAULT_TIMEOU
|
|
|
47
46
|
}
|
|
48
47
|
return res;
|
|
49
48
|
} catch (err) {
|
|
50
|
-
clearTimeout(timeoutId);
|
|
51
49
|
lastError = err;
|
|
52
50
|
if (err.name === 'AbortError') {
|
|
53
51
|
throw new Error(`Request timed out after ${timeoutMs / 1000}s`);
|
|
@@ -58,6 +56,8 @@ async function fetchWithTimeoutAndRetry(url, options, timeoutMs = DEFAULT_TIMEOU
|
|
|
58
56
|
continue;
|
|
59
57
|
}
|
|
60
58
|
throw lastError;
|
|
59
|
+
} finally {
|
|
60
|
+
clearTimeout(timeoutId);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
throw lastError;
|