wontopos 2.2.11 → 2.2.12
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 +54 -0
- package/dist/wontopos.js +102 -4
- package/package.json +1 -1
package/dist/wontopos.d.ts
CHANGED
|
@@ -32,12 +32,47 @@
|
|
|
32
32
|
* runtime's (never disabled by this SDK; Node 18+ floors TLS at 1.2). Prefer
|
|
33
33
|
* `Client.fromEnv()` over keys in source code.
|
|
34
34
|
*/
|
|
35
|
+
/** Quota from a response's `X-RateLimit-*` headers, or `null` if absent. */
|
|
36
|
+
export interface RateLimit {
|
|
37
|
+
limit: number | null;
|
|
38
|
+
remaining: number | null;
|
|
39
|
+
reset: number | null;
|
|
40
|
+
}
|
|
35
41
|
export declare class WosError extends Error {
|
|
36
42
|
readonly status: number;
|
|
37
43
|
/** The server's id for the request when it sent one — include it when contacting support. */
|
|
38
44
|
readonly requestId?: string;
|
|
39
45
|
constructor(status: number, message: string, requestId?: string);
|
|
40
46
|
}
|
|
47
|
+
/** The request never got a response (DNS/TLS/timeout/connection). `status` is 0. */
|
|
48
|
+
export declare class APIConnectionError extends WosError {
|
|
49
|
+
}
|
|
50
|
+
/** 400 — the request was malformed (bad arguments). */
|
|
51
|
+
export declare class BadRequestError extends WosError {
|
|
52
|
+
}
|
|
53
|
+
/** 401 — the API key is missing, wrong, or revoked. */
|
|
54
|
+
export declare class AuthenticationError extends WosError {
|
|
55
|
+
}
|
|
56
|
+
/** 402 — no card on file or the balance is depleted. Top up to continue. */
|
|
57
|
+
export declare class PaymentRequiredError extends WosError {
|
|
58
|
+
}
|
|
59
|
+
/** 403 — the key/model isn't allowed to do this. */
|
|
60
|
+
export declare class PermissionDeniedError extends WosError {
|
|
61
|
+
}
|
|
62
|
+
/** 404 — the store or resource doesn't exist (create the store first). */
|
|
63
|
+
export declare class NotFoundError extends WosError {
|
|
64
|
+
}
|
|
65
|
+
/** 409 — a concurrent write to the same store. Retry. */
|
|
66
|
+
export declare class ConflictError extends WosError {
|
|
67
|
+
}
|
|
68
|
+
/** 429 — too many requests. Back off and retry (the client already retries these). */
|
|
69
|
+
export declare class RateLimitError extends WosError {
|
|
70
|
+
}
|
|
71
|
+
/** 5xx — the service failed. Safe to retry. */
|
|
72
|
+
export declare class ServerError extends WosError {
|
|
73
|
+
}
|
|
74
|
+
/** Build the most specific WosError subclass for an HTTP status. */
|
|
75
|
+
export declare function errorFor(status: number, message: string, requestId?: string): WosError;
|
|
41
76
|
export interface Usage {
|
|
42
77
|
input_tokens?: number;
|
|
43
78
|
output_tokens?: number;
|
|
@@ -205,6 +240,11 @@ export declare class Client {
|
|
|
205
240
|
private readonly maxRetries;
|
|
206
241
|
/** The store every call uses unless one passes `userId`. */
|
|
207
242
|
private readonly defaultUser;
|
|
243
|
+
private _rateLimit;
|
|
244
|
+
/** Quota from the MOST RECENT call: `{ limit, remaining, reset }` (or `null` before
|
|
245
|
+
* the first call). Read it to self-throttle — the client already retries 429s, but
|
|
246
|
+
* this lets you slow down before hitting the wall. */
|
|
247
|
+
get rateLimit(): RateLimit | null;
|
|
208
248
|
constructor(opts: ClientOptions);
|
|
209
249
|
/** A client whose key comes from `WONTOPOS_API_KEY` (or `WOS_API_KEY`) — keeps
|
|
210
250
|
* keys out of source code. Node/Bun/Deno-with-env only. */
|
|
@@ -259,6 +299,20 @@ export declare class Client {
|
|
|
259
299
|
limit?: number;
|
|
260
300
|
cursor?: string;
|
|
261
301
|
}): Promise<MemoryPage>;
|
|
302
|
+
/** Async-iterate every stored memory in a store, paging under the hood — no cursor
|
|
303
|
+
* bookkeeping. Original text + metadata only, never vectors.
|
|
304
|
+
*
|
|
305
|
+
* for await (const m of mem.iterMemories()) console.log(m.id, m.content);
|
|
306
|
+
*/
|
|
307
|
+
iterMemories(userId?: string, opts?: {
|
|
308
|
+
pageSize?: number;
|
|
309
|
+
}): AsyncGenerator<Memory>;
|
|
310
|
+
/** Collect ALL of a store's memories into an array (original text + metadata, no vectors). */
|
|
311
|
+
exportMemories(userId?: string): Promise<Memory[]>;
|
|
312
|
+
/** Check connectivity AND that the API key works. Resolves `true`, or rejects — an
|
|
313
|
+
* `AuthenticationError` (bad key), `PaymentRequiredError` (valid key but no card /
|
|
314
|
+
* depleted balance), or `APIConnectionError` (unreachable). Makes one metered request. */
|
|
315
|
+
ping(): Promise<boolean>;
|
|
262
316
|
/** Available models: `[{ id, name, available, memory }, ...]`. Needs no API key. */
|
|
263
317
|
listModels(): Promise<ModelInfo[]>;
|
|
264
318
|
/** Create a store — the `userId` you read and write under. Stores are explicit:
|
package/dist/wontopos.js
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* runtime's (never disabled by this SDK; Node 18+ floors TLS at 1.2). Prefer
|
|
33
33
|
* `Client.fromEnv()` over keys in source code.
|
|
34
34
|
*/
|
|
35
|
-
const VERSION = "2.2.
|
|
35
|
+
const VERSION = "2.2.12";
|
|
36
36
|
const USER_AGENT = `wontopos-node/${VERSION}`;
|
|
37
37
|
const DEFAULT_BASE_URL = "https://api.wontopos.com";
|
|
38
38
|
const DEFAULT_MODEL = "tablet-1";
|
|
@@ -48,17 +48,77 @@ const MODEL_RE = /^[A-Za-z0-9._-]+$/;
|
|
|
48
48
|
function maskKey(key) {
|
|
49
49
|
return key.length > 12 ? `${key.slice(0, 4)}...${key.slice(-4)}` : "***";
|
|
50
50
|
}
|
|
51
|
+
function parseRateLimit(h) {
|
|
52
|
+
const num = (name) => {
|
|
53
|
+
const v = h.get(name);
|
|
54
|
+
if (v == null || v.trim() === "")
|
|
55
|
+
return null; // Number("") is 0 — treat blank as absent
|
|
56
|
+
const n = Number(v);
|
|
57
|
+
return Number.isFinite(n) ? n : null;
|
|
58
|
+
};
|
|
59
|
+
const limit = num("X-RateLimit-Limit");
|
|
60
|
+
const remaining = num("X-RateLimit-Remaining");
|
|
61
|
+
const reset = num("X-RateLimit-Reset");
|
|
62
|
+
if (limit === null && remaining === null && reset === null)
|
|
63
|
+
return null;
|
|
64
|
+
return { limit, remaining, reset };
|
|
65
|
+
}
|
|
51
66
|
export class WosError extends Error {
|
|
52
67
|
status;
|
|
53
68
|
/** The server's id for the request when it sent one — include it when contacting support. */
|
|
54
69
|
requestId;
|
|
55
70
|
constructor(status, message, requestId) {
|
|
56
71
|
super(`[${status}] ${message}${requestId ? ` (request_id: ${requestId})` : ""}`);
|
|
57
|
-
this.name =
|
|
72
|
+
this.name = new.target.name; // the concrete subclass name (RateLimitError, …)
|
|
58
73
|
this.status = status;
|
|
59
74
|
this.requestId = requestId;
|
|
60
75
|
}
|
|
61
76
|
}
|
|
77
|
+
// Typed subclasses so callers can branch on the failure — `catch (e) { if (e instanceof
|
|
78
|
+
// RateLimitError) … }`. Each is a WosError, so a broad `instanceof WosError` still works.
|
|
79
|
+
/** The request never got a response (DNS/TLS/timeout/connection). `status` is 0. */
|
|
80
|
+
export class APIConnectionError extends WosError {
|
|
81
|
+
}
|
|
82
|
+
/** 400 — the request was malformed (bad arguments). */
|
|
83
|
+
export class BadRequestError extends WosError {
|
|
84
|
+
}
|
|
85
|
+
/** 401 — the API key is missing, wrong, or revoked. */
|
|
86
|
+
export class AuthenticationError extends WosError {
|
|
87
|
+
}
|
|
88
|
+
/** 402 — no card on file or the balance is depleted. Top up to continue. */
|
|
89
|
+
export class PaymentRequiredError extends WosError {
|
|
90
|
+
}
|
|
91
|
+
/** 403 — the key/model isn't allowed to do this. */
|
|
92
|
+
export class PermissionDeniedError extends WosError {
|
|
93
|
+
}
|
|
94
|
+
/** 404 — the store or resource doesn't exist (create the store first). */
|
|
95
|
+
export class NotFoundError extends WosError {
|
|
96
|
+
}
|
|
97
|
+
/** 409 — a concurrent write to the same store. Retry. */
|
|
98
|
+
export class ConflictError extends WosError {
|
|
99
|
+
}
|
|
100
|
+
/** 429 — too many requests. Back off and retry (the client already retries these). */
|
|
101
|
+
export class RateLimitError extends WosError {
|
|
102
|
+
}
|
|
103
|
+
/** 5xx — the service failed. Safe to retry. */
|
|
104
|
+
export class ServerError extends WosError {
|
|
105
|
+
}
|
|
106
|
+
const STATUS_ERRORS = {
|
|
107
|
+
400: BadRequestError,
|
|
108
|
+
401: AuthenticationError,
|
|
109
|
+
402: PaymentRequiredError,
|
|
110
|
+
403: PermissionDeniedError,
|
|
111
|
+
404: NotFoundError,
|
|
112
|
+
409: ConflictError,
|
|
113
|
+
429: RateLimitError,
|
|
114
|
+
};
|
|
115
|
+
/** Build the most specific WosError subclass for an HTTP status. */
|
|
116
|
+
export function errorFor(status, message, requestId) {
|
|
117
|
+
const Cls = status === 0
|
|
118
|
+
? APIConnectionError
|
|
119
|
+
: STATUS_ERRORS[status] ?? (status >= 500 && status < 600 ? ServerError : WosError);
|
|
120
|
+
return new Cls(status, message, requestId);
|
|
121
|
+
}
|
|
62
122
|
const DEFAULT_USER = "default";
|
|
63
123
|
/** Wontopos memory client. Store and recall memories, isolated per `userId`. */
|
|
64
124
|
export class Client {
|
|
@@ -70,6 +130,13 @@ export class Client {
|
|
|
70
130
|
maxRetries;
|
|
71
131
|
/** The store every call uses unless one passes `userId`. */
|
|
72
132
|
defaultUser;
|
|
133
|
+
_rateLimit = null;
|
|
134
|
+
/** Quota from the MOST RECENT call: `{ limit, remaining, reset }` (or `null` before
|
|
135
|
+
* the first call). Read it to self-throttle — the client already retries 429s, but
|
|
136
|
+
* this lets you slow down before hitting the wall. */
|
|
137
|
+
get rateLimit() {
|
|
138
|
+
return this._rateLimit;
|
|
139
|
+
}
|
|
73
140
|
constructor(opts) {
|
|
74
141
|
// Trim the key and reject inner whitespace — a stray newline from a file or
|
|
75
142
|
// env var otherwise turns into a mystery 401 (or a mangled header).
|
|
@@ -215,6 +282,36 @@ export class Client {
|
|
|
215
282
|
body.cursor = opts.cursor;
|
|
216
283
|
return this.post("/api/v1/memory/list", body);
|
|
217
284
|
}
|
|
285
|
+
/** Async-iterate every stored memory in a store, paging under the hood — no cursor
|
|
286
|
+
* bookkeeping. Original text + metadata only, never vectors.
|
|
287
|
+
*
|
|
288
|
+
* for await (const m of mem.iterMemories()) console.log(m.id, m.content);
|
|
289
|
+
*/
|
|
290
|
+
async *iterMemories(userId, opts = {}) {
|
|
291
|
+
let cursor;
|
|
292
|
+
for (;;) {
|
|
293
|
+
const page = await this.listMemories(userId, { limit: opts.pageSize ?? 100, cursor });
|
|
294
|
+
for (const m of page.memories ?? [])
|
|
295
|
+
yield m;
|
|
296
|
+
cursor = page.next_cursor ?? undefined;
|
|
297
|
+
if (!cursor)
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/** Collect ALL of a store's memories into an array (original text + metadata, no vectors). */
|
|
302
|
+
async exportMemories(userId) {
|
|
303
|
+
const out = [];
|
|
304
|
+
for await (const m of this.iterMemories(userId))
|
|
305
|
+
out.push(m);
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
/** Check connectivity AND that the API key works. Resolves `true`, or rejects — an
|
|
309
|
+
* `AuthenticationError` (bad key), `PaymentRequiredError` (valid key but no card /
|
|
310
|
+
* depleted balance), or `APIConnectionError` (unreachable). Makes one metered request. */
|
|
311
|
+
async ping() {
|
|
312
|
+
await this.request("GET", "/api/v1/memory/collections");
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
218
315
|
// ----- models -----
|
|
219
316
|
/** Available models: `[{ id, name, available, memory }, ...]`. Needs no API key. */
|
|
220
317
|
async listModels() {
|
|
@@ -342,7 +439,7 @@ export class Client {
|
|
|
342
439
|
await new Promise((r) => setTimeout(r, this.backoffMs(attempt)));
|
|
343
440
|
continue;
|
|
344
441
|
}
|
|
345
|
-
throw new
|
|
442
|
+
throw new APIConnectionError(0, timedOut ? `request timed out after ${this.timeoutMs}ms` : `network error: ${e?.message ?? e}`);
|
|
346
443
|
}
|
|
347
444
|
clearTimeout(timer);
|
|
348
445
|
if (RETRY_STATUSES.has(res.status) && attempt + 1 < attempts) {
|
|
@@ -354,6 +451,7 @@ export class Client {
|
|
|
354
451
|
if ((res.status >= 300 && res.status < 400) || res.type === "opaqueredirect") {
|
|
355
452
|
throw new WosError(res.status, "unexpected redirect — refused (the API key never follows a redirect). Check baseUrl: exact host, https://.");
|
|
356
453
|
}
|
|
454
|
+
this._rateLimit = parseRateLimit(res.headers) ?? this._rateLimit;
|
|
357
455
|
const text = await this.readCapped(res);
|
|
358
456
|
if (!res.ok) {
|
|
359
457
|
// Server returns either Anthropic-style envelope
|
|
@@ -379,7 +477,7 @@ export class Client {
|
|
|
379
477
|
catch {
|
|
380
478
|
/* keep raw text */
|
|
381
479
|
}
|
|
382
|
-
throw
|
|
480
|
+
throw errorFor(res.status, msg, requestId);
|
|
383
481
|
}
|
|
384
482
|
return text ? JSON.parse(text) : null;
|
|
385
483
|
}
|
package/package.json
CHANGED