@seclai/sdk 1.0.6 → 1.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Seclai JavaScript SDK
2
2
 
3
- This is the official Seclai JavaScript SDK with TypeScript typings.
3
+ The official JavaScript/TypeScript SDK for the [Seclai](https://seclai.com) API. Provides full typed coverage of all API endpoints, file uploads, SSE streaming, polling helpers, and automatic pagination.
4
+
5
+ Works in Node.js 18+, Deno, Bun, Cloudflare Workers, and any runtime with a `fetch` implementation.
4
6
 
5
7
  ## Install
6
8
 
@@ -8,115 +10,467 @@ This is the official Seclai JavaScript SDK with TypeScript typings.
8
10
  npm install @seclai/sdk
9
11
  ```
10
12
 
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import { Seclai } from "@seclai/sdk";
17
+
18
+ const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
19
+
20
+ // List all sources
21
+ const sources = await client.listSources();
22
+
23
+ // Run an agent and stream the result
24
+ const result = await client.runStreamingAgentAndWait(
25
+ "agent_id",
26
+ { input: "Summarize the latest uploads", metadata: {} },
27
+ { timeoutMs: 120_000 },
28
+ );
29
+ console.log(result);
30
+ ```
31
+
32
+ ## Configuration
33
+
34
+ | Option | Environment variable | Default |
35
+ | --- | --- | --- |
36
+ | `apiKey` | `SECLAI_API_KEY` | *required* |
37
+ | `baseUrl` | `SECLAI_API_URL` | `https://api.seclai.com` |
38
+ | `apiKeyHeader` | — | `x-api-key` |
39
+ | `defaultHeaders` | — | `{}` |
40
+ | `fetch` | — | `globalThis.fetch` |
41
+
42
+ ```ts
43
+ const client = new Seclai({
44
+ apiKey: "sk-...",
45
+ baseUrl: "https://staging-api.seclai.com",
46
+ defaultHeaders: { "X-Custom": "value" },
47
+ });
48
+ ```
49
+
11
50
  ## API documentation
12
51
 
13
52
  Online API documentation (latest):
14
53
 
15
- https://seclai.github.io/seclai-javascript/1.0.6/
54
+ https://seclai.github.io/seclai-javascript/1.1.0/
55
+
56
+ ## Resources
16
57
 
17
- ## Usage
58
+ ### Agents
18
59
 
19
60
  ```ts
20
- import { Seclai } from "@seclai/sdk";
61
+ // CRUD
62
+ const agents = await client.listAgents({ page: 1, limit: 20 });
63
+ const agent = await client.createAgent({ name: "My Agent", description: "..." });
64
+ const fetched = await client.getAgent("agent_id");
65
+ const updated = await client.updateAgent("agent_id", { name: "Renamed" });
66
+ await client.deleteAgent("agent_id");
67
+
68
+ // Definition (step workflow)
69
+ const def = await client.getAgentDefinition("agent_id");
70
+ await client.updateAgentDefinition("agent_id", { steps: [...], change_id: def.change_id });
71
+ ```
21
72
 
22
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
73
+ ### Agent runs
23
74
 
24
- const sources = await client.listSources();
25
- console.log(sources.pagination, sources.data);
75
+ ```ts
76
+ // Start a run
77
+ const run = await client.runAgent("agent_id", { input: "Hello" });
78
+
79
+ // List & search runs
80
+ const runs = await client.listAgentRuns("agent_id", { status: "completed" });
81
+ const search = await client.searchAgentRuns({ agent_id: "...", status: ["completed"] });
82
+
83
+ // Fetch run details (optionally with step outputs)
84
+ const detail = await client.getAgentRun("run_id", { includeStepOutputs: true });
85
+
86
+ // Cancel or delete
87
+ await client.cancelAgentRun("run_id");
88
+ await client.deleteAgentRun("run_id");
26
89
  ```
27
90
 
28
- ### Run an agent with SSE streaming (wait for final result)
91
+ ### Streaming
29
92
 
30
- Use the SSE streaming endpoint and block until the final `done` event is received.
93
+ The SDK provides two streaming patterns over the SSE `/runs/stream` endpoint:
31
94
 
32
- If the stream ends before `done` or the timeout is reached, this method throws.
95
+ **Block until done** returns the final `done` payload or throws on timeout:
33
96
 
34
97
  ```ts
35
- import { Seclai } from "@seclai/sdk";
98
+ const result = await client.runStreamingAgentAndWait(
99
+ "agent_id",
100
+ { input: "Hello", metadata: {} },
101
+ { timeoutMs: 60_000 },
102
+ );
103
+ ```
36
104
 
37
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
105
+ **Async iterator** yields every SSE event as `{ event, data }`:
106
+
107
+ ```ts
108
+ for await (const event of client.runStreamingAgent(
109
+ "agent_id",
110
+ { input: "Hello" },
111
+ { timeoutMs: 120_000 },
112
+ )) {
113
+ console.log(event.event, event.data);
114
+ if (event.event === "done") break;
115
+ }
116
+ ```
117
+
118
+ ### Polling
38
119
 
39
- const run = await client.runStreamingAgentAndWait(
120
+ For environments where SSE is not practical, poll for a completed run:
121
+
122
+ ```ts
123
+ const result = await client.runAgentAndPoll(
40
124
  "agent_id",
41
- {
42
- input: "Hello from streaming",
43
- metadata: { app: "My App" },
44
- },
45
- { timeoutMs: 60_000 }
125
+ { input: "Hello" },
126
+ { pollIntervalMs: 2_000, timeoutMs: 120_000 },
46
127
  );
128
+ ```
129
+
130
+ ### Agent input uploads
47
131
 
48
- console.log(run);
132
+ ```ts
133
+ const upload = await client.uploadAgentInput("agent_id", {
134
+ file: new Uint8Array([...]),
135
+ fileName: "input.pdf",
136
+ });
137
+ const status = await client.getAgentInputUploadStatus("agent_id", upload.upload_id);
49
138
  ```
50
139
 
51
- ### Get agent run details
140
+ ### Agent AI assistant
52
141
 
53
- Fetch details for a specific agent run, optionally including per-step outputs:
142
+ ```ts
143
+ const steps = await client.generateAgentSteps("agent_id", { user_input: "Build a RAG pipeline" });
144
+ const config = await client.generateStepConfig("agent_id", { step_type: "llm", user_input: "..." });
145
+
146
+ // Conversation history
147
+ const history = await client.getAgentAiConversationHistory("agent_id");
148
+ await client.markAgentAiSuggestion("agent_id", "conversation_id", { accepted: true });
149
+ ```
150
+
151
+ ### Agent evaluations
54
152
 
55
153
  ```ts
56
- import { Seclai } from "@seclai/sdk";
154
+ const criteria = await client.listEvaluationCriteria("agent_id");
155
+ const created = await client.createEvaluationCriteria("agent_id", { name: "Accuracy", ... });
156
+ const detail = await client.getEvaluationCriteria("criteria_id");
157
+ await client.updateEvaluationCriteria("criteria_id", { ... });
158
+ await client.deleteEvaluationCriteria("criteria_id");
57
159
 
58
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
160
+ // Test a draft
161
+ await client.testDraftEvaluation("agent_id", { criteria: { ... }, run_id: "..." });
162
+
163
+ // Results by criteria
164
+ const results = await client.listEvaluationResults("criteria_id");
165
+ const summary = await client.getEvaluationCriteriaSummary("criteria_id");
166
+ await client.createEvaluationResult("criteria_id", { ... });
167
+
168
+ // Results by run
169
+ const runResults = await client.listRunEvaluationResults("agent_id", "run_id");
170
+
171
+ // Non-manual evaluation summary
172
+ const nonManual = await client.getNonManualEvaluationSummary("agent_id");
59
173
 
60
- // Basic details
61
- const run = await client.getAgentRun("run_id");
62
- console.log(run);
174
+ // Compatible runs for a criteria
175
+ const compatible = await client.listCompatibleRuns("criteria_id");
176
+ ```
177
+
178
+ ### Knowledge bases
179
+
180
+ ```ts
181
+ const kbs = await client.listKnowledgeBases();
182
+ const kb = await client.createKnowledgeBase({ name: "Docs KB" });
183
+ const fetched = await client.getKnowledgeBase("kb_id");
184
+ await client.updateKnowledgeBase("kb_id", { name: "Renamed KB" });
185
+ await client.deleteKnowledgeBase("kb_id");
186
+ ```
187
+
188
+ ### Memory banks
189
+
190
+ ```ts
191
+ const banks = await client.listMemoryBanks();
192
+ const bank = await client.createMemoryBank({ name: "Chat Memory", type: "conversation" });
193
+ const fetched = await client.getMemoryBank("mb_id");
194
+ await client.updateMemoryBank("mb_id", { name: "Renamed" });
195
+ await client.deleteMemoryBank("mb_id");
196
+
197
+ // Stats & compaction
198
+ const stats = await client.getMemoryBankStats("mb_id");
199
+ await client.compactMemoryBank("mb_id");
200
+
201
+ // Test compaction
202
+ const test = await client.testMemoryBankCompaction("mb_id", { ... });
203
+ const standalone = await client.testCompactionPromptStandalone({ ... });
204
+
205
+ // Templates & agents
206
+ const templates = await client.listMemoryBankTemplates();
207
+ const agents = await client.getAgentsUsingMemoryBank("mb_id");
208
+
209
+ // AI assistant
210
+ const suggestion = await client.generateMemoryBankConfig({ user_input: "..." });
211
+ const history = await client.getMemoryBankAiLastConversation();
212
+ await client.acceptMemoryBankAiSuggestion("conv_id", { ... });
213
+
214
+ // Source management
215
+ await client.deleteMemoryBankSource("mb_id");
216
+ ```
217
+
218
+ ### Sources
219
+
220
+ ```ts
221
+ const sources = await client.listSources({ page: 1, limit: 20, order: "asc" });
222
+ const source = await client.createSource({ name: "My Source", ... });
223
+ const fetched = await client.getSource("source_id");
224
+ await client.updateSource("source_id", { name: "Renamed" });
225
+ await client.deleteSource("source_id");
226
+ ```
227
+
228
+ ### File uploads
229
+
230
+ Upload a file to a source (max 200 MiB). The SDK infers MIME type from the file extension when `mimeType` is not provided.
231
+
232
+ ```ts
233
+ import { readFile } from "node:fs/promises";
63
234
 
64
- // Include per-step outputs with timing, durations, and credits
65
- const runWithSteps = await client.getAgentRun("run_id", {
66
- includeStepOutputs: true,
235
+ await client.uploadFileToSource("source_id", {
236
+ file: await readFile("document.pdf"),
237
+ fileName: "document.pdf",
238
+ title: "Q4 Report",
239
+ metadata: { department: "finance" },
67
240
  });
68
- console.log(runWithSteps);
69
241
  ```
70
242
 
71
- ### Upload a file
243
+ Upload inline text:
72
244
 
73
- **Max file size:** 200 MiB.
245
+ ```ts
246
+ await client.uploadInlineTextToSource("source_id", {
247
+ text: "Hello, world!",
248
+ title: "Greeting",
249
+ });
250
+ ```
74
251
 
75
- **Supported MIME types:**
76
- - `application/epub+zip`
77
- - `application/json`
78
- - `application/msword`
79
- - `application/pdf`
80
- - `application/vnd.ms-excel`
81
- - `application/vnd.ms-outlook`
82
- - `application/vnd.ms-powerpoint`
83
- - `application/vnd.openxmlformats-officedocument.presentationml.presentation`
84
- - `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
85
- - `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
86
- - `application/xml`
87
- - `application/zip`
88
- - `audio/flac`, `audio/mp4`, `audio/mpeg`, `audio/ogg`, `audio/wav`
89
- - `image/bmp`, `image/gif`, `image/jpeg`, `image/png`, `image/tiff`, `image/webp`
90
- - `text/csv`, `text/html`, `text/markdown`, `text/x-markdown`, `text/plain`, `text/xml`
91
- - `video/mp4`, `video/quicktime`, `video/x-msvideo`
252
+ Replace a content version with a new file:
92
253
 
93
- If the upload is sent as `application/octet-stream`, the server attempts to infer the type from the file extension, so pass `fileName` with a meaningful extension.
254
+ ```ts
255
+ await client.uploadFileToContent("content_version_id", {
256
+ file: await readFile("updated.pdf"),
257
+ fileName: "updated.pdf",
258
+ mimeType: "application/pdf",
259
+ });
260
+ ```
261
+
262
+ ### Source exports
94
263
 
95
264
  ```ts
96
- import { Seclai } from "@seclai/sdk";
265
+ const exports = await client.listSourceExports("source_id");
266
+ const exp = await client.createSourceExport("source_id", { format: "json" });
267
+ const status = await client.getSourceExport("source_id", exp.id);
268
+ const estimate = await client.estimateSourceExport("source_id", {});
269
+ const response = await client.downloadSourceExport("source_id", exp.id);
270
+ await client.deleteSourceExport("source_id", exp.id);
271
+ await client.cancelSourceExport("source_id", exp.id);
272
+ ```
97
273
 
98
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
274
+ ### Source embedding migrations
275
+
276
+ ```ts
277
+ const migration = await client.getSourceEmbeddingMigration("source_id");
278
+ await client.startSourceEmbeddingMigration("source_id", { target_model: "..." });
279
+ await client.cancelSourceEmbeddingMigration("source_id");
280
+ ```
281
+
282
+ ### Content
99
283
 
100
- const bytes = new TextEncoder().encode("hello");
101
- const upload = await client.uploadFileToSource("source_connection_id", {
102
- file: bytes,
103
- fileName: "hello.txt",
104
- mimeType: "text/plain",
105
- title: "Hello",
284
+ ```ts
285
+ const detail = await client.getContentDetail("content_id", { start: 0, end: 1000 });
286
+ const embeddings = await client.listContentEmbeddings("content_id");
287
+ await client.deleteContent("content_id");
288
+
289
+ // Replace content with inline text
290
+ await client.replaceContentWithInlineText("content_id", { text: "Updated text", title: "Updated" });
291
+
292
+ // Upload a replacement file
293
+ await client.uploadFileToContent("content_id", {
294
+ file: await readFile("updated.pdf"),
295
+ fileName: "updated.pdf",
106
296
  });
107
- console.log(upload);
108
297
  ```
109
298
 
110
- ## Development
299
+ ### Solutions
111
300
 
112
- ### Base URL
301
+ ```ts
302
+ const solutions = await client.listSolutions();
303
+ const sol = await client.createSolution({ name: "My Solution" });
304
+ const fetched = await client.getSolution("solution_id");
305
+ await client.updateSolution("solution_id", { name: "Renamed" });
306
+ await client.deleteSolution("solution_id");
307
+
308
+ // Link / unlink resources
309
+ await client.linkAgentsToSolution("solution_id", { ids: ["agent_id"] });
310
+ await client.unlinkAgentsFromSolution("solution_id", { ids: ["agent_id"] });
311
+ await client.linkKnowledgeBasesToSolution("solution_id", { ids: ["kb_id"] });
312
+ await client.unlinkKnowledgeBasesFromSolution("solution_id", { ids: ["kb_id"] });
313
+ await client.linkSourceConnectionsToSolution("solution_id", { ids: ["source_id"] });
314
+ await client.unlinkSourceConnectionsFromSolution("solution_id", { ids: ["source_id"] });
315
+
316
+ // AI assistant
317
+ const plan = await client.generateSolutionAiPlan("solution_id", { user_input: "Set up a RAG pipeline" });
318
+ await client.acceptSolutionAiPlan("solution_id", "conversation_id", {});
319
+ await client.declineSolutionAiPlan("solution_id", "conversation_id");
320
+
321
+ // AI-generated knowledge base / source within the solution
322
+ await client.generateSolutionAiKnowledgeBase("solution_id", { user_input: "..." });
323
+ await client.generateSolutionAiSource("solution_id", { user_input: "..." });
324
+
325
+ // Conversations
326
+ const convs = await client.listSolutionConversations("solution_id");
327
+ await client.addSolutionConversationTurn("solution_id", { user_input: "..." });
328
+ await client.markSolutionConversationTurn("solution_id", "conversation_id", { ... });
329
+ ```
113
330
 
114
- Set `SECLAI_API_URL` to point at a different API host (e.g., staging):
331
+ ### Governance AI
115
332
 
116
- ```bash
117
- export SECLAI_API_URL="https://example.invalid"
333
+ ```ts
334
+ const plan = await client.generateGovernanceAiPlan({ user_input: "Add a toxicity policy" });
335
+ const convs = await client.listGovernanceAiConversations();
336
+ await client.acceptGovernanceAiPlan("conversation_id");
337
+ await client.declineGovernanceAiPlan("conversation_id");
118
338
  ```
119
339
 
340
+ ### Alerts
341
+
342
+ ```ts
343
+ const alerts = await client.listAlerts({ status: "active" });
344
+ const alert = await client.getAlert("alert_id");
345
+ await client.changeAlertStatus("alert_id", { status: "resolved" });
346
+ await client.addAlertComment("alert_id", { text: "Investigating" });
347
+
348
+ // Subscriptions
349
+ await client.subscribeToAlert("alert_id");
350
+ await client.unsubscribeFromAlert("alert_id");
351
+
352
+ // Alert configs
353
+ const configs = await client.listAlertConfigs();
354
+ await client.createAlertConfig({ ... });
355
+ await client.getAlertConfig("config_id");
356
+ await client.updateAlertConfig("config_id", { ... });
357
+ await client.deleteAlertConfig("config_id");
358
+
359
+ // Organization preferences
360
+ const prefs = await client.listOrganizationAlertPreferences();
361
+ await client.updateOrganizationAlertPreference("org_id", "alert_type", { ... });
362
+ ```
363
+
364
+ ### Models
365
+
366
+ ```ts
367
+ const alerts = await client.listModelAlerts();
368
+ await client.markModelAlertRead("alert_id");
369
+ await client.markAllModelAlertsRead();
370
+ const unread = await client.getUnreadModelAlertCount();
371
+ const recs = await client.getModelRecommendations("model_id");
372
+ ```
373
+
374
+ ### Search
375
+
376
+ ```ts
377
+ const results = await client.search({ query: "quarterly report" });
378
+ const filtered = await client.search({ query: "my agent", entityType: "agent", limit: 5 });
379
+ ```
380
+
381
+ ### Top-level AI assistant
382
+
383
+ ```ts
384
+ // Generate plans for different resource types
385
+ const kb = await client.aiAssistantKnowledgeBase({ user_input: "Create a docs KB" });
386
+ const src = await client.aiAssistantSource({ user_input: "Add a web source" });
387
+ const sol = await client.aiAssistantSolution({ user_input: "Set up monitoring" });
388
+ const mb = await client.aiAssistantMemoryBank({ user_input: "Create a chat memory" });
389
+
390
+ // Accept or decline the generated plan
391
+ await client.acceptAiAssistantPlan("conversation_id", { confirm_deletions: true });
392
+ await client.declineAiAssistantPlan("conversation_id");
393
+
394
+ // Memory bank conversation history
395
+ const history = await client.getAiAssistantMemoryBankHistory();
396
+ await client.acceptAiMemoryBankSuggestion("conversation_id", { ... });
397
+
398
+ // Feedback
399
+ await client.submitAiFeedback({ ... });
400
+ ```
401
+
402
+ ## Pagination helper
403
+
404
+ Automatically iterate through all pages:
405
+
406
+ ```ts
407
+ for await (const source of client.paginate(
408
+ (opts) => client.listSources(opts),
409
+ { limit: 50 },
410
+ )) {
411
+ console.log(source.name);
412
+ }
413
+ ```
414
+
415
+ ## Error handling
416
+
417
+ All errors extend `SeclaiError`:
418
+
419
+ ```ts
420
+ import {
421
+ SeclaiError,
422
+ SeclaiConfigurationError,
423
+ SeclaiAPIStatusError,
424
+ SeclaiAPIValidationError,
425
+ SeclaiStreamingError,
426
+ } from "@seclai/sdk";
427
+
428
+ try {
429
+ await client.getAgent("bad_id");
430
+ } catch (err) {
431
+ if (err instanceof SeclaiAPIValidationError) {
432
+ console.error("Validation:", err.validationError);
433
+ } else if (err instanceof SeclaiAPIStatusError) {
434
+ console.error(`HTTP ${err.statusCode}:`, err.responseText);
435
+ } else if (err instanceof SeclaiStreamingError) {
436
+ console.error("Stream failed for run:", err.runId);
437
+ }
438
+ }
439
+ ```
440
+
441
+ ## Cancellation (AbortSignal)
442
+
443
+ All low-level methods support an `AbortSignal` for request cancellation:
444
+
445
+ ```ts
446
+ const controller = new AbortController();
447
+
448
+ // Cancel after 5 seconds
449
+ setTimeout(() => controller.abort(), 5_000);
450
+
451
+ const data = await client.request("GET", "/agents", {
452
+ signal: controller.signal,
453
+ });
454
+ ```
455
+
456
+ ## Low-level access
457
+
458
+ For endpoints not yet covered by a convenience method, use `request` or `requestRaw`:
459
+
460
+ ```ts
461
+ // JSON request/response
462
+ const data = await client.request("POST", "/custom/endpoint", {
463
+ json: { key: "value" },
464
+ query: { filter: "active" },
465
+ });
466
+
467
+ // Raw Response (e.g. binary downloads)
468
+ const response = await client.requestRaw("GET", "/files/download/123");
469
+ const blob = await response.blob();
470
+ ```
471
+
472
+ ## Development
473
+
120
474
  ### Install dependencies
121
475
 
122
476
  ```bash
@@ -137,6 +491,12 @@ npm run build
137
491
 
138
492
  This also regenerates `src/openapi.ts` from `openapi/seclai.openapi.json`.
139
493
 
494
+ ### Run tests
495
+
496
+ ```bash
497
+ npm test
498
+ ```
499
+
140
500
  ### Generate docs
141
501
 
142
502
  Generate HTML docs into `build/docs/`: