@jettson/sdk 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,500 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AgentTimeoutError: () => AgentTimeoutError,
24
+ Jettson: () => Jettson,
25
+ JettsonAuthError: () => JettsonAuthError,
26
+ JettsonError: () => JettsonError,
27
+ JettsonNetworkError: () => JettsonNetworkError,
28
+ JettsonNotFoundError: () => JettsonNotFoundError,
29
+ JettsonQuotaExceededError: () => JettsonQuotaExceededError,
30
+ JettsonRateLimitError: () => JettsonRateLimitError,
31
+ JettsonServerError: () => JettsonServerError,
32
+ JettsonValidationError: () => JettsonValidationError
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/errors.ts
37
+ var JettsonError = class extends Error {
38
+ code;
39
+ status;
40
+ details;
41
+ constructor(opts) {
42
+ super(opts.message);
43
+ this.name = "JettsonError";
44
+ this.code = opts.code;
45
+ this.status = opts.status;
46
+ this.details = opts.details;
47
+ }
48
+ };
49
+ var JettsonAuthError = class extends JettsonError {
50
+ constructor(opts) {
51
+ super({ ...opts, status: 401 });
52
+ this.name = "JettsonAuthError";
53
+ }
54
+ };
55
+ var JettsonRateLimitError = class extends JettsonError {
56
+ retryAfterSeconds;
57
+ constructor(opts) {
58
+ super({ ...opts, status: 429 });
59
+ this.name = "JettsonRateLimitError";
60
+ this.retryAfterSeconds = opts.retryAfterSeconds;
61
+ }
62
+ };
63
+ var JettsonQuotaExceededError = class extends JettsonError {
64
+ used;
65
+ limit;
66
+ plan;
67
+ constructor(opts) {
68
+ super({ ...opts, status: 402 });
69
+ this.name = "JettsonQuotaExceededError";
70
+ this.used = opts.used;
71
+ this.limit = opts.limit;
72
+ this.plan = opts.plan;
73
+ }
74
+ };
75
+ var JettsonValidationError = class extends JettsonError {
76
+ constructor(opts) {
77
+ super({ ...opts, status: 400 });
78
+ this.name = "JettsonValidationError";
79
+ }
80
+ };
81
+ var JettsonNotFoundError = class extends JettsonError {
82
+ constructor(opts) {
83
+ super({ ...opts, status: 404 });
84
+ this.name = "JettsonNotFoundError";
85
+ }
86
+ };
87
+ var JettsonServerError = class extends JettsonError {
88
+ constructor(opts) {
89
+ super(opts);
90
+ this.name = "JettsonServerError";
91
+ }
92
+ };
93
+ var JettsonNetworkError = class extends JettsonError {
94
+ constructor(message) {
95
+ super({ message, code: "network_error", status: 0 });
96
+ this.name = "JettsonNetworkError";
97
+ }
98
+ };
99
+ var AgentTimeoutError = class extends JettsonError {
100
+ agentId;
101
+ elapsedMs;
102
+ constructor(agentId, elapsedMs) {
103
+ super({
104
+ message: `Agent ${agentId} did not reach a terminal state within ${elapsedMs}ms.`,
105
+ code: "agent_timeout",
106
+ status: 0
107
+ });
108
+ this.name = "AgentTimeoutError";
109
+ this.agentId = agentId;
110
+ this.elapsedMs = elapsedMs;
111
+ }
112
+ };
113
+ function errorFromResponse(input) {
114
+ const status = input.status;
115
+ const code = input.body?.error?.code ?? "unknown_error";
116
+ const message = input.body?.error?.message ?? `Request failed with status ${status}`;
117
+ const details = input.body?.error?.details;
118
+ if (status === 402) {
119
+ return new JettsonQuotaExceededError({
120
+ message,
121
+ code,
122
+ used: numField(details, "used") ?? 0,
123
+ limit: numField(details, "limit") ?? numField(details, "limitHours") ?? 0,
124
+ plan: strField(details, "plan") ?? "unknown",
125
+ details
126
+ });
127
+ }
128
+ if (status === 401) return new JettsonAuthError({ message, code, details });
129
+ if (status === 404) return new JettsonNotFoundError({ message, code, details });
130
+ if (status === 400 || status === 422) {
131
+ return new JettsonValidationError({ message, code, details });
132
+ }
133
+ if (status === 429) {
134
+ const retryAfter = parseRetryAfter(input.retryAfterHeader, details);
135
+ return new JettsonRateLimitError({
136
+ message,
137
+ code,
138
+ retryAfterSeconds: retryAfter,
139
+ details
140
+ });
141
+ }
142
+ if (status >= 500) {
143
+ return new JettsonServerError({ message, code, status, details });
144
+ }
145
+ return new JettsonError({ message, code, status, details });
146
+ }
147
+ function parseRetryAfter(header, details) {
148
+ if (header) {
149
+ const n = Number.parseInt(header, 10);
150
+ if (Number.isFinite(n) && n > 0) return n;
151
+ }
152
+ const detail = numField(details, "retryAfterSeconds");
153
+ return detail ?? 1;
154
+ }
155
+ function numField(obj, key) {
156
+ const v = obj?.[key];
157
+ return typeof v === "number" && Number.isFinite(v) ? v : null;
158
+ }
159
+ function strField(obj, key) {
160
+ const v = obj?.[key];
161
+ return typeof v === "string" ? v : null;
162
+ }
163
+
164
+ // src/agents.ts
165
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
166
+ "completed",
167
+ "error",
168
+ "stopped"
169
+ ]);
170
+ var AgentsResource = class {
171
+ constructor(http) {
172
+ this.http = http;
173
+ }
174
+ http;
175
+ /**
176
+ * Spawn a new agent. Returns immediately with `status: "spawning"`.
177
+ * Use `wait()` to block until the agent finishes.
178
+ */
179
+ async spawn(input) {
180
+ const body = { task: input.task };
181
+ if (input.name !== void 0) body.name = input.name;
182
+ if (input.region !== void 0) body.region = input.region;
183
+ if (input.metadata !== void 0) body.metadata = input.metadata;
184
+ return this.http.request({
185
+ method: "POST",
186
+ path: "/agents",
187
+ body,
188
+ idempotencyKey: input.idempotencyKey
189
+ });
190
+ }
191
+ /** Fetch one agent by ID. */
192
+ async get(agentId) {
193
+ return this.http.request({
194
+ method: "GET",
195
+ path: `/agents/${encodeURIComponent(agentId)}`
196
+ });
197
+ }
198
+ /** Paginated list of the caller's agents, newest first. */
199
+ async list(input = {}) {
200
+ return this.http.request({
201
+ method: "GET",
202
+ path: "/agents",
203
+ query: { limit: input.limit, cursor: input.cursor }
204
+ });
205
+ }
206
+ /**
207
+ * Cancel a running agent. Idempotent — terminal agents return 200
208
+ * unchanged.
209
+ */
210
+ async cancel(agentId) {
211
+ await this.http.request({
212
+ method: "DELETE",
213
+ path: `/agents/${encodeURIComponent(agentId)}`
214
+ });
215
+ }
216
+ /**
217
+ * Poll `get(agentId)` until the agent reaches a terminal status, with
218
+ * gentle exponential backoff. Throws `AgentTimeoutError` if the
219
+ * deadline lapses.
220
+ */
221
+ async wait(agentId, opts = {}) {
222
+ const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1e3;
223
+ const initialInterval = opts.pollIntervalMs ?? 1e3;
224
+ const maxInterval = opts.maxPollIntervalMs ?? 5e3;
225
+ const deadline = Date.now() + timeoutMs;
226
+ let interval = initialInterval;
227
+ while (true) {
228
+ const agent = await this.get(agentId);
229
+ if (TERMINAL_STATUSES.has(agent.status)) return agent;
230
+ if (Date.now() >= deadline) {
231
+ throw new AgentTimeoutError(agentId, timeoutMs);
232
+ }
233
+ const remaining = deadline - Date.now();
234
+ const sleepMs = Math.max(0, Math.min(interval, remaining));
235
+ await sleep(sleepMs);
236
+ interval = Math.min(Math.round(interval * 1.5), maxInterval);
237
+ }
238
+ }
239
+ };
240
+ function sleep(ms) {
241
+ return new Promise((resolve) => setTimeout(resolve, ms));
242
+ }
243
+
244
+ // src/http.ts
245
+ var SDK_VERSION = "0.1.0";
246
+ var HttpClient = class {
247
+ apiKey;
248
+ baseUrl;
249
+ maxRetries;
250
+ initialBackoffMs;
251
+ constructor(opts) {
252
+ if (!opts.apiKey) {
253
+ throw new JettsonError({
254
+ message: "Missing `apiKey` \u2014 pass one when constructing `new Jettson()`.",
255
+ code: "missing_api_key",
256
+ status: 0
257
+ });
258
+ }
259
+ this.apiKey = opts.apiKey;
260
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
261
+ this.maxRetries = opts.maxRetries ?? 3;
262
+ this.initialBackoffMs = opts.initialBackoffMs ?? 1e3;
263
+ }
264
+ async request(req) {
265
+ const url = this.buildUrl(req.path, req.query);
266
+ const headers = {
267
+ Authorization: `Bearer ${this.apiKey}`,
268
+ "User-Agent": `jettson-node/${SDK_VERSION}`,
269
+ Accept: "application/json"
270
+ };
271
+ if (req.body !== void 0) {
272
+ headers["Content-Type"] = "application/json";
273
+ }
274
+ if (req.idempotencyKey) {
275
+ headers["X-Idempotency-Key"] = req.idempotencyKey;
276
+ }
277
+ let lastError = null;
278
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
279
+ let res;
280
+ try {
281
+ res = await fetch(url, {
282
+ method: req.method,
283
+ headers,
284
+ body: req.body !== void 0 ? JSON.stringify(req.body) : void 0
285
+ });
286
+ } catch (err) {
287
+ lastError = new JettsonNetworkError(
288
+ err instanceof Error ? err.message : "Network request failed"
289
+ );
290
+ if (attempt === 0 && this.maxRetries > 0) {
291
+ await sleep2(this.initialBackoffMs);
292
+ continue;
293
+ }
294
+ throw lastError;
295
+ }
296
+ if (res.ok) {
297
+ if (res.status === 204) return void 0;
298
+ const text = await res.text();
299
+ if (text.length === 0) return void 0;
300
+ try {
301
+ return JSON.parse(text);
302
+ } catch {
303
+ throw new JettsonError({
304
+ message: "Server returned malformed JSON.",
305
+ code: "invalid_response",
306
+ status: res.status
307
+ });
308
+ }
309
+ }
310
+ const body = await safeJson(res);
311
+ const error = errorFromResponse({
312
+ status: res.status,
313
+ body,
314
+ retryAfterHeader: res.headers.get("retry-after")
315
+ });
316
+ lastError = error;
317
+ const retryable = res.status === 429 && attempt < this.maxRetries || res.status >= 500 && attempt < this.maxRetries;
318
+ if (!retryable) throw error;
319
+ const delayMs = res.status === 429 ? Math.max(1, "retryAfterSeconds" in error ? error.retryAfterSeconds : 1) * 1e3 : this.initialBackoffMs * 2 ** attempt;
320
+ await sleep2(delayMs);
321
+ }
322
+ throw lastError ?? new JettsonError({
323
+ message: "Request failed after retries.",
324
+ code: "max_retries_exceeded",
325
+ status: 0
326
+ });
327
+ }
328
+ buildUrl(path, query) {
329
+ const url = `${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
330
+ if (!query) return url;
331
+ const search = new URLSearchParams();
332
+ for (const [k, v] of Object.entries(query)) {
333
+ if (v === void 0 || v === null) continue;
334
+ search.append(k, String(v));
335
+ }
336
+ const qs = search.toString();
337
+ return qs.length === 0 ? url : `${url}?${qs}`;
338
+ }
339
+ };
340
+ function sleep2(ms) {
341
+ return new Promise((resolve) => setTimeout(resolve, ms));
342
+ }
343
+ async function safeJson(res) {
344
+ try {
345
+ const text = await res.text();
346
+ if (text.length === 0) return null;
347
+ return JSON.parse(text);
348
+ } catch {
349
+ return null;
350
+ }
351
+ }
352
+
353
+ // src/memory.ts
354
+ var MemoryResource = class {
355
+ constructor(http) {
356
+ this.http = http;
357
+ }
358
+ http;
359
+ async put(input) {
360
+ const body = {
361
+ key: input.key,
362
+ value: input.value
363
+ };
364
+ if (input.namespace !== void 0) body.namespace = input.namespace;
365
+ if (input.tags !== void 0) body.tags = input.tags;
366
+ if (input.importance !== void 0) body.importance = input.importance;
367
+ if (input.expiresInDays !== void 0) {
368
+ body.expires_in_days = input.expiresInDays;
369
+ }
370
+ return this.http.request({
371
+ method: "POST",
372
+ path: "/memory",
373
+ body
374
+ });
375
+ }
376
+ /** Returns `null` (not an error) when no memory matches. */
377
+ async get(key, opts = {}) {
378
+ try {
379
+ return await this.http.request({
380
+ method: "GET",
381
+ path: "/memory",
382
+ query: { key, namespace: opts.namespace }
383
+ });
384
+ } catch (err) {
385
+ if (err?.status === 404) return null;
386
+ throw err;
387
+ }
388
+ }
389
+ async delete(key, opts = {}) {
390
+ await this.http.request({
391
+ method: "DELETE",
392
+ path: "/memory",
393
+ query: {
394
+ key,
395
+ namespace: opts.namespace,
396
+ hard: opts.hardDelete ? "true" : void 0
397
+ }
398
+ });
399
+ }
400
+ async search(input) {
401
+ const body = { query: input.query };
402
+ if (input.namespace !== void 0) body.namespace = input.namespace;
403
+ if (input.tags !== void 0) body.tags = input.tags;
404
+ if (input.limit !== void 0) body.limit = input.limit;
405
+ if (input.mode !== void 0) body.mode = input.mode;
406
+ if (input.minScore !== void 0) body.min_score = input.minScore;
407
+ const res = await this.http.request({
408
+ method: "POST",
409
+ path: "/memory/search",
410
+ body
411
+ });
412
+ return res.results;
413
+ }
414
+ async list(input = {}) {
415
+ const res = await this.http.request({
416
+ method: "GET",
417
+ path: "/memory/list",
418
+ query: {
419
+ namespace: input.namespace,
420
+ tags: input.tags?.join(","),
421
+ limit: input.limit
422
+ }
423
+ });
424
+ return res.memories;
425
+ }
426
+ async dedupe(input = {}) {
427
+ const body = {};
428
+ if (input.namespace !== void 0) body.namespace = input.namespace;
429
+ if (input.similarityThreshold !== void 0) {
430
+ body.threshold = input.similarityThreshold;
431
+ }
432
+ if (input.dryRun !== void 0) body.dry_run = input.dryRun;
433
+ return this.http.request({
434
+ method: "POST",
435
+ path: "/memory/dedupe",
436
+ body
437
+ });
438
+ }
439
+ async consolidate(input = {}) {
440
+ const body = {};
441
+ if (input.namespace !== void 0) body.namespace = input.namespace;
442
+ if (input.minClusterSize !== void 0) {
443
+ body.min_cluster_size = input.minClusterSize;
444
+ }
445
+ if (input.threshold !== void 0) body.threshold = input.threshold;
446
+ return this.http.request({
447
+ method: "POST",
448
+ path: "/memory/consolidate",
449
+ body
450
+ });
451
+ }
452
+ async namespaces() {
453
+ const res = await this.http.request({
454
+ method: "GET",
455
+ path: "/memory/namespaces"
456
+ });
457
+ return res.namespaces;
458
+ }
459
+ async export() {
460
+ return this.http.request({
461
+ method: "GET",
462
+ path: "/memory/export"
463
+ });
464
+ }
465
+ async import(memories) {
466
+ return this.http.request({
467
+ method: "POST",
468
+ path: "/memory/import",
469
+ body: { memories }
470
+ });
471
+ }
472
+ };
473
+
474
+ // src/index.ts
475
+ var Jettson = class {
476
+ agents;
477
+ memory;
478
+ constructor(options) {
479
+ const http = new HttpClient({
480
+ apiKey: options.apiKey,
481
+ baseUrl: options.baseUrl ?? "https://jettson.dev/api/v1",
482
+ maxRetries: options.maxRetries
483
+ });
484
+ this.agents = new AgentsResource(http);
485
+ this.memory = new MemoryResource(http);
486
+ }
487
+ };
488
+ // Annotate the CommonJS export names for ESM import in node:
489
+ 0 && (module.exports = {
490
+ AgentTimeoutError,
491
+ Jettson,
492
+ JettsonAuthError,
493
+ JettsonError,
494
+ JettsonNetworkError,
495
+ JettsonNotFoundError,
496
+ JettsonQuotaExceededError,
497
+ JettsonRateLimitError,
498
+ JettsonServerError,
499
+ JettsonValidationError
500
+ });