@safekeylab/sdk 1.0.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/README.md +212 -0
- package/dist/index.d.mts +100 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.js +241 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +231 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/anthropic.d.mts +65 -0
- package/dist/integrations/anthropic.d.ts +65 -0
- package/dist/integrations/anthropic.js +347 -0
- package/dist/integrations/anthropic.js.map +1 -0
- package/dist/integrations/anthropic.mjs +341 -0
- package/dist/integrations/anthropic.mjs.map +1 -0
- package/dist/integrations/langchain.d.mts +106 -0
- package/dist/integrations/langchain.d.ts +106 -0
- package/dist/integrations/langchain.js +415 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/integrations/langchain.mjs +406 -0
- package/dist/integrations/langchain.mjs.map +1 -0
- package/dist/integrations/openai.d.mts +72 -0
- package/dist/integrations/openai.d.ts +72 -0
- package/dist/integrations/openai.js +327 -0
- package/dist/integrations/openai.js.map +1 -0
- package/dist/integrations/openai.mjs +321 -0
- package/dist/integrations/openai.mjs.map +1 -0
- package/dist/integrations/vercel-ai.d.mts +80 -0
- package/dist/integrations/vercel-ai.d.ts +80 -0
- package/dist/integrations/vercel-ai.js +377 -0
- package/dist/integrations/vercel-ai.js.map +1 -0
- package/dist/integrations/vercel-ai.mjs +371 -0
- package/dist/integrations/vercel-ai.mjs.map +1 -0
- package/dist/types-CtDYLaOX.d.mts +129 -0
- package/dist/types-CtDYLaOX.d.ts +129 -0
- package/package.json +106 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
// src/types.ts
|
|
6
|
+
var SafeKeyLabError = class extends Error {
|
|
7
|
+
constructor(message, code, statusCode) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.name = "SafeKeyLabError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var PIIBlockedError = class extends SafeKeyLabError {
|
|
15
|
+
constructor(detections) {
|
|
16
|
+
super(
|
|
17
|
+
`Request blocked: PII detected (${detections.map((d) => d.type).join(", ")})`,
|
|
18
|
+
"PII_BLOCKED"
|
|
19
|
+
);
|
|
20
|
+
this.detections = detections;
|
|
21
|
+
this.name = "PIIBlockedError";
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var ThreatBlockedError = class extends SafeKeyLabError {
|
|
25
|
+
constructor(threats) {
|
|
26
|
+
super(
|
|
27
|
+
`Request blocked: Threat detected (${threats.map((t) => t.type).join(", ")})`,
|
|
28
|
+
"THREAT_BLOCKED"
|
|
29
|
+
);
|
|
30
|
+
this.threats = threats;
|
|
31
|
+
this.name = "ThreatBlockedError";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/client.ts
|
|
36
|
+
var DEFAULT_BASE_URL = "https://api.safekeylab.com";
|
|
37
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
38
|
+
var SafeKeyLab = class {
|
|
39
|
+
constructor(config) {
|
|
40
|
+
if (!config.apiKey) {
|
|
41
|
+
throw new SafeKeyLabError("API key is required", "MISSING_API_KEY");
|
|
42
|
+
}
|
|
43
|
+
this.apiKey = config.apiKey;
|
|
44
|
+
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
45
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
46
|
+
this.debug = config.debug || false;
|
|
47
|
+
}
|
|
48
|
+
log(...args) {
|
|
49
|
+
if (this.debug) {
|
|
50
|
+
console.log("[SafeKeyLab]", ...args);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async request(method, endpoint, body) {
|
|
54
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
57
|
+
this.log(`${method} ${url}`, body);
|
|
58
|
+
try {
|
|
59
|
+
const response = await fetch(url, {
|
|
60
|
+
method,
|
|
61
|
+
headers: {
|
|
62
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
"User-Agent": "@safekeylab/sdk/1.0.0"
|
|
65
|
+
},
|
|
66
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
67
|
+
signal: controller.signal
|
|
68
|
+
});
|
|
69
|
+
clearTimeout(timeoutId);
|
|
70
|
+
const data = await response.json();
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new SafeKeyLabError(
|
|
73
|
+
data.error || data.message || "Request failed",
|
|
74
|
+
data.code || "API_ERROR",
|
|
75
|
+
response.status
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
this.log("Response:", data);
|
|
79
|
+
return data;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
clearTimeout(timeoutId);
|
|
82
|
+
if (error instanceof SafeKeyLabError) {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
if (error instanceof Error) {
|
|
86
|
+
if (error.name === "AbortError") {
|
|
87
|
+
throw new SafeKeyLabError("Request timeout", "TIMEOUT");
|
|
88
|
+
}
|
|
89
|
+
throw new SafeKeyLabError(error.message, "NETWORK_ERROR");
|
|
90
|
+
}
|
|
91
|
+
throw new SafeKeyLabError("Unknown error", "UNKNOWN_ERROR");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Detect PII in text
|
|
96
|
+
*/
|
|
97
|
+
async detect(request) {
|
|
98
|
+
return this.request("POST", "/v1/detect", request);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Detect PII in text (convenience method)
|
|
102
|
+
*/
|
|
103
|
+
async detectPII(text, options) {
|
|
104
|
+
return this.detect({ text, ...options });
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Redact/protect PII in text
|
|
108
|
+
*/
|
|
109
|
+
async protect(request) {
|
|
110
|
+
return this.request("POST", "/v1/protect", request);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Redact PII from text (convenience method)
|
|
114
|
+
*/
|
|
115
|
+
async redact(text, options) {
|
|
116
|
+
const response = await this.protect({ text, ...options });
|
|
117
|
+
return response.redacted_text;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Validate a prompt for security threats
|
|
121
|
+
*/
|
|
122
|
+
async validatePrompt(request) {
|
|
123
|
+
return this.request("POST", "/v1/validate-prompt", request);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if a prompt is safe (convenience method)
|
|
127
|
+
*/
|
|
128
|
+
async isPromptSafe(prompt, context) {
|
|
129
|
+
const response = await this.validatePrompt({ prompt, context });
|
|
130
|
+
return response.is_safe;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Scan LLM output for issues
|
|
134
|
+
*/
|
|
135
|
+
async scanOutput(request) {
|
|
136
|
+
return this.request("POST", "/v1/scan-output", request);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Full protection pipeline: validate input, redact PII, return protected text
|
|
140
|
+
*/
|
|
141
|
+
async protectInput(text) {
|
|
142
|
+
const [detectResult, validateResult] = await Promise.all([
|
|
143
|
+
this.detect({ text }),
|
|
144
|
+
this.validatePrompt({ prompt: text })
|
|
145
|
+
]);
|
|
146
|
+
let protectedText = text;
|
|
147
|
+
let wasModified = false;
|
|
148
|
+
if (detectResult.count > 0) {
|
|
149
|
+
const protectResult = await this.protect({ text });
|
|
150
|
+
protectedText = protectResult.redacted_text;
|
|
151
|
+
wasModified = true;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
text: protectedText,
|
|
155
|
+
wasModified,
|
|
156
|
+
detections: detectResult.detections,
|
|
157
|
+
threats: validateResult.threats
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create a sandbox client (no API key required, rate limited)
|
|
162
|
+
*/
|
|
163
|
+
static sandbox(options) {
|
|
164
|
+
return new SafeKeyLabSandbox(options);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
var SafeKeyLabSandbox = class {
|
|
168
|
+
constructor(config) {
|
|
169
|
+
this.baseUrl = config?.baseUrl || DEFAULT_BASE_URL;
|
|
170
|
+
this.timeout = config?.timeout || DEFAULT_TIMEOUT;
|
|
171
|
+
this.debug = config?.debug || false;
|
|
172
|
+
}
|
|
173
|
+
log(...args) {
|
|
174
|
+
if (this.debug) {
|
|
175
|
+
console.log("[SafeKeyLab:Sandbox]", ...args);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async request(method, endpoint, body) {
|
|
179
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
180
|
+
const controller = new AbortController();
|
|
181
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
182
|
+
this.log(`${method} ${url}`, body);
|
|
183
|
+
try {
|
|
184
|
+
const response = await fetch(url, {
|
|
185
|
+
method,
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": "application/json",
|
|
188
|
+
"User-Agent": "@safekeylab/sdk/1.0.0"
|
|
189
|
+
},
|
|
190
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
191
|
+
signal: controller.signal
|
|
192
|
+
});
|
|
193
|
+
clearTimeout(timeoutId);
|
|
194
|
+
const data = await response.json();
|
|
195
|
+
if (!response.ok) {
|
|
196
|
+
throw new SafeKeyLabError(
|
|
197
|
+
data.error || data.message || "Request failed",
|
|
198
|
+
data.code || "API_ERROR",
|
|
199
|
+
response.status
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
this.log("Response:", data);
|
|
203
|
+
return data;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
clearTimeout(timeoutId);
|
|
206
|
+
if (error instanceof SafeKeyLabError) {
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
if (error instanceof Error) {
|
|
210
|
+
if (error.name === "AbortError") {
|
|
211
|
+
throw new SafeKeyLabError("Request timeout", "TIMEOUT");
|
|
212
|
+
}
|
|
213
|
+
throw new SafeKeyLabError(error.message, "NETWORK_ERROR");
|
|
214
|
+
}
|
|
215
|
+
throw new SafeKeyLabError("Unknown error", "UNKNOWN_ERROR");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async detect(request) {
|
|
219
|
+
return this.request("POST", "/sandbox/v1/detect", request);
|
|
220
|
+
}
|
|
221
|
+
async protect(request) {
|
|
222
|
+
return this.request("POST", "/sandbox/v1/protect", request);
|
|
223
|
+
}
|
|
224
|
+
async validatePrompt(request) {
|
|
225
|
+
return this.request("POST", "/sandbox/v1/validate-prompt", request);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// src/integrations/langchain.ts
|
|
230
|
+
var SafeKeyLabCallbackHandler = class {
|
|
231
|
+
constructor(config) {
|
|
232
|
+
this.detections = [];
|
|
233
|
+
this.threats = [];
|
|
234
|
+
this.name = "SafeKeyLabCallbackHandler";
|
|
235
|
+
this.ignoreLLM = false;
|
|
236
|
+
this.ignoreChain = false;
|
|
237
|
+
this.ignoreAgent = false;
|
|
238
|
+
this.ignoreRetriever = false;
|
|
239
|
+
this.client = new SafeKeyLab({
|
|
240
|
+
apiKey: config.apiKey,
|
|
241
|
+
baseUrl: config.baseUrl,
|
|
242
|
+
timeout: config.timeout,
|
|
243
|
+
debug: config.debug
|
|
244
|
+
});
|
|
245
|
+
this.config = config;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Called when LLM starts processing
|
|
249
|
+
*/
|
|
250
|
+
async handleLLMStart(llm, prompts, runId) {
|
|
251
|
+
for (const prompt of prompts) {
|
|
252
|
+
const detectResult = await this.client.detect({
|
|
253
|
+
text: prompt,
|
|
254
|
+
types: this.config.piiTypes
|
|
255
|
+
});
|
|
256
|
+
if (detectResult.detections.length > 0) {
|
|
257
|
+
this.detections.push(...detectResult.detections);
|
|
258
|
+
console.warn(
|
|
259
|
+
`[SafeKeyLab] PII detected in prompt: ${detectResult.detections.map((d) => d.type).join(", ")}`
|
|
260
|
+
);
|
|
261
|
+
if (this.config.blockOnPII) {
|
|
262
|
+
throw new PIIBlockedError(detectResult.detections);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const validateResult = await this.client.validatePrompt({ prompt });
|
|
266
|
+
if (validateResult.threats.length > 0) {
|
|
267
|
+
this.threats.push(...validateResult.threats);
|
|
268
|
+
console.warn(
|
|
269
|
+
`[SafeKeyLab] Threats detected in prompt: ${validateResult.threats.map((t) => t.type).join(", ")}`
|
|
270
|
+
);
|
|
271
|
+
if (this.config.blockOnThreat !== false) {
|
|
272
|
+
throw new ThreatBlockedError(validateResult.threats);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Called when chat model starts
|
|
279
|
+
*/
|
|
280
|
+
async handleChatModelStart(llm, messages, runId) {
|
|
281
|
+
for (const messageGroup of messages) {
|
|
282
|
+
for (const message of messageGroup) {
|
|
283
|
+
if (typeof message.content === "string") {
|
|
284
|
+
const detectResult = await this.client.detect({
|
|
285
|
+
text: message.content,
|
|
286
|
+
types: this.config.piiTypes
|
|
287
|
+
});
|
|
288
|
+
if (detectResult.detections.length > 0) {
|
|
289
|
+
this.detections.push(...detectResult.detections);
|
|
290
|
+
if (this.config.blockOnPII) {
|
|
291
|
+
throw new PIIBlockedError(detectResult.detections);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const validateResult = await this.client.validatePrompt({
|
|
295
|
+
prompt: message.content
|
|
296
|
+
});
|
|
297
|
+
if (validateResult.threats.length > 0) {
|
|
298
|
+
this.threats.push(...validateResult.threats);
|
|
299
|
+
if (this.config.blockOnThreat !== false) {
|
|
300
|
+
throw new ThreatBlockedError(validateResult.threats);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Called when LLM ends
|
|
309
|
+
*/
|
|
310
|
+
async handleLLMEnd(output, runId) {
|
|
311
|
+
for (const generation of output.generations.flat()) {
|
|
312
|
+
if (generation.text) {
|
|
313
|
+
const scanResult = await this.client.scanOutput({ output: generation.text });
|
|
314
|
+
if (!scanResult.is_safe) {
|
|
315
|
+
console.warn(
|
|
316
|
+
`[SafeKeyLab] Issues detected in LLM output: ${scanResult.issues.map((i) => i.type).join(", ")}`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Get all detections from this session
|
|
324
|
+
*/
|
|
325
|
+
getDetections() {
|
|
326
|
+
return [...this.detections];
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Get all threats from this session
|
|
330
|
+
*/
|
|
331
|
+
getThreats() {
|
|
332
|
+
return [...this.threats];
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Clear detection history
|
|
336
|
+
*/
|
|
337
|
+
clearHistory() {
|
|
338
|
+
this.detections = [];
|
|
339
|
+
this.threats = [];
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
function createPIIProtectionTool(config) {
|
|
343
|
+
const client = new SafeKeyLab({
|
|
344
|
+
apiKey: config.apiKey,
|
|
345
|
+
baseUrl: config.baseUrl,
|
|
346
|
+
timeout: config.timeout,
|
|
347
|
+
debug: config.debug
|
|
348
|
+
});
|
|
349
|
+
return {
|
|
350
|
+
name: "safekeylab_pii_protection",
|
|
351
|
+
description: "Detects and redacts PII (personally identifiable information) from text. Use this tool to protect sensitive data before processing or storing. Input should be the text to protect. Output is the redacted text.",
|
|
352
|
+
func: async (input) => {
|
|
353
|
+
const result = await client.protect({ text: input });
|
|
354
|
+
return result.redacted_text;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function createPIIDetectionTool(config) {
|
|
359
|
+
const client = new SafeKeyLab({
|
|
360
|
+
apiKey: config.apiKey,
|
|
361
|
+
baseUrl: config.baseUrl,
|
|
362
|
+
timeout: config.timeout,
|
|
363
|
+
debug: config.debug
|
|
364
|
+
});
|
|
365
|
+
return {
|
|
366
|
+
name: "safekeylab_pii_detection",
|
|
367
|
+
description: "Detects PII (personally identifiable information) in text without modifying it. Use this tool to identify what sensitive data is present. Input should be the text to scan. Output is a list of detected PII types and locations.",
|
|
368
|
+
func: async (input) => {
|
|
369
|
+
const result = await client.detect({ text: input });
|
|
370
|
+
if (result.count === 0) {
|
|
371
|
+
return "No PII detected.";
|
|
372
|
+
}
|
|
373
|
+
return `Found ${result.count} PII items:
|
|
374
|
+
` + result.detections.map((d) => `- ${d.type}: "${d.value}" (confidence: ${d.confidence})`).join("\n");
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function createPromptValidationTool(config) {
|
|
379
|
+
const client = new SafeKeyLab({
|
|
380
|
+
apiKey: config.apiKey,
|
|
381
|
+
baseUrl: config.baseUrl,
|
|
382
|
+
timeout: config.timeout,
|
|
383
|
+
debug: config.debug
|
|
384
|
+
});
|
|
385
|
+
return {
|
|
386
|
+
name: "safekeylab_prompt_validation",
|
|
387
|
+
description: "Validates a prompt for security threats like injection attacks. Use this tool to check if user input is safe before using it in an LLM prompt. Input should be the prompt to validate. Output indicates if the prompt is safe.",
|
|
388
|
+
func: async (input) => {
|
|
389
|
+
const result = await client.validatePrompt({ prompt: input });
|
|
390
|
+
if (result.is_safe) {
|
|
391
|
+
return "Prompt is safe.";
|
|
392
|
+
}
|
|
393
|
+
return `Prompt has ${result.threats_detected} threats:
|
|
394
|
+
` + result.threats.map((t) => `- ${t.type} (${t.severity}): ${t.description}`).join("\n") + `
|
|
395
|
+
Recommendation: ${result.recommendation}`;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function getSafeKeyLabTools(config) {
|
|
400
|
+
return [
|
|
401
|
+
createPIIProtectionTool(config),
|
|
402
|
+
createPIIDetectionTool(config),
|
|
403
|
+
createPromptValidationTool(config)
|
|
404
|
+
];
|
|
405
|
+
}
|
|
406
|
+
var langchain_default = SafeKeyLabCallbackHandler;
|
|
407
|
+
|
|
408
|
+
exports.SafeKeyLabCallbackHandler = SafeKeyLabCallbackHandler;
|
|
409
|
+
exports.createPIIDetectionTool = createPIIDetectionTool;
|
|
410
|
+
exports.createPIIProtectionTool = createPIIProtectionTool;
|
|
411
|
+
exports.createPromptValidationTool = createPromptValidationTool;
|
|
412
|
+
exports.default = langchain_default;
|
|
413
|
+
exports.getSafeKeyLabTools = getSafeKeyLabTools;
|
|
414
|
+
//# sourceMappingURL=langchain.js.map
|
|
415
|
+
//# sourceMappingURL=langchain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types.ts","../../src/client.ts","../../src/integrations/langchain.ts"],"names":[],"mappings":";;;;;AAgIO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EACzC,WAAA,CACE,OAAA,EACO,IAAA,EACA,UAAA,EACP;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAHN,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AAGP,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF,CAAA;AAEO,IAAM,eAAA,GAAN,cAA8B,eAAA,CAAgB;AAAA,EACnD,YACS,UAAA,EACP;AACA,IAAA,KAAA;AAAA,MACE,CAAA,+BAAA,EAAkC,WAAW,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,IAAI,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA;AAAA,MACxE;AAAA,KACF;AALO,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AAMP,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF,CAAA;AAEO,IAAM,kBAAA,GAAN,cAAiC,eAAA,CAAgB;AAAA,EACtD,YACS,OAAA,EACP;AACA,IAAA,KAAA;AAAA,MACE,CAAA,kCAAA,EAAqC,QAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,IAAI,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA;AAAA,MACxE;AAAA,KACF;AALO,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAMP,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAA;AAAA,EACd;AACF,CAAA;;;AChJA,IAAM,gBAAA,GAAmB,4BAAA;AACzB,IAAM,eAAA,GAAkB,GAAA;AAEjB,IAAM,aAAN,MAAiB;AAAA,EAMtB,YAAY,MAAA,EAA0B;AACpC,IAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,MAAA,MAAM,IAAI,eAAA,CAAgB,qBAAA,EAAuB,iBAAiB,CAAA;AAAA,IACpE;AACA,IAAA,IAAA,CAAK,SAAS,MAAA,CAAO,MAAA;AACrB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAO,OAAA,IAAW,gBAAA;AACjC,IAAA,IAAA,CAAK,OAAA,GAAU,OAAO,OAAA,IAAW,eAAA;AACjC,IAAA,IAAA,CAAK,KAAA,GAAQ,OAAO,KAAA,IAAS,KAAA;AAAA,EAC/B;AAAA,EAEQ,OAAO,IAAA,EAAuB;AACpC,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,GAAA,CAAI,cAAA,EAAgB,GAAG,IAAI,CAAA;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAc,OAAA,CACZ,MAAA,EACA,QAAA,EACA,IAAA,EACY;AACZ,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,GAAG,QAAQ,CAAA,CAAA;AACtC,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,OAAO,CAAA;AAEnE,IAAA,IAAA,CAAK,IAAI,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,GAAG,IAAI,IAAI,CAAA;AAEjC,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,MAAA;AAAA,QACA,OAAA,EAAS;AAAA,UACP,eAAA,EAAiB,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,UACtC,cAAA,EAAgB,kBAAA;AAAA,UAChB,YAAA,EAAc;AAAA,SAChB;AAAA,QACA,IAAA,EAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI,KAAA,CAAA;AAAA,QACpC,QAAQ,UAAA,CAAW;AAAA,OACpB,CAAA;AAED,MAAA,YAAA,CAAa,SAAS,CAAA;AAEtB,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,IAAI,eAAA;AAAA,UACP,IAAA,CAAK,KAAA,IAAqB,IAAA,CAAK,OAAA,IAAsB,gBAAA;AAAA,UACrD,KAAK,IAAA,IAAmB,WAAA;AAAA,UACzB,QAAA,CAAS;AAAA,SACX;AAAA,MACF;AAEA,MAAA,IAAA,CAAK,GAAA,CAAI,aAAa,IAAI,CAAA;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,YAAA,CAAa,SAAS,CAAA;AACtB,MAAA,IAAI,iBAAiB,eAAA,EAAiB;AACpC,QAAA,MAAM,KAAA;AAAA,MACR;AACA,MAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,QAAA,IAAI,KAAA,CAAM,SAAS,YAAA,EAAc;AAC/B,UAAA,MAAM,IAAI,eAAA,CAAgB,iBAAA,EAAmB,SAAS,CAAA;AAAA,QACxD;AACA,QAAA,MAAM,IAAI,eAAA,CAAgB,KAAA,CAAM,OAAA,EAAS,eAAe,CAAA;AAAA,MAC1D;AACA,MAAA,MAAM,IAAI,eAAA,CAAgB,eAAA,EAAiB,eAAe,CAAA;AAAA,IAC5D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,OAAA,EAAiD;AAC5D,IAAA,OAAO,IAAA,CAAK,OAAA,CAAwB,MAAA,EAAQ,YAAA,EAAc,OAAO,CAAA;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAA,CAAU,IAAA,EAAc,OAAA,EAAgE;AAC5F,IAAA,OAAO,KAAK,MAAA,CAAO,EAAE,IAAA,EAAM,GAAG,SAAS,CAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,OAAA,EAAmD;AAC/D,IAAA,OAAO,IAAA,CAAK,OAAA,CAAyB,MAAA,EAAQ,aAAA,EAAe,OAAO,CAAA;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,CAAO,IAAA,EAAc,OAAA,EAAyD;AAClF,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA,CAAQ,EAAE,IAAA,EAAM,GAAG,SAAS,CAAA;AACxD,IAAA,OAAO,QAAA,CAAS,aAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,OAAA,EAAiE;AACpF,IAAA,OAAO,IAAA,CAAK,OAAA,CAAgC,MAAA,EAAQ,qBAAA,EAAuB,OAAO,CAAA;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAA,CAAa,MAAA,EAAgB,OAAA,EAAoC;AACrE,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,eAAe,EAAE,MAAA,EAAQ,SAAS,CAAA;AAC9D,IAAA,OAAO,QAAA,CAAS,OAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,OAAA,EAAyD;AACxE,IAAA,OAAO,IAAA,CAAK,OAAA,CAA4B,MAAA,EAAQ,iBAAA,EAAmB,OAAO,CAAA;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,IAAA,EAKhB;AAED,IAAA,MAAM,CAAC,YAAA,EAAc,cAAc,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,MACvD,IAAA,CAAK,MAAA,CAAO,EAAE,IAAA,EAAM,CAAA;AAAA,MACpB,IAAA,CAAK,cAAA,CAAe,EAAE,MAAA,EAAQ,MAAM;AAAA,KACrC,CAAA;AAED,IAAA,IAAI,aAAA,GAAgB,IAAA;AACpB,IAAA,IAAI,WAAA,GAAc,KAAA;AAGlB,IAAA,IAAI,YAAA,CAAa,QAAQ,CAAA,EAAG;AAC1B,MAAA,MAAM,gBAAgB,MAAM,IAAA,CAAK,OAAA,CAAQ,EAAE,MAAM,CAAA;AACjD,MAAA,aAAA,GAAgB,aAAA,CAAc,aAAA;AAC9B,MAAA,WAAA,GAAc,IAAA;AAAA,IAChB;AAEA,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,aAAA;AAAA,MACN,WAAA;AAAA,MACA,YAAY,YAAA,CAAa,UAAA;AAAA,MACzB,SAAS,cAAA,CAAe;AAAA,KAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,QAAQ,OAAA,EAA+D;AAC5E,IAAA,OAAO,IAAI,kBAAkB,OAAO,CAAA;AAAA,EACtC;AACF,CAAA;AAKO,IAAM,oBAAN,MAAwB;AAAA,EAK7B,YAAY,MAAA,EAA2C;AACrD,IAAA,IAAA,CAAK,OAAA,GAAU,QAAQ,OAAA,IAAW,gBAAA;AAClC,IAAA,IAAA,CAAK,OAAA,GAAU,QAAQ,OAAA,IAAW,eAAA;AAClC,IAAA,IAAA,CAAK,KAAA,GAAQ,QAAQ,KAAA,IAAS,KAAA;AAAA,EAChC;AAAA,EAEQ,OAAO,IAAA,EAAuB;AACpC,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,GAAA,CAAI,sBAAA,EAAwB,GAAG,IAAI,CAAA;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,MAAc,OAAA,CACZ,MAAA,EACA,QAAA,EACA,IAAA,EACY;AACZ,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,GAAG,QAAQ,CAAA,CAAA;AACtC,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,OAAO,CAAA;AAEnE,IAAA,IAAA,CAAK,IAAI,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,GAAG,IAAI,IAAI,CAAA;AAEjC,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,MAAA;AAAA,QACA,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,YAAA,EAAc;AAAA,SAChB;AAAA,QACA,IAAA,EAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI,KAAA,CAAA;AAAA,QACpC,QAAQ,UAAA,CAAW;AAAA,OACpB,CAAA;AAED,MAAA,YAAA,CAAa,SAAS,CAAA;AACtB,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,IAAI,eAAA;AAAA,UACP,IAAA,CAAK,KAAA,IAAqB,IAAA,CAAK,OAAA,IAAsB,gBAAA;AAAA,UACrD,KAAK,IAAA,IAAmB,WAAA;AAAA,UACzB,QAAA,CAAS;AAAA,SACX;AAAA,MACF;AAEA,MAAA,IAAA,CAAK,GAAA,CAAI,aAAa,IAAI,CAAA;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,YAAA,CAAa,SAAS,CAAA;AACtB,MAAA,IAAI,iBAAiB,eAAA,EAAiB;AACpC,QAAA,MAAM,KAAA;AAAA,MACR;AACA,MAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,QAAA,IAAI,KAAA,CAAM,SAAS,YAAA,EAAc;AAC/B,UAAA,MAAM,IAAI,eAAA,CAAgB,iBAAA,EAAmB,SAAS,CAAA;AAAA,QACxD;AACA,QAAA,MAAM,IAAI,eAAA,CAAgB,KAAA,CAAM,OAAA,EAAS,eAAe,CAAA;AAAA,MAC1D;AACA,MAAA,MAAM,IAAI,eAAA,CAAgB,eAAA,EAAiB,eAAe,CAAA;AAAA,IAC5D;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,OAAA,EAAiD;AAC5D,IAAA,OAAO,IAAA,CAAK,OAAA,CAAwB,MAAA,EAAQ,oBAAA,EAAsB,OAAO,CAAA;AAAA,EAC3E;AAAA,EAEA,MAAM,QAAQ,OAAA,EAAmD;AAC/D,IAAA,OAAO,IAAA,CAAK,OAAA,CAAyB,MAAA,EAAQ,qBAAA,EAAuB,OAAO,CAAA;AAAA,EAC7E;AAAA,EAEA,MAAM,eAAe,OAAA,EAAiE;AACpF,IAAA,OAAO,IAAA,CAAK,OAAA,CAAgC,MAAA,EAAQ,6BAAA,EAA+B,OAAO,CAAA;AAAA,EAC5F;AACF,CAAA;;;AChNO,IAAM,4BAAN,MAAgC;AAAA,EAYrC,YAAY,MAAA,EAAkC;AAT9C,IAAA,IAAA,CAAQ,aAA6B,EAAC;AACtC,IAAA,IAAA,CAAQ,UAAoB,EAAC;AAE7B,IAAA,IAAA,CAAA,IAAA,GAAO,2BAAA;AACP,IAAA,IAAA,CAAA,SAAA,GAAY,KAAA;AACZ,IAAA,IAAA,CAAA,WAAA,GAAc,KAAA;AACd,IAAA,IAAA,CAAA,WAAA,GAAc,KAAA;AACd,IAAA,IAAA,CAAA,eAAA,GAAkB,KAAA;AAGhB,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,UAAA,CAAW;AAAA,MAC3B,QAAQ,MAAA,CAAO,MAAA;AAAA,MACf,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,OAAO,MAAA,CAAO;AAAA,KACf,CAAA;AACD,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAA,CACJ,GAAA,EACA,OAAA,EACA,KAAA,EACe;AACf,IAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAE5B,MAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO;AAAA,QAC5C,IAAA,EAAM,MAAA;AAAA,QACN,KAAA,EAAO,KAAK,MAAA,CAAO;AAAA,OACpB,CAAA;AAED,MAAA,IAAI,YAAA,CAAa,UAAA,CAAW,MAAA,GAAS,CAAA,EAAG;AACtC,QAAA,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,GAAG,YAAA,CAAa,UAAU,CAAA;AAC/C,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,CAAA,qCAAA,EAAwC,YAAA,CAAa,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SAC/F;AAEA,QAAA,IAAI,IAAA,CAAK,OAAO,UAAA,EAAY;AAC1B,UAAA,MAAM,IAAI,eAAA,CAAgB,YAAA,CAAa,UAAU,CAAA;AAAA,QACnD;AAAA,MACF;AAGA,MAAA,MAAM,iBAAiB,MAAM,IAAA,CAAK,OAAO,cAAA,CAAe,EAAE,QAAQ,CAAA;AAElE,MAAA,IAAI,cAAA,CAAe,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACrC,QAAA,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,GAAG,cAAA,CAAe,OAAO,CAAA;AAC3C,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,CAAA,yCAAA,EAA4C,cAAA,CAAe,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SAClG;AAEA,QAAA,IAAI,IAAA,CAAK,MAAA,CAAO,aAAA,KAAkB,KAAA,EAAO;AACvC,UAAA,MAAM,IAAI,kBAAA,CAAmB,cAAA,CAAe,OAAO,CAAA;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAA,CACJ,GAAA,EACA,QAAA,EACA,KAAA,EACe;AACf,IAAA,KAAA,MAAW,gBAAgB,QAAA,EAAU;AACnC,MAAA,KAAA,MAAW,WAAW,YAAA,EAAc;AAClC,QAAA,IAAI,OAAO,OAAA,CAAQ,OAAA,KAAY,QAAA,EAAU;AACvC,UAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO;AAAA,YAC5C,MAAM,OAAA,CAAQ,OAAA;AAAA,YACd,KAAA,EAAO,KAAK,MAAA,CAAO;AAAA,WACpB,CAAA;AAED,UAAA,IAAI,YAAA,CAAa,UAAA,CAAW,MAAA,GAAS,CAAA,EAAG;AACtC,YAAA,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,GAAG,YAAA,CAAa,UAAU,CAAA;AAE/C,YAAA,IAAI,IAAA,CAAK,OAAO,UAAA,EAAY;AAC1B,cAAA,MAAM,IAAI,eAAA,CAAgB,YAAA,CAAa,UAAU,CAAA;AAAA,YACnD;AAAA,UACF;AAEA,UAAA,MAAM,cAAA,GAAiB,MAAM,IAAA,CAAK,MAAA,CAAO,cAAA,CAAe;AAAA,YACtD,QAAQ,OAAA,CAAQ;AAAA,WACjB,CAAA;AAED,UAAA,IAAI,cAAA,CAAe,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACrC,YAAA,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,GAAG,cAAA,CAAe,OAAO,CAAA;AAE3C,YAAA,IAAI,IAAA,CAAK,MAAA,CAAO,aAAA,KAAkB,KAAA,EAAO;AACvC,cAAA,MAAM,IAAI,kBAAA,CAAmB,cAAA,CAAe,OAAO,CAAA;AAAA,YACrD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAA,CAAa,MAAA,EAAmB,KAAA,EAA8B;AAClE,IAAA,KAAA,MAAW,UAAA,IAAc,MAAA,CAAO,WAAA,CAAY,IAAA,EAAK,EAAG;AAClD,MAAA,IAAI,WAAW,IAAA,EAAM;AACnB,QAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,MAAA,CAAO,WAAW,EAAE,MAAA,EAAQ,UAAA,CAAW,IAAA,EAAM,CAAA;AAE3E,QAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACvB,UAAA,OAAA,CAAQ,IAAA;AAAA,YACN,CAAA,4CAAA,EAA+C,UAAA,CAAW,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,WAChG;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAA,GAAgC;AAC9B,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,UAAU,CAAA;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,GAAuB;AACrB,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,OAAO,CAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAA,GAAqB;AACnB,IAAA,IAAA,CAAK,aAAa,EAAC;AACnB,IAAA,IAAA,CAAK,UAAU,EAAC;AAAA,EAClB;AACF;AAcO,SAAS,wBAAwB,MAAA,EAA+C;AACrF,EAAA,MAAM,MAAA,GAAS,IAAI,UAAA,CAAW;AAAA,IAC5B,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,OAAO,MAAA,CAAO;AAAA,GACf,CAAA;AAED,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,2BAAA;AAAA,IACN,WAAA,EACE,kNAAA;AAAA,IAGF,IAAA,EAAM,OAAO,KAAA,KAAmC;AAC9C,MAAA,MAAM,SAAS,MAAM,MAAA,CAAO,QAAQ,EAAE,IAAA,EAAM,OAAO,CAAA;AACnD,MAAA,OAAO,MAAA,CAAO,aAAA;AAAA,IAChB;AAAA,GACF;AACF;AAKO,SAAS,uBAAuB,MAAA,EAA+C;AACpF,EAAA,MAAM,MAAA,GAAS,IAAI,UAAA,CAAW;AAAA,IAC5B,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,OAAO,MAAA,CAAO;AAAA,GACf,CAAA;AAED,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,0BAAA;AAAA,IACN,WAAA,EACE,mOAAA;AAAA,IAGF,IAAA,EAAM,OAAO,KAAA,KAAmC;AAC9C,MAAA,MAAM,SAAS,MAAM,MAAA,CAAO,OAAO,EAAE,IAAA,EAAM,OAAO,CAAA;AAClD,MAAA,IAAI,MAAA,CAAO,UAAU,CAAA,EAAG;AACtB,QAAA,OAAO,kBAAA;AAAA,MACT;AACA,MAAA,OACE,CAAA,MAAA,EAAS,OAAO,KAAK,CAAA;AAAA,CAAA,GACrB,OAAO,UAAA,CACJ,GAAA,CAAI,CAAC,CAAA,KAAM,KAAK,CAAA,CAAE,IAAI,CAAA,GAAA,EAAM,CAAA,CAAE,KAAK,CAAA,eAAA,EAAkB,CAAA,CAAE,UAAU,CAAA,CAAA,CAAG,CAAA,CACpE,KAAK,IAAI,CAAA;AAAA,IAEhB;AAAA,GACF;AACF;AAKO,SAAS,2BAA2B,MAAA,EAA+C;AACxF,EAAA,MAAM,MAAA,GAAS,IAAI,UAAA,CAAW;AAAA,IAC5B,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,OAAO,MAAA,CAAO;AAAA,GACf,CAAA;AAED,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,8BAAA;AAAA,IACN,WAAA,EACE,gOAAA;AAAA,IAGF,IAAA,EAAM,OAAO,KAAA,KAAmC;AAC9C,MAAA,MAAM,SAAS,MAAM,MAAA,CAAO,eAAe,EAAE,MAAA,EAAQ,OAAO,CAAA;AAC5D,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,OAAO,iBAAA;AAAA,MACT;AACA,MAAA,OACE,CAAA,WAAA,EAAc,OAAO,gBAAgB,CAAA;AAAA,CAAA,GACrC,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,EAAA,EAAK,EAAE,IAAI,CAAA,EAAA,EAAK,CAAA,CAAE,QAAQ,MAAM,CAAA,CAAE,WAAW,EAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,GACpF;AAAA,gBAAA,EAAqB,OAAO,cAAc,CAAA,CAAA;AAAA,IAE9C;AAAA,GACF;AACF;AAKO,SAAS,mBAAmB,MAAA,EAAiD;AAClF,EAAA,OAAO;AAAA,IACL,wBAAwB,MAAM,CAAA;AAAA,IAC9B,uBAAuB,MAAM,CAAA;AAAA,IAC7B,2BAA2B,MAAM;AAAA,GACnC;AACF;AAEA,IAAO,iBAAA,GAAQ","file":"langchain.js","sourcesContent":["/**\n * SafeKeyLab SDK Types\n */\n\nexport interface SafeKeyLabConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL for the API (default: https://api.safekeylab.com) */\n baseUrl?: string;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\nexport interface PIIDetection {\n /** Type of PII detected (EMAIL, PHONE, SSN, etc.) */\n type: string;\n /** The detected value */\n value: string;\n /** Start position in text */\n start: number;\n /** End position in text */\n end: number;\n /** Confidence score (0-1) */\n confidence: number;\n}\n\nexport interface DetectRequest {\n /** Text to scan for PII */\n text: string;\n /** Types of PII to detect (default: all) */\n types?: string[];\n /** Minimum confidence threshold (default: 0.5) */\n minConfidence?: number;\n}\n\nexport interface DetectResponse {\n success: boolean;\n detections: PIIDetection[];\n count: number;\n processing_time_ms: number;\n}\n\nexport interface ProtectRequest {\n /** Text to redact PII from */\n text: string;\n /** Types of PII to redact (default: all) */\n types?: string[];\n /** Redaction style: 'placeholder' | 'mask' | 'hash' (default: placeholder) */\n style?: 'placeholder' | 'mask' | 'hash';\n}\n\nexport interface Redaction {\n type: string;\n original: string;\n start: number;\n end: number;\n}\n\nexport interface ProtectResponse {\n success: boolean;\n redacted_text: string;\n redactions: Redaction[];\n redactions_count: number;\n original_length: number;\n redacted_length: number;\n processing_time_ms: number;\n}\n\nexport interface ValidatePromptRequest {\n /** Prompt to validate */\n prompt: string;\n /** Additional context for validation */\n context?: string;\n}\n\nexport interface Threat {\n type: string;\n severity: 'low' | 'medium' | 'high' | 'critical';\n description: string;\n matched?: string;\n}\n\nexport interface ValidatePromptResponse {\n success: boolean;\n is_safe: boolean;\n threats: Threat[];\n threats_detected: number;\n recommendation: 'allow' | 'review' | 'block';\n processing_time_ms: number;\n}\n\nexport interface ScanOutputRequest {\n /** LLM output to scan */\n output: string;\n /** Original prompt for context */\n prompt?: string;\n}\n\nexport interface ScanOutputResponse {\n success: boolean;\n is_safe: boolean;\n issues: Array<{\n type: string;\n severity: string;\n description: string;\n }>;\n processing_time_ms: number;\n}\n\nexport interface WrapperConfig {\n /** Block requests containing PII (default: false, will redact instead) */\n blockOnPII?: boolean;\n /** Block requests with detected threats (default: true) */\n blockOnThreat?: boolean;\n /** Types of PII to detect/redact */\n piiTypes?: string[];\n /** Scan LLM outputs for issues (default: true) */\n scanOutputs?: boolean;\n /** Custom error handler */\n onError?: (error: Error) => void;\n /** Callback when PII is detected */\n onPIIDetected?: (detections: PIIDetection[]) => void;\n /** Callback when threat is detected */\n onThreatDetected?: (threats: Threat[]) => void;\n}\n\nexport class SafeKeyLabError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number\n ) {\n super(message);\n this.name = 'SafeKeyLabError';\n }\n}\n\nexport class PIIBlockedError extends SafeKeyLabError {\n constructor(\n public detections: PIIDetection[]\n ) {\n super(\n `Request blocked: PII detected (${detections.map(d => d.type).join(', ')})`,\n 'PII_BLOCKED'\n );\n this.name = 'PIIBlockedError';\n }\n}\n\nexport class ThreatBlockedError extends SafeKeyLabError {\n constructor(\n public threats: Threat[]\n ) {\n super(\n `Request blocked: Threat detected (${threats.map(t => t.type).join(', ')})`,\n 'THREAT_BLOCKED'\n );\n this.name = 'ThreatBlockedError';\n }\n}\n","/**\n * SafeKeyLab API Client\n */\n\nimport {\n SafeKeyLabConfig,\n DetectRequest,\n DetectResponse,\n ProtectRequest,\n ProtectResponse,\n ValidatePromptRequest,\n ValidatePromptResponse,\n ScanOutputRequest,\n ScanOutputResponse,\n SafeKeyLabError,\n} from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.safekeylab.com';\nconst DEFAULT_TIMEOUT = 30000;\n\nexport class SafeKeyLab {\n private apiKey: string;\n private baseUrl: string;\n private timeout: number;\n private debug: boolean;\n\n constructor(config: SafeKeyLabConfig) {\n if (!config.apiKey) {\n throw new SafeKeyLabError('API key is required', 'MISSING_API_KEY');\n }\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n this.debug = config.debug || false;\n }\n\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log('[SafeKeyLab]', ...args);\n }\n }\n\n private async request<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n this.log(`${method} ${url}`, body);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n 'User-Agent': '@safekeylab/sdk/1.0.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const data = await response.json() as Record<string, unknown>;\n\n if (!response.ok) {\n throw new SafeKeyLabError(\n (data.error as string) || (data.message as string) || 'Request failed',\n (data.code as string) || 'API_ERROR',\n response.status\n );\n }\n\n this.log('Response:', data);\n return data as T;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof SafeKeyLabError) {\n throw error;\n }\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new SafeKeyLabError('Request timeout', 'TIMEOUT');\n }\n throw new SafeKeyLabError(error.message, 'NETWORK_ERROR');\n }\n throw new SafeKeyLabError('Unknown error', 'UNKNOWN_ERROR');\n }\n }\n\n /**\n * Detect PII in text\n */\n async detect(request: DetectRequest): Promise<DetectResponse> {\n return this.request<DetectResponse>('POST', '/v1/detect', request);\n }\n\n /**\n * Detect PII in text (convenience method)\n */\n async detectPII(text: string, options?: Omit<DetectRequest, 'text'>): Promise<DetectResponse> {\n return this.detect({ text, ...options });\n }\n\n /**\n * Redact/protect PII in text\n */\n async protect(request: ProtectRequest): Promise<ProtectResponse> {\n return this.request<ProtectResponse>('POST', '/v1/protect', request);\n }\n\n /**\n * Redact PII from text (convenience method)\n */\n async redact(text: string, options?: Omit<ProtectRequest, 'text'>): Promise<string> {\n const response = await this.protect({ text, ...options });\n return response.redacted_text;\n }\n\n /**\n * Validate a prompt for security threats\n */\n async validatePrompt(request: ValidatePromptRequest): Promise<ValidatePromptResponse> {\n return this.request<ValidatePromptResponse>('POST', '/v1/validate-prompt', request);\n }\n\n /**\n * Check if a prompt is safe (convenience method)\n */\n async isPromptSafe(prompt: string, context?: string): Promise<boolean> {\n const response = await this.validatePrompt({ prompt, context });\n return response.is_safe;\n }\n\n /**\n * Scan LLM output for issues\n */\n async scanOutput(request: ScanOutputRequest): Promise<ScanOutputResponse> {\n return this.request<ScanOutputResponse>('POST', '/v1/scan-output', request);\n }\n\n /**\n * Full protection pipeline: validate input, redact PII, return protected text\n */\n async protectInput(text: string): Promise<{\n text: string;\n wasModified: boolean;\n detections: DetectResponse['detections'];\n threats: ValidatePromptResponse['threats'];\n }> {\n // Run detection and validation in parallel\n const [detectResult, validateResult] = await Promise.all([\n this.detect({ text }),\n this.validatePrompt({ prompt: text }),\n ]);\n\n let protectedText = text;\n let wasModified = false;\n\n // Redact if PII found\n if (detectResult.count > 0) {\n const protectResult = await this.protect({ text });\n protectedText = protectResult.redacted_text;\n wasModified = true;\n }\n\n return {\n text: protectedText,\n wasModified,\n detections: detectResult.detections,\n threats: validateResult.threats,\n };\n }\n\n /**\n * Create a sandbox client (no API key required, rate limited)\n */\n static sandbox(options?: Omit<SafeKeyLabConfig, 'apiKey'>): SafeKeyLabSandbox {\n return new SafeKeyLabSandbox(options);\n }\n}\n\n/**\n * Sandbox client for testing (no API key required)\n */\nexport class SafeKeyLabSandbox {\n private baseUrl: string;\n private timeout: number;\n private debug: boolean;\n\n constructor(config?: Omit<SafeKeyLabConfig, 'apiKey'>) {\n this.baseUrl = config?.baseUrl || DEFAULT_BASE_URL;\n this.timeout = config?.timeout || DEFAULT_TIMEOUT;\n this.debug = config?.debug || false;\n }\n\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log('[SafeKeyLab:Sandbox]', ...args);\n }\n }\n\n private async request<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n this.log(`${method} ${url}`, body);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': '@safekeylab/sdk/1.0.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n const data = await response.json() as Record<string, unknown>;\n\n if (!response.ok) {\n throw new SafeKeyLabError(\n (data.error as string) || (data.message as string) || 'Request failed',\n (data.code as string) || 'API_ERROR',\n response.status\n );\n }\n\n this.log('Response:', data);\n return data as T;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof SafeKeyLabError) {\n throw error;\n }\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new SafeKeyLabError('Request timeout', 'TIMEOUT');\n }\n throw new SafeKeyLabError(error.message, 'NETWORK_ERROR');\n }\n throw new SafeKeyLabError('Unknown error', 'UNKNOWN_ERROR');\n }\n }\n\n async detect(request: DetectRequest): Promise<DetectResponse> {\n return this.request<DetectResponse>('POST', '/sandbox/v1/detect', request);\n }\n\n async protect(request: ProtectRequest): Promise<ProtectResponse> {\n return this.request<ProtectResponse>('POST', '/sandbox/v1/protect', request);\n }\n\n async validatePrompt(request: ValidatePromptRequest): Promise<ValidatePromptResponse> {\n return this.request<ValidatePromptResponse>('POST', '/sandbox/v1/validate-prompt', request);\n }\n}\n\nexport default SafeKeyLab;\n","/**\n * SafeKeyLab LangChain Integration\n *\n * Provides callbacks and tools for LangChain/LangChain.js\n *\n * @example\n * ```typescript\n * import { ChatOpenAI } from '@langchain/openai';\n * import { SafeKeyLabCallbackHandler, createPIIProtectionTool } from '@safekeylab/sdk/langchain';\n *\n * const handler = new SafeKeyLabCallbackHandler({ apiKey: 'sk_live_...' });\n * const model = new ChatOpenAI({ callbacks: [handler] });\n *\n * // Or use tools in an agent\n * const tools = [createPIIProtectionTool({ apiKey: 'sk_live_...' })];\n * ```\n */\n\nimport { SafeKeyLab } from '../client';\nimport {\n SafeKeyLabConfig,\n PIIDetection,\n Threat,\n PIIBlockedError,\n ThreatBlockedError,\n} from '../types';\n\n// Minimal LangChain types to avoid hard dependency\ninterface BaseCallbackHandlerInput {\n ignoreLLM?: boolean;\n ignoreChain?: boolean;\n ignoreAgent?: boolean;\n}\n\ninterface Serialized {\n [key: string]: unknown;\n}\n\ninterface LLMResult {\n generations: Array<Array<{ text: string; [key: string]: unknown }>>;\n [key: string]: unknown;\n}\n\ninterface ChainValues {\n [key: string]: unknown;\n}\n\nexport interface SafeKeyLabCallbackConfig extends Partial<SafeKeyLabConfig> {\n /** Block on PII detection (default: false, will log instead) */\n blockOnPII?: boolean;\n /** Block on threat detection (default: true) */\n blockOnThreat?: boolean;\n /** Types of PII to detect */\n piiTypes?: string[];\n}\n\n/**\n * LangChain callback handler for SafeKeyLab protection\n */\nexport class SafeKeyLabCallbackHandler {\n private client: SafeKeyLab;\n private config: SafeKeyLabCallbackConfig;\n private detections: PIIDetection[] = [];\n private threats: Threat[] = [];\n\n name = 'SafeKeyLabCallbackHandler';\n ignoreLLM = false;\n ignoreChain = false;\n ignoreAgent = false;\n ignoreRetriever = false;\n\n constructor(config: SafeKeyLabCallbackConfig) {\n this.client = new SafeKeyLab({\n apiKey: config.apiKey!,\n baseUrl: config.baseUrl,\n timeout: config.timeout,\n debug: config.debug,\n });\n this.config = config;\n }\n\n /**\n * Called when LLM starts processing\n */\n async handleLLMStart(\n llm: Serialized,\n prompts: string[],\n runId: string\n ): Promise<void> {\n for (const prompt of prompts) {\n // Check for PII\n const detectResult = await this.client.detect({\n text: prompt,\n types: this.config.piiTypes,\n });\n\n if (detectResult.detections.length > 0) {\n this.detections.push(...detectResult.detections);\n console.warn(\n `[SafeKeyLab] PII detected in prompt: ${detectResult.detections.map((d) => d.type).join(', ')}`\n );\n\n if (this.config.blockOnPII) {\n throw new PIIBlockedError(detectResult.detections);\n }\n }\n\n // Check for threats\n const validateResult = await this.client.validatePrompt({ prompt });\n\n if (validateResult.threats.length > 0) {\n this.threats.push(...validateResult.threats);\n console.warn(\n `[SafeKeyLab] Threats detected in prompt: ${validateResult.threats.map((t) => t.type).join(', ')}`\n );\n\n if (this.config.blockOnThreat !== false) {\n throw new ThreatBlockedError(validateResult.threats);\n }\n }\n }\n }\n\n /**\n * Called when chat model starts\n */\n async handleChatModelStart(\n llm: Serialized,\n messages: Array<Array<{ content: string; [key: string]: unknown }>>,\n runId: string\n ): Promise<void> {\n for (const messageGroup of messages) {\n for (const message of messageGroup) {\n if (typeof message.content === 'string') {\n const detectResult = await this.client.detect({\n text: message.content,\n types: this.config.piiTypes,\n });\n\n if (detectResult.detections.length > 0) {\n this.detections.push(...detectResult.detections);\n\n if (this.config.blockOnPII) {\n throw new PIIBlockedError(detectResult.detections);\n }\n }\n\n const validateResult = await this.client.validatePrompt({\n prompt: message.content,\n });\n\n if (validateResult.threats.length > 0) {\n this.threats.push(...validateResult.threats);\n\n if (this.config.blockOnThreat !== false) {\n throw new ThreatBlockedError(validateResult.threats);\n }\n }\n }\n }\n }\n }\n\n /**\n * Called when LLM ends\n */\n async handleLLMEnd(output: LLMResult, runId: string): Promise<void> {\n for (const generation of output.generations.flat()) {\n if (generation.text) {\n const scanResult = await this.client.scanOutput({ output: generation.text });\n\n if (!scanResult.is_safe) {\n console.warn(\n `[SafeKeyLab] Issues detected in LLM output: ${scanResult.issues.map((i) => i.type).join(', ')}`\n );\n }\n }\n }\n }\n\n /**\n * Get all detections from this session\n */\n getDetections(): PIIDetection[] {\n return [...this.detections];\n }\n\n /**\n * Get all threats from this session\n */\n getThreats(): Threat[] {\n return [...this.threats];\n }\n\n /**\n * Clear detection history\n */\n clearHistory(): void {\n this.detections = [];\n this.threats = [];\n }\n}\n\n/**\n * LangChain tool interface\n */\ninterface ToolConfig {\n name: string;\n description: string;\n func: (input: string) => Promise<string>;\n}\n\n/**\n * Create a PII protection tool for LangChain agents\n */\nexport function createPIIProtectionTool(config: Partial<SafeKeyLabConfig>): ToolConfig {\n const client = new SafeKeyLab({\n apiKey: config.apiKey!,\n baseUrl: config.baseUrl,\n timeout: config.timeout,\n debug: config.debug,\n });\n\n return {\n name: 'safekeylab_pii_protection',\n description:\n 'Detects and redacts PII (personally identifiable information) from text. ' +\n 'Use this tool to protect sensitive data before processing or storing. ' +\n 'Input should be the text to protect. Output is the redacted text.',\n func: async (input: string): Promise<string> => {\n const result = await client.protect({ text: input });\n return result.redacted_text;\n },\n };\n}\n\n/**\n * Create a PII detection tool for LangChain agents\n */\nexport function createPIIDetectionTool(config: Partial<SafeKeyLabConfig>): ToolConfig {\n const client = new SafeKeyLab({\n apiKey: config.apiKey!,\n baseUrl: config.baseUrl,\n timeout: config.timeout,\n debug: config.debug,\n });\n\n return {\n name: 'safekeylab_pii_detection',\n description:\n 'Detects PII (personally identifiable information) in text without modifying it. ' +\n 'Use this tool to identify what sensitive data is present. ' +\n 'Input should be the text to scan. Output is a list of detected PII types and locations.',\n func: async (input: string): Promise<string> => {\n const result = await client.detect({ text: input });\n if (result.count === 0) {\n return 'No PII detected.';\n }\n return (\n `Found ${result.count} PII items:\\n` +\n result.detections\n .map((d) => `- ${d.type}: \"${d.value}\" (confidence: ${d.confidence})`)\n .join('\\n')\n );\n },\n };\n}\n\n/**\n * Create a prompt validation tool for LangChain agents\n */\nexport function createPromptValidationTool(config: Partial<SafeKeyLabConfig>): ToolConfig {\n const client = new SafeKeyLab({\n apiKey: config.apiKey!,\n baseUrl: config.baseUrl,\n timeout: config.timeout,\n debug: config.debug,\n });\n\n return {\n name: 'safekeylab_prompt_validation',\n description:\n 'Validates a prompt for security threats like injection attacks. ' +\n 'Use this tool to check if user input is safe before using it in an LLM prompt. ' +\n 'Input should be the prompt to validate. Output indicates if the prompt is safe.',\n func: async (input: string): Promise<string> => {\n const result = await client.validatePrompt({ prompt: input });\n if (result.is_safe) {\n return 'Prompt is safe.';\n }\n return (\n `Prompt has ${result.threats_detected} threats:\\n` +\n result.threats.map((t) => `- ${t.type} (${t.severity}): ${t.description}`).join('\\n') +\n `\\nRecommendation: ${result.recommendation}`\n );\n },\n };\n}\n\n/**\n * Get all SafeKeyLab tools for use in LangChain agents\n */\nexport function getSafeKeyLabTools(config: Partial<SafeKeyLabConfig>): ToolConfig[] {\n return [\n createPIIProtectionTool(config),\n createPIIDetectionTool(config),\n createPromptValidationTool(config),\n ];\n}\n\nexport default SafeKeyLabCallbackHandler;\n"]}
|