wontopos 2.2.13 → 2.2.14
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/wontopos.d.ts +7 -5
- package/dist/wontopos.js +62 -8
- package/package.json +1 -1
package/dist/wontopos.d.ts
CHANGED
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
* Pure semantic retrieval — no keyword/BM25 matching, so it works the SAME in every
|
|
23
23
|
* language. Storing and searching call no LLM; you only pay for embeddings.
|
|
24
24
|
*
|
|
25
|
-
* Reliability: every call retries transient failures
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* Reliability: every call retries transient failures with exponential backoff +
|
|
26
|
+
* jitter, honoring `Retry-After` — 429 always; 502/503 and connection errors only
|
|
27
|
+
* when a retry can never double-process a write (idempotent calls, or a failure
|
|
28
|
+
* at connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`.
|
|
28
29
|
*
|
|
29
30
|
* Security posture (not configurable off): redirects are refused so the API key
|
|
30
31
|
* can never follow one to another host; responses over 64MB are refused; the key
|
|
@@ -226,8 +227,9 @@ export interface ClientOptions {
|
|
|
226
227
|
/** Default store for every call. Override per call by passing `userId`. Defaults
|
|
227
228
|
* to the account's built-in `default` store. */
|
|
228
229
|
userId?: string;
|
|
229
|
-
/** How many times to retry transient failures
|
|
230
|
-
* errors
|
|
230
|
+
/** How many times to retry transient failures before throwing — 429 always;
|
|
231
|
+
* 502/503 and connection errors only when a retry can never double-process a
|
|
232
|
+
* write. 0 disables retries (default 2). */
|
|
231
233
|
maxRetries?: number;
|
|
232
234
|
}
|
|
233
235
|
/** Wontopos memory client. Store and recall memories, isolated per `userId`. */
|
package/dist/wontopos.js
CHANGED
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
* Pure semantic retrieval — no keyword/BM25 matching, so it works the SAME in every
|
|
23
23
|
* language. Storing and searching call no LLM; you only pay for embeddings.
|
|
24
24
|
*
|
|
25
|
-
* Reliability: every call retries transient failures
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* Reliability: every call retries transient failures with exponential backoff +
|
|
26
|
+
* jitter, honoring `Retry-After` — 429 always; 502/503 and connection errors only
|
|
27
|
+
* when a retry can never double-process a write (idempotent calls, or a failure
|
|
28
|
+
* at connect time). Tune with `maxRetries` (0 disables) and `timeoutMs`.
|
|
28
29
|
*
|
|
29
30
|
* Security posture (not configurable off): redirects are refused so the API key
|
|
30
31
|
* can never follow one to another host; responses over 64MB are refused; the key
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
* runtime's (never disabled by this SDK; Node 18+ floors TLS at 1.2). Prefer
|
|
33
34
|
* `Client.fromEnv()` over keys in source code.
|
|
34
35
|
*/
|
|
35
|
-
const VERSION = "2.2.
|
|
36
|
+
const VERSION = "2.2.14";
|
|
36
37
|
const USER_AGENT = `wontopos-node/${VERSION}`;
|
|
37
38
|
const DEFAULT_BASE_URL = "https://api.wontopos.com";
|
|
38
39
|
const DEFAULT_MODEL = "tablet-1";
|
|
@@ -49,6 +50,37 @@ const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
|
|
|
49
50
|
const MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
50
51
|
/** Model names travel in a header — only header-safe characters. */
|
|
51
52
|
const MODEL_RE = /^[A-Za-z0-9._-]+$/;
|
|
53
|
+
/** Error codes that only occur while ESTABLISHING a connection — the request
|
|
54
|
+
* never reached the server, so a retry can't double-process a write. Mid-stream
|
|
55
|
+
* codes (ECONNRESET, EPIPE, UND_ERR_SOCKET, ETIMEDOUT) are ambiguous: the server
|
|
56
|
+
* may already have processed (and billed) the request, so they're excluded. */
|
|
57
|
+
const CONNECT_FAIL_CODES = new Set([
|
|
58
|
+
"ECONNREFUSED",
|
|
59
|
+
"ENOTFOUND",
|
|
60
|
+
"EAI_AGAIN",
|
|
61
|
+
"EHOSTUNREACH",
|
|
62
|
+
"ENETUNREACH",
|
|
63
|
+
"ENETDOWN",
|
|
64
|
+
"EADDRNOTAVAIL",
|
|
65
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
66
|
+
]);
|
|
67
|
+
/** Walk an error's `cause` chain (and AggregateError members — happy-eyeballs
|
|
68
|
+
* connects) looking for a connect-level failure code. */
|
|
69
|
+
function isConnectFailure(e) {
|
|
70
|
+
const stack = [e];
|
|
71
|
+
for (let steps = 0; stack.length && steps < 24; steps++) {
|
|
72
|
+
const cur = stack.pop();
|
|
73
|
+
if (!cur || typeof cur !== "object")
|
|
74
|
+
continue;
|
|
75
|
+
if (typeof cur.code === "string" && CONNECT_FAIL_CODES.has(cur.code))
|
|
76
|
+
return true;
|
|
77
|
+
if (cur.cause)
|
|
78
|
+
stack.push(cur.cause);
|
|
79
|
+
if (Array.isArray(cur.errors))
|
|
80
|
+
stack.push(...cur.errors.slice(0, 8));
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
52
84
|
/** `wos-abc...wxyz` — enough to tell keys apart, never enough to use. */
|
|
53
85
|
function maskKey(key) {
|
|
54
86
|
return key.length > 12 ? `${key.slice(0, 4)}...${key.slice(-4)}` : "***";
|
|
@@ -152,7 +184,11 @@ export class Client {
|
|
|
152
184
|
throw new Error("apiKey contains whitespace - check for a stray newline or paste error");
|
|
153
185
|
this.apiKey = key;
|
|
154
186
|
this.base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
155
|
-
|
|
187
|
+
// Coerce a non-finite or non-positive timeout back to the default — NaN
|
|
188
|
+
// (e.g. Number() of an unset env var) would otherwise make the abort timer
|
|
189
|
+
// fire immediately and every request "time out after NaNms".
|
|
190
|
+
const tm = opts.timeoutMs;
|
|
191
|
+
this.timeoutMs = typeof tm === "number" && Number.isFinite(tm) && tm > 0 ? tm : 30_000;
|
|
156
192
|
this.model = opts.model ?? DEFAULT_MODEL;
|
|
157
193
|
if (this.model && !MODEL_RE.test(this.model)) {
|
|
158
194
|
throw new Error(`invalid model name: ${JSON.stringify(this.model)} (letters, digits, '.', '_', '-' only)`);
|
|
@@ -458,7 +494,12 @@ export class Client {
|
|
|
458
494
|
clearTimeout(timer);
|
|
459
495
|
// Timeouts are ambiguous (the write may have landed) — don't retry those.
|
|
460
496
|
const timedOut = e?.name === "AbortError" || ctrl.signal.aborted;
|
|
461
|
-
|
|
497
|
+
// Other network errors retry only when a retry can't double-process a
|
|
498
|
+
// write: idempotent methods always; writes only for connect-level
|
|
499
|
+
// failures (the request never reached the server). A mid-stream drop on
|
|
500
|
+
// a POST may already have stored + billed server-side.
|
|
501
|
+
const safe = IDEMPOTENT_METHODS.has(method.toUpperCase()) || isConnectFailure(e);
|
|
502
|
+
if (!timedOut && safe && attempt + 1 < attempts) {
|
|
462
503
|
await new Promise((r) => setTimeout(r, this.backoffMs(attempt)));
|
|
463
504
|
continue;
|
|
464
505
|
}
|
|
@@ -489,7 +530,11 @@ export class Client {
|
|
|
489
530
|
if (e?.name === "AbortError" || ctrl.signal.aborted) {
|
|
490
531
|
throw new APIConnectionError(0, `request timed out after ${this.timeoutMs}ms`);
|
|
491
532
|
}
|
|
492
|
-
|
|
533
|
+
if (e instanceof WosError)
|
|
534
|
+
throw e; // the size cap — already the right error
|
|
535
|
+
// A drop while READING the body is a transport failure — surface it as
|
|
536
|
+
// APIConnectionError, not a raw fetch TypeError. Ambiguous, never retried.
|
|
537
|
+
throw new APIConnectionError(0, `network error: ${e?.message ?? e}`);
|
|
493
538
|
}
|
|
494
539
|
finally {
|
|
495
540
|
clearTimeout(timer);
|
|
@@ -520,7 +565,16 @@ export class Client {
|
|
|
520
565
|
}
|
|
521
566
|
throw errorFor(res.status, msg, requestId);
|
|
522
567
|
}
|
|
523
|
-
|
|
568
|
+
if (!text)
|
|
569
|
+
return null;
|
|
570
|
+
try {
|
|
571
|
+
return JSON.parse(text);
|
|
572
|
+
}
|
|
573
|
+
catch (e) {
|
|
574
|
+
// A 2xx with a corrupt body is a real failure — surface it, don't leak
|
|
575
|
+
// a raw SyntaxError (parity with the Rust SDK).
|
|
576
|
+
throw new WosError(res.status, `invalid JSON in response: ${e?.message ?? e}`);
|
|
577
|
+
}
|
|
524
578
|
}
|
|
525
579
|
throw new WosError(0, "retries exhausted"); // unreachable; keeps the compiler happy
|
|
526
580
|
}
|
package/package.json
CHANGED