kloakd-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.d.mts +526 -0
- package/dist/index.d.ts +526 -0
- package/dist/index.js +876 -0
- package/dist/index.mjs +843 -0
- package/package.json +53 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,843 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var KloakdError = class extends Error {
|
|
3
|
+
constructor(message, statusCode) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "KloakdError";
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var AuthenticationError = class extends KloakdError {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message, 401);
|
|
13
|
+
this.name = "AuthenticationError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var NotEntitledError = class extends KloakdError {
|
|
17
|
+
constructor(message, module, upgradeUrl = "https://app.kloakd.dev/billing") {
|
|
18
|
+
super(message, 403);
|
|
19
|
+
this.name = "NotEntitledError";
|
|
20
|
+
this.module = module;
|
|
21
|
+
this.upgradeUrl = upgradeUrl;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var RateLimitError = class extends KloakdError {
|
|
25
|
+
constructor(message, retryAfter, resetAt = "") {
|
|
26
|
+
super(message, 429);
|
|
27
|
+
this.name = "RateLimitError";
|
|
28
|
+
this.retryAfter = retryAfter;
|
|
29
|
+
this.resetAt = resetAt;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var UpstreamError = class extends KloakdError {
|
|
33
|
+
constructor(message) {
|
|
34
|
+
super(message, 502);
|
|
35
|
+
this.name = "UpstreamError";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var ApiError = class extends KloakdError {
|
|
39
|
+
constructor(message, statusCode) {
|
|
40
|
+
super(message, statusCode);
|
|
41
|
+
this.name = "ApiError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
function raiseForStatus(statusCode, body) {
|
|
45
|
+
if (statusCode < 400) return;
|
|
46
|
+
const detail = body["detail"] ?? body["message"] ?? `HTTP ${statusCode}`;
|
|
47
|
+
if (statusCode === 401) {
|
|
48
|
+
throw new AuthenticationError(`Invalid or expired API key: ${detail}`);
|
|
49
|
+
}
|
|
50
|
+
if (statusCode === 403) {
|
|
51
|
+
throw new NotEntitledError(
|
|
52
|
+
`Not entitled to this module: ${detail}`,
|
|
53
|
+
body["module"] ?? "unknown",
|
|
54
|
+
body["upgrade_url"] ?? "https://app.kloakd.dev/billing"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (statusCode === 429) {
|
|
58
|
+
throw new RateLimitError(
|
|
59
|
+
`Rate limit exceeded: ${detail}`,
|
|
60
|
+
Number(body["retry_after"] ?? 60),
|
|
61
|
+
body["reset_at"] ?? ""
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (statusCode === 502) {
|
|
65
|
+
throw new UpstreamError(`Upstream fetch failed: ${detail}`);
|
|
66
|
+
}
|
|
67
|
+
throw new ApiError(`KLOAKD API error ${statusCode}: ${detail}`, statusCode);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/_http.ts
|
|
71
|
+
var SDK_VERSION = "0.1.0";
|
|
72
|
+
var RETRYABLE = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
73
|
+
function sdkHeader() {
|
|
74
|
+
return `typescript/${SDK_VERSION}`;
|
|
75
|
+
}
|
|
76
|
+
function backoff(attempt, retryAfter) {
|
|
77
|
+
if (retryAfter != null && retryAfter > 0) return retryAfter * 1e3;
|
|
78
|
+
return Math.min(1e3 * Math.pow(2, attempt), 6e4);
|
|
79
|
+
}
|
|
80
|
+
function sleep(ms) {
|
|
81
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
82
|
+
}
|
|
83
|
+
var HttpTransport = class {
|
|
84
|
+
constructor(opts) {
|
|
85
|
+
this._apiKey = opts.apiKey;
|
|
86
|
+
this._organizationId = opts.organizationId;
|
|
87
|
+
this._baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
88
|
+
this._timeout = opts.timeout;
|
|
89
|
+
this._maxRetries = opts.maxRetries;
|
|
90
|
+
this._fetch = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
91
|
+
}
|
|
92
|
+
authHeaders() {
|
|
93
|
+
return {
|
|
94
|
+
Authorization: `Bearer ${this._apiKey}`,
|
|
95
|
+
"X-Kloakd-Organization": this._organizationId,
|
|
96
|
+
"X-Kloakd-SDK": sdkHeader(),
|
|
97
|
+
"Content-Type": "application/json"
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
orgPrefix() {
|
|
101
|
+
return `/api/v1/organizations/${this._organizationId}`;
|
|
102
|
+
}
|
|
103
|
+
url(path) {
|
|
104
|
+
return `${this._baseUrl}${this.orgPrefix()}/${path.replace(/^\//, "")}`;
|
|
105
|
+
}
|
|
106
|
+
async request(method, path, body, params) {
|
|
107
|
+
let fullUrl = this.url(path);
|
|
108
|
+
if (params && Object.keys(params).length > 0) {
|
|
109
|
+
fullUrl += "?" + new URLSearchParams(params).toString();
|
|
110
|
+
}
|
|
111
|
+
const headers = this.authHeaders();
|
|
112
|
+
const init = {
|
|
113
|
+
method,
|
|
114
|
+
headers,
|
|
115
|
+
signal: AbortSignal.timeout(this._timeout * 1e3)
|
|
116
|
+
};
|
|
117
|
+
if (body !== void 0) {
|
|
118
|
+
init.body = JSON.stringify(body);
|
|
119
|
+
}
|
|
120
|
+
let lastError;
|
|
121
|
+
for (let attempt = 0; attempt <= this._maxRetries; attempt++) {
|
|
122
|
+
const resp = await this._fetch(fullUrl, init);
|
|
123
|
+
const json = resp.headers.get("content-type")?.includes("application/json") ? await resp.json() : {};
|
|
124
|
+
try {
|
|
125
|
+
raiseForStatus(resp.status, json);
|
|
126
|
+
return json;
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (!(err instanceof KloakdError)) throw err;
|
|
129
|
+
if (!RETRYABLE.has(err.statusCode)) throw err;
|
|
130
|
+
lastError = err;
|
|
131
|
+
if (attempt < this._maxRetries) {
|
|
132
|
+
const retryAfter = err.statusCode === 429 ? Number(resp.headers.get("retry-after") ?? json["retry_after"] ?? 60) : void 0;
|
|
133
|
+
await sleep(backoff(attempt, retryAfter));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
throw lastError;
|
|
138
|
+
}
|
|
139
|
+
async get(path, params) {
|
|
140
|
+
return this.request("GET", path, void 0, params);
|
|
141
|
+
}
|
|
142
|
+
async post(path, body) {
|
|
143
|
+
return this.request("POST", path, body);
|
|
144
|
+
}
|
|
145
|
+
async delete(path) {
|
|
146
|
+
await this.request("DELETE", path);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Open an SSE stream and return an AsyncIterable over raw `data:` payloads.
|
|
150
|
+
* Caller is responsible for parsing each chunk.
|
|
151
|
+
*/
|
|
152
|
+
async *stream(path, body) {
|
|
153
|
+
const headers = { ...this.authHeaders(), Accept: "text/event-stream" };
|
|
154
|
+
const resp = await this._fetch(this.url(path), {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers,
|
|
157
|
+
body: JSON.stringify(body),
|
|
158
|
+
signal: AbortSignal.timeout(0)
|
|
159
|
+
// no timeout on streams
|
|
160
|
+
});
|
|
161
|
+
raiseForStatus(resp.status, {});
|
|
162
|
+
if (!resp.body) return;
|
|
163
|
+
const reader = resp.body.getReader();
|
|
164
|
+
const decoder = new TextDecoder();
|
|
165
|
+
let buffer = "";
|
|
166
|
+
try {
|
|
167
|
+
while (true) {
|
|
168
|
+
const { done, value } = await reader.read();
|
|
169
|
+
if (done) break;
|
|
170
|
+
buffer += decoder.decode(value, { stream: true });
|
|
171
|
+
const lines = buffer.split("\n");
|
|
172
|
+
buffer = lines.pop() ?? "";
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
const trimmed = line.trim();
|
|
175
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
176
|
+
const dataStr = trimmed.slice(5).trim();
|
|
177
|
+
if (!dataStr) continue;
|
|
178
|
+
try {
|
|
179
|
+
yield JSON.parse(dataStr);
|
|
180
|
+
} catch {
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} finally {
|
|
185
|
+
reader.releaseLock();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* SSE stream that also surfaces `event:` field alongside `data:`.
|
|
190
|
+
* Used by Parlyr.chatStream().
|
|
191
|
+
*/
|
|
192
|
+
async *streamWithEvents(path, body) {
|
|
193
|
+
const headers = { ...this.authHeaders(), Accept: "text/event-stream" };
|
|
194
|
+
const resp = await this._fetch(this.url(path), {
|
|
195
|
+
method: "POST",
|
|
196
|
+
headers,
|
|
197
|
+
body: JSON.stringify(body),
|
|
198
|
+
signal: AbortSignal.timeout(0)
|
|
199
|
+
});
|
|
200
|
+
raiseForStatus(resp.status, {});
|
|
201
|
+
if (!resp.body) return;
|
|
202
|
+
const reader = resp.body.getReader();
|
|
203
|
+
const decoder = new TextDecoder();
|
|
204
|
+
let buffer = "";
|
|
205
|
+
let currentEvent = "";
|
|
206
|
+
try {
|
|
207
|
+
while (true) {
|
|
208
|
+
const { done, value } = await reader.read();
|
|
209
|
+
if (done) break;
|
|
210
|
+
buffer += decoder.decode(value, { stream: true });
|
|
211
|
+
const lines = buffer.split("\n");
|
|
212
|
+
buffer = lines.pop() ?? "";
|
|
213
|
+
for (const line of lines) {
|
|
214
|
+
const trimmed = line.trim();
|
|
215
|
+
if (trimmed.startsWith("event:")) {
|
|
216
|
+
currentEvent = trimmed.slice(6).trim();
|
|
217
|
+
} else if (trimmed.startsWith("data:")) {
|
|
218
|
+
const dataStr = trimmed.slice(5).trim();
|
|
219
|
+
if (!dataStr) continue;
|
|
220
|
+
try {
|
|
221
|
+
yield { event: currentEvent, data: JSON.parse(dataStr) };
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
} finally {
|
|
228
|
+
reader.releaseLock();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// src/modules/evadr.ts
|
|
234
|
+
function parseFetch(raw, url) {
|
|
235
|
+
const result = {
|
|
236
|
+
success: Boolean(raw["success"]),
|
|
237
|
+
url: raw["url"] ?? url,
|
|
238
|
+
statusCode: Number(raw["status_code"] ?? 0),
|
|
239
|
+
tierUsed: Number(raw["tier_used"] ?? 1),
|
|
240
|
+
html: raw["html"] ?? null,
|
|
241
|
+
vendorDetected: raw["vendor_detected"] ?? null,
|
|
242
|
+
antiBotBypassed: Boolean(raw["anti_bot_bypassed"]),
|
|
243
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
244
|
+
error: raw["error"] ?? null
|
|
245
|
+
};
|
|
246
|
+
return { ...result, get ok() {
|
|
247
|
+
return result.success && result.error === null;
|
|
248
|
+
} };
|
|
249
|
+
}
|
|
250
|
+
var EvadrNamespace = class {
|
|
251
|
+
constructor(_t) {
|
|
252
|
+
this._t = _t;
|
|
253
|
+
}
|
|
254
|
+
async fetch(url, opts = {}) {
|
|
255
|
+
const body = { url };
|
|
256
|
+
if (opts.forceBrowser) body["force_browser"] = true;
|
|
257
|
+
if (opts.useProxy) body["use_proxy"] = true;
|
|
258
|
+
if (opts.sessionArtifactId) body["session_artifact_id"] = opts.sessionArtifactId;
|
|
259
|
+
const raw = await this._t.post("evadr/fetch", body);
|
|
260
|
+
return parseFetch(raw, url);
|
|
261
|
+
}
|
|
262
|
+
async fetchAsync(url, opts = {}) {
|
|
263
|
+
const body = { url, async: true };
|
|
264
|
+
if (opts.forceBrowser) body["force_browser"] = true;
|
|
265
|
+
if (opts.useProxy) body["use_proxy"] = true;
|
|
266
|
+
if (opts.webhookUrl) body["webhook_url"] = opts.webhookUrl;
|
|
267
|
+
const raw = await this._t.post("evadr/fetch", body);
|
|
268
|
+
return String(raw["job_id"]);
|
|
269
|
+
}
|
|
270
|
+
async *fetchStream(url, opts = {}) {
|
|
271
|
+
const body = { url };
|
|
272
|
+
if (opts.forceBrowser) body["force_browser"] = true;
|
|
273
|
+
for await (const data of this._t.stream("evadr/fetch/stream", body)) {
|
|
274
|
+
yield {
|
|
275
|
+
type: String(data["type"] ?? ""),
|
|
276
|
+
tier: data["tier"] != null ? Number(data["tier"]) : null,
|
|
277
|
+
vendor: data["vendor"] ?? null,
|
|
278
|
+
metadata: data["metadata"] ?? {}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async analyze(url, opts = {}) {
|
|
283
|
+
const raw = await this._t.post("evadr/analyze", {
|
|
284
|
+
url,
|
|
285
|
+
status_code: opts.statusCode ?? 200,
|
|
286
|
+
headers: opts.headers ?? {},
|
|
287
|
+
body_snippet: opts.bodySnippet ?? null
|
|
288
|
+
});
|
|
289
|
+
return {
|
|
290
|
+
blocked: Boolean(raw["blocked"]),
|
|
291
|
+
vendor: raw["vendor"] ?? null,
|
|
292
|
+
confidence: Number(raw["confidence"] ?? 0),
|
|
293
|
+
recommendedActions: raw["recommended_actions"] ?? []
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
async storeProxy(name, proxyUrl) {
|
|
297
|
+
await this._t.post("evadr/proxies", { name, proxy_url: proxyUrl });
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// src/modules/fetchyr.ts
|
|
302
|
+
var FetchyrNamespace = class {
|
|
303
|
+
constructor(_t) {
|
|
304
|
+
this._t = _t;
|
|
305
|
+
}
|
|
306
|
+
async login(url, usernameSelector, passwordSelector, username, password, opts = {}) {
|
|
307
|
+
const body = {
|
|
308
|
+
url,
|
|
309
|
+
username_selector: usernameSelector,
|
|
310
|
+
password_selector: passwordSelector,
|
|
311
|
+
username,
|
|
312
|
+
password
|
|
313
|
+
};
|
|
314
|
+
if (opts.submitSelector) body["submit_selector"] = opts.submitSelector;
|
|
315
|
+
if (opts.successUrlContains) body["success_url_contains"] = opts.successUrlContains;
|
|
316
|
+
const raw = await this._t.post("fetchyr/login", body);
|
|
317
|
+
const result = {
|
|
318
|
+
success: Boolean(raw["success"]),
|
|
319
|
+
sessionId: raw["session_id"] ?? "",
|
|
320
|
+
url: raw["url"] ?? url,
|
|
321
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
322
|
+
screenshotUrl: raw["screenshot_url"] ?? null,
|
|
323
|
+
error: raw["error"] ?? null
|
|
324
|
+
};
|
|
325
|
+
return { ...result, get ok() {
|
|
326
|
+
return result.success && result.error === null;
|
|
327
|
+
} };
|
|
328
|
+
}
|
|
329
|
+
async fetch(url, sessionArtifactId, opts = {}) {
|
|
330
|
+
const body = { url, session_artifact_id: sessionArtifactId };
|
|
331
|
+
if (opts.waitForSelector) body["wait_for_selector"] = opts.waitForSelector;
|
|
332
|
+
if (opts.extractHtml !== void 0) body["extract_html"] = opts.extractHtml;
|
|
333
|
+
const raw = await this._t.post("fetchyr/fetch", body);
|
|
334
|
+
const result = {
|
|
335
|
+
success: Boolean(raw["success"]),
|
|
336
|
+
url: raw["url"] ?? url,
|
|
337
|
+
statusCode: Number(raw["status_code"] ?? 0),
|
|
338
|
+
html: raw["html"] ?? null,
|
|
339
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
340
|
+
sessionArtifactId,
|
|
341
|
+
error: raw["error"] ?? null
|
|
342
|
+
};
|
|
343
|
+
return { ...result, get ok() {
|
|
344
|
+
return result.success && result.error === null;
|
|
345
|
+
} };
|
|
346
|
+
}
|
|
347
|
+
async getSession(artifactId) {
|
|
348
|
+
return this._t.get(`fetchyr/sessions/${artifactId}`);
|
|
349
|
+
}
|
|
350
|
+
async invalidateSession(artifactId) {
|
|
351
|
+
await this._t.delete(`fetchyr/sessions/${artifactId}`);
|
|
352
|
+
}
|
|
353
|
+
async createWorkflow(name, steps, url) {
|
|
354
|
+
const body = { name, steps };
|
|
355
|
+
if (url) body["url"] = url;
|
|
356
|
+
const raw = await this._t.post("fetchyr/workflows", body);
|
|
357
|
+
const result = {
|
|
358
|
+
workflowId: raw["workflow_id"] ?? "",
|
|
359
|
+
name: raw["name"] ?? name,
|
|
360
|
+
steps: raw["steps"] ?? steps,
|
|
361
|
+
url: raw["url"] ?? null,
|
|
362
|
+
createdAt: raw["created_at"] ?? "",
|
|
363
|
+
error: raw["error"] ?? null
|
|
364
|
+
};
|
|
365
|
+
return { ...result, get ok() {
|
|
366
|
+
return result.error === null;
|
|
367
|
+
} };
|
|
368
|
+
}
|
|
369
|
+
async executeWorkflow(workflowId) {
|
|
370
|
+
const raw = await this._t.post(`fetchyr/workflows/${workflowId}/execute`, {});
|
|
371
|
+
return parseExecution(raw);
|
|
372
|
+
}
|
|
373
|
+
async getExecution(workflowId, executionId) {
|
|
374
|
+
const raw = await this._t.get(`fetchyr/workflows/${workflowId}/executions/${executionId}`);
|
|
375
|
+
return parseExecution(raw);
|
|
376
|
+
}
|
|
377
|
+
async detectForms(url, opts = {}) {
|
|
378
|
+
const body = { url };
|
|
379
|
+
if (opts.sessionArtifactId) body["session_artifact_id"] = opts.sessionArtifactId;
|
|
380
|
+
const raw = await this._t.post("fetchyr/detect-forms", body);
|
|
381
|
+
return {
|
|
382
|
+
forms: raw["forms"] ?? [],
|
|
383
|
+
totalForms: Number(raw["total_forms"] ?? 0),
|
|
384
|
+
error: raw["error"] ?? null
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
async detectMfa(url, opts = {}) {
|
|
388
|
+
const body = { url };
|
|
389
|
+
if (opts.sessionArtifactId) body["session_artifact_id"] = opts.sessionArtifactId;
|
|
390
|
+
const raw = await this._t.post("fetchyr/detect-mfa", body);
|
|
391
|
+
return {
|
|
392
|
+
mfaDetected: Boolean(raw["mfa_detected"]),
|
|
393
|
+
challengeId: raw["challenge_id"] ?? null,
|
|
394
|
+
mfaType: raw["mfa_type"] ?? null,
|
|
395
|
+
error: raw["error"] ?? null
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
async submitMfa(challengeId, code) {
|
|
399
|
+
const raw = await this._t.post("fetchyr/submit-mfa", { challenge_id: challengeId, code });
|
|
400
|
+
return {
|
|
401
|
+
success: Boolean(raw["success"]),
|
|
402
|
+
sessionArtifactId: raw["session_artifact_id"] ?? null,
|
|
403
|
+
error: raw["error"] ?? null
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async checkDuplicates(records, opts = {}) {
|
|
407
|
+
const body = { records };
|
|
408
|
+
if (opts.domain) body["domain"] = opts.domain;
|
|
409
|
+
const raw = await this._t.post("fetchyr/deduplicate", body);
|
|
410
|
+
const result = {
|
|
411
|
+
uniqueRecords: raw["unique_records"] ?? [],
|
|
412
|
+
duplicateCount: Number(raw["duplicate_count"] ?? 0),
|
|
413
|
+
totalInput: Number(raw["total_input"] ?? records.length),
|
|
414
|
+
error: raw["error"] ?? null
|
|
415
|
+
};
|
|
416
|
+
return { ...result, get ok() {
|
|
417
|
+
return result.error === null;
|
|
418
|
+
} };
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
function parseExecution(raw) {
|
|
422
|
+
const result = {
|
|
423
|
+
executionId: raw["execution_id"] ?? "",
|
|
424
|
+
workflowId: raw["workflow_id"] ?? "",
|
|
425
|
+
status: raw["status"] ?? "unknown",
|
|
426
|
+
startedAt: raw["started_at"] ?? null,
|
|
427
|
+
completedAt: raw["completed_at"] ?? null,
|
|
428
|
+
records: raw["records"] ?? [],
|
|
429
|
+
error: raw["error"] ?? null
|
|
430
|
+
};
|
|
431
|
+
return { ...result, get ok() {
|
|
432
|
+
return result.status === "completed" && result.error === null;
|
|
433
|
+
} };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/modules/kolektr.ts
|
|
437
|
+
function parseExtraction(raw, url) {
|
|
438
|
+
const result = {
|
|
439
|
+
success: Boolean(raw["success"]),
|
|
440
|
+
url: raw["url"] ?? url,
|
|
441
|
+
method: raw["method"] ?? "unknown",
|
|
442
|
+
records: raw["records"] ?? [],
|
|
443
|
+
totalRecords: Number(raw["total_records"] ?? 0),
|
|
444
|
+
pagesScraped: Number(raw["pages_scraped"] ?? 0),
|
|
445
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
446
|
+
jobId: raw["job_id"] ?? null,
|
|
447
|
+
error: raw["error"] ?? null,
|
|
448
|
+
hasMore: Boolean(raw["has_more"]),
|
|
449
|
+
total: Number(raw["total"] ?? 0)
|
|
450
|
+
};
|
|
451
|
+
return { ...result, get ok() {
|
|
452
|
+
return result.success && result.error === null;
|
|
453
|
+
} };
|
|
454
|
+
}
|
|
455
|
+
var KolektrNamespace = class {
|
|
456
|
+
constructor(_t) {
|
|
457
|
+
this._t = _t;
|
|
458
|
+
}
|
|
459
|
+
async page(url, opts = {}) {
|
|
460
|
+
const body = {
|
|
461
|
+
url,
|
|
462
|
+
limit: opts.limit ?? 100,
|
|
463
|
+
offset: opts.offset ?? 0
|
|
464
|
+
};
|
|
465
|
+
if (opts.schema) body["schema"] = opts.schema;
|
|
466
|
+
if (opts.fetchArtifactId) body["fetch_artifact_id"] = opts.fetchArtifactId;
|
|
467
|
+
if (opts.sessionArtifactId) body["session_artifact_id"] = opts.sessionArtifactId;
|
|
468
|
+
if (opts.apiMapArtifactId) body["api_map_artifact_id"] = opts.apiMapArtifactId;
|
|
469
|
+
if (opts.options) body["options"] = opts.options;
|
|
470
|
+
const raw = await this._t.post("kolektr/extract", body);
|
|
471
|
+
return parseExtraction(raw, url);
|
|
472
|
+
}
|
|
473
|
+
async pageAll(url, opts = {}) {
|
|
474
|
+
const all = [];
|
|
475
|
+
let offset = 0;
|
|
476
|
+
while (true) {
|
|
477
|
+
const result = await this.page(url, { ...opts, limit: 100, offset });
|
|
478
|
+
all.push(...result.records);
|
|
479
|
+
if (!result.hasMore) break;
|
|
480
|
+
offset += result.records.length;
|
|
481
|
+
}
|
|
482
|
+
return all;
|
|
483
|
+
}
|
|
484
|
+
async extractHtml(html, url, opts = {}) {
|
|
485
|
+
const body = { html, url };
|
|
486
|
+
if (opts.schema) body["schema"] = opts.schema;
|
|
487
|
+
const raw = await this._t.post("kolektr/extract/html", body);
|
|
488
|
+
return parseExtraction(raw, url);
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// src/modules/nexus.ts
|
|
493
|
+
var NexusNamespace = class {
|
|
494
|
+
constructor(_t) {
|
|
495
|
+
this._t = _t;
|
|
496
|
+
}
|
|
497
|
+
async analyze(url, opts = {}) {
|
|
498
|
+
const body = { url };
|
|
499
|
+
if (opts.html) body["html"] = opts.html;
|
|
500
|
+
if (opts.constraints) body["constraints"] = opts.constraints;
|
|
501
|
+
const raw = await this._t.post("nexus/analyze", body);
|
|
502
|
+
const result = {
|
|
503
|
+
perceptionId: raw["perception_id"] ?? "",
|
|
504
|
+
strategy: raw["strategy"] ?? {},
|
|
505
|
+
pageType: raw["page_type"] ?? "unknown",
|
|
506
|
+
complexityLevel: raw["complexity_level"] ?? "unknown",
|
|
507
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
508
|
+
durationMs: Number(raw["duration_ms"] ?? 0),
|
|
509
|
+
error: raw["error"] ?? null
|
|
510
|
+
};
|
|
511
|
+
return { ...result, get ok() {
|
|
512
|
+
return result.error === null;
|
|
513
|
+
} };
|
|
514
|
+
}
|
|
515
|
+
async synthesize(perceptionId, opts = {}) {
|
|
516
|
+
const body = { perception_id: perceptionId };
|
|
517
|
+
if (opts.strategy) body["strategy"] = opts.strategy;
|
|
518
|
+
if (opts.timeout) body["timeout"] = opts.timeout;
|
|
519
|
+
const raw = await this._t.post("nexus/synthesize", body);
|
|
520
|
+
const result = {
|
|
521
|
+
strategyId: raw["strategy_id"] ?? "",
|
|
522
|
+
strategyName: raw["strategy_name"] ?? "",
|
|
523
|
+
generatedCode: raw["generated_code"] ?? "",
|
|
524
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
525
|
+
synthesisTimeMs: Number(raw["synthesis_time_ms"] ?? 0),
|
|
526
|
+
error: raw["error"] ?? null
|
|
527
|
+
};
|
|
528
|
+
return { ...result, get ok() {
|
|
529
|
+
return result.error === null;
|
|
530
|
+
} };
|
|
531
|
+
}
|
|
532
|
+
async verify(strategyId) {
|
|
533
|
+
const raw = await this._t.post("nexus/verify", { strategy_id: strategyId });
|
|
534
|
+
const result = {
|
|
535
|
+
verificationResultId: raw["verification_result_id"] ?? "",
|
|
536
|
+
isSafe: Boolean(raw["is_safe"]),
|
|
537
|
+
riskScore: Number(raw["risk_score"] ?? 0),
|
|
538
|
+
safetyScore: Number(raw["safety_score"] ?? 0),
|
|
539
|
+
violations: raw["violations"] ?? [],
|
|
540
|
+
durationMs: Number(raw["duration_ms"] ?? 0),
|
|
541
|
+
error: raw["error"] ?? null
|
|
542
|
+
};
|
|
543
|
+
return { ...result, get ok() {
|
|
544
|
+
return result.isSafe && result.error === null;
|
|
545
|
+
} };
|
|
546
|
+
}
|
|
547
|
+
async execute(strategyId, url) {
|
|
548
|
+
const raw = await this._t.post("nexus/execute", { strategy_id: strategyId, url });
|
|
549
|
+
const result = {
|
|
550
|
+
executionResultId: raw["execution_result_id"] ?? "",
|
|
551
|
+
success: Boolean(raw["success"]),
|
|
552
|
+
records: raw["data"] ?? [],
|
|
553
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
554
|
+
durationMs: Number(raw["duration_ms"] ?? 0),
|
|
555
|
+
error: raw["error"] ?? null
|
|
556
|
+
};
|
|
557
|
+
return { ...result, get ok() {
|
|
558
|
+
return result.success && result.error === null;
|
|
559
|
+
} };
|
|
560
|
+
}
|
|
561
|
+
async knowledge(executionResultId, opts = {}) {
|
|
562
|
+
const raw = await this._t.post("nexus/knowledge", {
|
|
563
|
+
execution_result_id: executionResultId,
|
|
564
|
+
limit: opts.limit ?? 100,
|
|
565
|
+
offset: opts.offset ?? 0
|
|
566
|
+
});
|
|
567
|
+
const result = {
|
|
568
|
+
learnedConcepts: raw["learned_concepts"] ?? [],
|
|
569
|
+
learnedPatterns: raw["learned_patterns"] ?? [],
|
|
570
|
+
durationMs: Number(raw["duration_ms"] ?? 0),
|
|
571
|
+
error: raw["error"] ?? null,
|
|
572
|
+
hasMore: Boolean(raw["has_more"]),
|
|
573
|
+
total: Number(raw["total"] ?? 0)
|
|
574
|
+
};
|
|
575
|
+
return { ...result, get ok() {
|
|
576
|
+
return result.error === null;
|
|
577
|
+
} };
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// src/modules/parlyr.ts
|
|
582
|
+
var ParlyrNamespace = class {
|
|
583
|
+
constructor(_t) {
|
|
584
|
+
this._t = _t;
|
|
585
|
+
}
|
|
586
|
+
async parse(message, opts = {}) {
|
|
587
|
+
const body = { message };
|
|
588
|
+
if (opts.sessionId) body["session_id"] = opts.sessionId;
|
|
589
|
+
const raw = await this._t.post("parlyr/parse", body);
|
|
590
|
+
const result = {
|
|
591
|
+
intent: raw["intent"] ?? "unknown",
|
|
592
|
+
confidence: Number(raw["confidence"] ?? 0),
|
|
593
|
+
tier: Number(raw["tier"] ?? 0),
|
|
594
|
+
source: raw["source"] ?? "",
|
|
595
|
+
entities: raw["entities"] ?? {},
|
|
596
|
+
requiresAction: Boolean(raw["requires_action"]),
|
|
597
|
+
clarificationNeeded: raw["clarification_needed"] ?? null,
|
|
598
|
+
reasoning: raw["reasoning"] ?? null,
|
|
599
|
+
detectedUrl: raw["detected_url"] ?? null
|
|
600
|
+
};
|
|
601
|
+
return { ...result, get ok() {
|
|
602
|
+
return !result.requiresAction;
|
|
603
|
+
} };
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Send one chat turn and collect all SSE events into a ChatTurn.
|
|
607
|
+
* Blocks until the `done` event is received.
|
|
608
|
+
*/
|
|
609
|
+
async chat(sessionId, message) {
|
|
610
|
+
let intentData = {};
|
|
611
|
+
let responseText = "";
|
|
612
|
+
for await (const { event, data } of this._t.streamWithEvents("parlyr/chat", {
|
|
613
|
+
session_id: sessionId,
|
|
614
|
+
message
|
|
615
|
+
})) {
|
|
616
|
+
if (event === "intent") intentData = data;
|
|
617
|
+
else if (event === "response") responseText = data["content"] ?? "";
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
sessionId,
|
|
621
|
+
intent: intentData["intent"] ?? "unknown",
|
|
622
|
+
confidence: Number(intentData["confidence"] ?? 0),
|
|
623
|
+
tier: Number(intentData["tier"] ?? 0),
|
|
624
|
+
response: responseText,
|
|
625
|
+
entities: intentData["entities"] ?? {},
|
|
626
|
+
requiresAction: Boolean(intentData["requires_action"]),
|
|
627
|
+
clarificationNeeded: intentData["clarification_needed"] ?? null
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Async iterable SSE stream for one chat turn.
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```typescript
|
|
635
|
+
* for await (const event of client.parlyr.chatStream('sess-1', 'Scrape linkedin.com')) {
|
|
636
|
+
* console.log(event.event, event.data);
|
|
637
|
+
* }
|
|
638
|
+
* ```
|
|
639
|
+
*/
|
|
640
|
+
async *chatStream(sessionId, message) {
|
|
641
|
+
for await (const { event, data } of this._t.streamWithEvents("parlyr/chat", {
|
|
642
|
+
session_id: sessionId,
|
|
643
|
+
message
|
|
644
|
+
})) {
|
|
645
|
+
yield { event, data };
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
async deleteSession(sessionId) {
|
|
649
|
+
await this._t.delete(`parlyr/chat/${sessionId}`);
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// src/modules/skanyr.ts
|
|
654
|
+
function parseEndpoint(e) {
|
|
655
|
+
return {
|
|
656
|
+
url: String(e["url"] ?? ""),
|
|
657
|
+
method: String(e["method"] ?? "GET"),
|
|
658
|
+
apiType: String(e["api_type"] ?? "rest"),
|
|
659
|
+
confidence: Number(e["confidence"] ?? 0),
|
|
660
|
+
parameters: e["parameters"] ?? {}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function parseDiscover(raw, url) {
|
|
664
|
+
const endpoints = (raw["endpoints"] ?? []).map(parseEndpoint);
|
|
665
|
+
const result = {
|
|
666
|
+
success: Boolean(raw["success"]),
|
|
667
|
+
discoveryId: raw["discovery_id"] ?? "",
|
|
668
|
+
url: raw["url"] ?? url,
|
|
669
|
+
totalEndpoints: Number(raw["total_endpoints"] ?? 0),
|
|
670
|
+
endpoints,
|
|
671
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
672
|
+
error: raw["error"] ?? null,
|
|
673
|
+
hasMore: Boolean(raw["has_more"]),
|
|
674
|
+
total: Number(raw["total"] ?? 0)
|
|
675
|
+
};
|
|
676
|
+
return { ...result, get ok() {
|
|
677
|
+
return result.success && result.error === null;
|
|
678
|
+
} };
|
|
679
|
+
}
|
|
680
|
+
var SkanyrNamespace = class {
|
|
681
|
+
constructor(_t) {
|
|
682
|
+
this._t = _t;
|
|
683
|
+
}
|
|
684
|
+
async discover(url, opts = {}) {
|
|
685
|
+
const body = {
|
|
686
|
+
url,
|
|
687
|
+
max_requests: opts.maxRequests ?? 200,
|
|
688
|
+
limit: opts.limit ?? 100,
|
|
689
|
+
offset: opts.offset ?? 0
|
|
690
|
+
};
|
|
691
|
+
if (opts.siteHierarchyArtifactId) body["site_hierarchy_artifact_id"] = opts.siteHierarchyArtifactId;
|
|
692
|
+
if (opts.sessionArtifactId) body["session_artifact_id"] = opts.sessionArtifactId;
|
|
693
|
+
const raw = await this._t.post("skanyr/discover", body);
|
|
694
|
+
return parseDiscover(raw, url);
|
|
695
|
+
}
|
|
696
|
+
async discoverAll(url, opts = {}) {
|
|
697
|
+
const all = [];
|
|
698
|
+
let offset = 0;
|
|
699
|
+
while (true) {
|
|
700
|
+
const result = await this.discover(url, { ...opts, limit: 100, offset });
|
|
701
|
+
all.push(...result.endpoints);
|
|
702
|
+
if (!result.hasMore) break;
|
|
703
|
+
offset += result.endpoints.length;
|
|
704
|
+
}
|
|
705
|
+
return all;
|
|
706
|
+
}
|
|
707
|
+
async *discoverStream(url, opts = {}) {
|
|
708
|
+
const body = { url, max_requests: opts.maxRequests ?? 200 };
|
|
709
|
+
if (opts.siteHierarchyArtifactId) body["site_hierarchy_artifact_id"] = opts.siteHierarchyArtifactId;
|
|
710
|
+
for await (const data of this._t.stream("skanyr/discover/stream", body)) {
|
|
711
|
+
yield {
|
|
712
|
+
type: String(data["type"] ?? ""),
|
|
713
|
+
endpointUrl: data["endpoint_url"] ?? null,
|
|
714
|
+
apiType: data["api_type"] ?? null,
|
|
715
|
+
metadata: data["metadata"] ?? {}
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
async getApiMap(artifactId) {
|
|
720
|
+
return this._t.get(`skanyr/api-map/${artifactId}`);
|
|
721
|
+
}
|
|
722
|
+
async getJob(jobId) {
|
|
723
|
+
return this._t.get(`skanyr/jobs/${jobId}`);
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
// src/modules/webgrph.ts
|
|
728
|
+
function parsePageNode(n) {
|
|
729
|
+
return {
|
|
730
|
+
url: String(n["url"] ?? ""),
|
|
731
|
+
depth: Number(n["depth"] ?? 0),
|
|
732
|
+
title: n["title"] ?? null,
|
|
733
|
+
statusCode: n["status_code"] != null ? Number(n["status_code"]) : null,
|
|
734
|
+
children: n["children"] ?? []
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
function parseCrawl(raw, url) {
|
|
738
|
+
const pages = (raw["pages"] ?? []).map(parsePageNode);
|
|
739
|
+
const result = {
|
|
740
|
+
success: Boolean(raw["success"]),
|
|
741
|
+
crawlId: raw["crawl_id"] ?? "",
|
|
742
|
+
url: raw["url"] ?? url,
|
|
743
|
+
totalPages: Number(raw["total_pages"] ?? 0),
|
|
744
|
+
maxDepthReached: Number(raw["max_depth_reached"] ?? 0),
|
|
745
|
+
pages,
|
|
746
|
+
artifactId: raw["artifact_id"] ?? null,
|
|
747
|
+
error: raw["error"] ?? null,
|
|
748
|
+
hasMore: Boolean(raw["has_more"]),
|
|
749
|
+
total: Number(raw["total"] ?? 0)
|
|
750
|
+
};
|
|
751
|
+
return { ...result, get ok() {
|
|
752
|
+
return result.success && result.error === null;
|
|
753
|
+
} };
|
|
754
|
+
}
|
|
755
|
+
var WebgrphNamespace = class {
|
|
756
|
+
constructor(_t) {
|
|
757
|
+
this._t = _t;
|
|
758
|
+
}
|
|
759
|
+
async crawl(url, opts = {}) {
|
|
760
|
+
const body = {
|
|
761
|
+
url,
|
|
762
|
+
max_depth: opts.maxDepth ?? 3,
|
|
763
|
+
max_pages: opts.maxPages ?? 100,
|
|
764
|
+
include_external_links: opts.includeExternalLinks ?? false,
|
|
765
|
+
limit: opts.limit ?? 100,
|
|
766
|
+
offset: opts.offset ?? 0
|
|
767
|
+
};
|
|
768
|
+
if (opts.sessionArtifactId) body["session_artifact_id"] = opts.sessionArtifactId;
|
|
769
|
+
const raw = await this._t.post("webgrph/crawl", body);
|
|
770
|
+
return parseCrawl(raw, url);
|
|
771
|
+
}
|
|
772
|
+
async crawlAll(url, opts = {}) {
|
|
773
|
+
const all = [];
|
|
774
|
+
let offset = 0;
|
|
775
|
+
while (true) {
|
|
776
|
+
const result = await this.crawl(url, { ...opts, limit: 100, offset });
|
|
777
|
+
all.push(...result.pages);
|
|
778
|
+
if (!result.hasMore) break;
|
|
779
|
+
offset += result.pages.length;
|
|
780
|
+
}
|
|
781
|
+
return all;
|
|
782
|
+
}
|
|
783
|
+
async *crawlStream(url, opts = {}) {
|
|
784
|
+
const body = {
|
|
785
|
+
url,
|
|
786
|
+
max_depth: opts.maxDepth ?? 3,
|
|
787
|
+
max_pages: opts.maxPages ?? 100
|
|
788
|
+
};
|
|
789
|
+
for await (const data of this._t.stream("webgrph/crawl/stream", body)) {
|
|
790
|
+
yield {
|
|
791
|
+
type: String(data["type"] ?? ""),
|
|
792
|
+
url: data["url"] ?? null,
|
|
793
|
+
depth: data["depth"] != null ? Number(data["depth"]) : null,
|
|
794
|
+
pagesFound: data["pages_found"] != null ? Number(data["pages_found"]) : null,
|
|
795
|
+
metadata: data["metadata"] ?? {}
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
async getHierarchy(artifactId) {
|
|
800
|
+
return this._t.get(`webgrph/hierarchy/${artifactId}`);
|
|
801
|
+
}
|
|
802
|
+
async getJob(jobId) {
|
|
803
|
+
return this._t.get(`webgrph/jobs/${jobId}`);
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
// src/client.ts
|
|
808
|
+
var DEFAULT_BASE_URL = "https://api.kloakd.dev";
|
|
809
|
+
var DEFAULT_TIMEOUT = 60;
|
|
810
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
811
|
+
var Kloakd = class {
|
|
812
|
+
constructor(opts) {
|
|
813
|
+
if (!opts.apiKey?.trim()) throw new Error("Kloakd: apiKey is required");
|
|
814
|
+
if (!opts.organizationId?.trim()) throw new Error("Kloakd: organizationId is required");
|
|
815
|
+
this._transport = new HttpTransport({
|
|
816
|
+
apiKey: opts.apiKey,
|
|
817
|
+
organizationId: opts.organizationId,
|
|
818
|
+
baseUrl: opts.baseUrl ?? DEFAULT_BASE_URL,
|
|
819
|
+
timeout: opts.timeout ?? DEFAULT_TIMEOUT,
|
|
820
|
+
maxRetries: opts.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
821
|
+
...opts.fetchImpl !== void 0 && { fetchImpl: opts.fetchImpl }
|
|
822
|
+
});
|
|
823
|
+
this.evadr = new EvadrNamespace(this._transport);
|
|
824
|
+
this.webgrph = new WebgrphNamespace(this._transport);
|
|
825
|
+
this.skanyr = new SkanyrNamespace(this._transport);
|
|
826
|
+
this.nexus = new NexusNamespace(this._transport);
|
|
827
|
+
this.parlyr = new ParlyrNamespace(this._transport);
|
|
828
|
+
this.fetchyr = new FetchyrNamespace(this._transport);
|
|
829
|
+
this.kolektr = new KolektrNamespace(this._transport);
|
|
830
|
+
}
|
|
831
|
+
toString() {
|
|
832
|
+
return `Kloakd(organizationId=${this._transport._organizationId}, baseUrl=${this._transport._baseUrl})`;
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
export {
|
|
836
|
+
ApiError,
|
|
837
|
+
AuthenticationError,
|
|
838
|
+
Kloakd,
|
|
839
|
+
KloakdError,
|
|
840
|
+
NotEntitledError,
|
|
841
|
+
RateLimitError,
|
|
842
|
+
UpstreamError
|
|
843
|
+
};
|