aether-code 0.40.0 → 0.42.0
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/api.js +24 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aether-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.0",
|
|
4
4
|
"description": "Uncensored AI coding agent for your terminal — Claude Code alternative with MCP support. Reads code, writes files, runs commands. Drives IDA Pro, Roblox Studio, Wireshark, Blender, and any MCP server. No refusal layer.",
|
|
5
5
|
"homepage": "https://trynoguard.com",
|
|
6
6
|
"repository": {
|
package/src/api.js
CHANGED
|
@@ -7,18 +7,37 @@ const USER_AGENT = `aether-code/${getVersion()}`;
|
|
|
7
7
|
|
|
8
8
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
9
9
|
|
|
10
|
+
// Per-request connect timeout. The server retries upstream internally (up to
|
|
11
|
+
// 5 × 75s = 375s worst case), and during that time the CLI's fetch() just
|
|
12
|
+
// hangs with zero feedback. This cap ensures we abort, show a retry message,
|
|
13
|
+
// and try again rather than sitting dead for minutes.
|
|
14
|
+
const CONNECT_TIMEOUT_MS = 90_000;
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* fetch() with bounded exponential-backoff retries. Retries on thrown network
|
|
12
18
|
* errors (DNS/reset/offline blip) and transient 5xx; never retries 4xx
|
|
13
19
|
* (auth/credit/validation — a retry won't help). Body is always a small JSON
|
|
14
20
|
* string, so resending is safe. For the streaming endpoint only the initial
|
|
15
21
|
* connection is retried.
|
|
22
|
+
*
|
|
23
|
+
* Each request has a hard 90s connect timeout via AbortController — if the
|
|
24
|
+
* server hasn't responded with headers by then, we abort and retry. This
|
|
25
|
+
* prevents the CLI from hanging indefinitely while the server retries
|
|
26
|
+
* upstream internally.
|
|
16
27
|
*/
|
|
17
|
-
export async function fetchWithRetry(url, options, { retries =
|
|
28
|
+
export async function fetchWithRetry(url, options, { retries = 4, baseDelay = 1000, timeoutMs = CONNECT_TIMEOUT_MS, onRetry } = {}) {
|
|
18
29
|
let attempt = 0;
|
|
19
30
|
while (true) {
|
|
31
|
+
const ac = new AbortController();
|
|
32
|
+
const timer = setTimeout(() => ac.abort(new Error(`Server did not respond within ${Math.round(timeoutMs / 1000)}s`)), timeoutMs);
|
|
33
|
+
// If the caller already set a signal, chain it so both can abort.
|
|
34
|
+
const callerSignal = options?.signal;
|
|
35
|
+
if (callerSignal) {
|
|
36
|
+
callerSignal.addEventListener("abort", () => ac.abort(callerSignal.reason), { once: true });
|
|
37
|
+
}
|
|
20
38
|
try {
|
|
21
|
-
const res = await fetch(url, options);
|
|
39
|
+
const res = await fetch(url, { ...options, signal: ac.signal });
|
|
40
|
+
clearTimeout(timer);
|
|
22
41
|
if (res.status >= 500 && attempt < retries) {
|
|
23
42
|
// Release the connection back to the pool before retrying — an
|
|
24
43
|
// unconsumed body keeps the socket open (undici won't reuse it).
|
|
@@ -30,6 +49,7 @@ export async function fetchWithRetry(url, options, { retries = 2, baseDelay = 50
|
|
|
30
49
|
}
|
|
31
50
|
return res;
|
|
32
51
|
} catch (e) {
|
|
52
|
+
clearTimeout(timer);
|
|
33
53
|
if (attempt < retries) {
|
|
34
54
|
attempt++;
|
|
35
55
|
if (onRetry) onRetry(attempt, e.message);
|
|
@@ -202,12 +222,12 @@ export async function agentTurnStream({
|
|
|
202
222
|
|
|
203
223
|
// Watchdog: if the open stream stalls (no bytes) for too long, abort instead
|
|
204
224
|
// of hanging the CLI forever.
|
|
205
|
-
const READ_TIMEOUT_MS =
|
|
225
|
+
const READ_TIMEOUT_MS = 180_000;
|
|
206
226
|
const readOnce = () => {
|
|
207
227
|
let timer;
|
|
208
228
|
const timeout = new Promise((_, reject) => {
|
|
209
229
|
timer = setTimeout(
|
|
210
|
-
() => reject(new AetherError(
|
|
230
|
+
() => reject(new AetherError(`Stream stalled — no data for ${Math.round(READ_TIMEOUT_MS / 1000)}s`, "STREAM_TIMEOUT", 0)),
|
|
211
231
|
READ_TIMEOUT_MS,
|
|
212
232
|
);
|
|
213
233
|
});
|