@signalvaultio/node 0.4.0 → 0.5.1
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 +25 -3
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +143 -39
- package/dist/index.mjs +16 -0
- package/package.json +19 -5
package/README.md
CHANGED
|
@@ -20,6 +20,18 @@ npm install @signalvaultio/node @anthropic-ai/sdk
|
|
|
20
20
|
npm install @signalvaultio/node openai @anthropic-ai/sdk
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
Supported provider SDKs: `openai` 4.4 through 7.x (openai 7 needs Node.js 22+) and `@anthropic-ai/sdk` 0.20 or later. Install only the one you use; the other is optional. TypeScript projects without `openai` need `skipLibCheck: true` (the default in most setups), because the SDK's type declarations refer to openai's types.
|
|
24
|
+
|
|
25
|
+
The package works with both `import` and `require()`:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import SignalVaultClient from '@signalvaultio/node'; // ESM, TypeScript
|
|
29
|
+
import { SignalVaultClient } from '@signalvaultio/node'; // also works
|
|
30
|
+
const { SignalVaultClient } = require('@signalvaultio/node'); // CommonJS
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Server-side bundlers (webpack, esbuild) are supported. esbuild's ESM output needs its usual `createRequire` banner for CommonJS dependencies: `--banner:js="import { createRequire } from 'module'; const require = createRequire(import.meta.url);"`.
|
|
34
|
+
|
|
23
35
|
## Quick Start — OpenAI
|
|
24
36
|
|
|
25
37
|
```typescript
|
|
@@ -28,8 +40,6 @@ import SignalVaultClient from '@signalvaultio/node';
|
|
|
28
40
|
const client = new SignalVaultClient({
|
|
29
41
|
apiKey: 'sk_live_your_signalvault_key',
|
|
30
42
|
openaiApiKey: process.env.OPENAI_API_KEY!,
|
|
31
|
-
baseUrl: 'https://api.signalvault.io',
|
|
32
|
-
environment: 'production',
|
|
33
43
|
});
|
|
34
44
|
|
|
35
45
|
// Use exactly like OpenAI SDK
|
|
@@ -91,7 +101,7 @@ for await (const event of stream) {
|
|
|
91
101
|
}
|
|
92
102
|
```
|
|
93
103
|
|
|
94
|
-
For
|
|
104
|
+
For OpenAI streams, the SDK sets `stream_options: { include_usage: true }` so the response is logged with its token counts. OpenAI then sends one extra final chunk that carries only the usage and no choices; the SDK consumes that chunk rather than passing it to you, so the stream you receive looks the same as without usage reporting. If you set `stream_options.include_usage` yourself, your setting is kept: with `true` you receive the usage chunk as usual, with `false` the response is logged without token counts. If your OpenAI-compatible server rejects `stream_options`, pass `stream_options: null` and the SDK sends none.
|
|
95
105
|
|
|
96
106
|
## Agent Tool-Use Capture
|
|
97
107
|
|
|
@@ -251,8 +261,20 @@ const client = new SignalVaultClient({
|
|
|
251
261
|
});
|
|
252
262
|
```
|
|
253
263
|
|
|
264
|
+
`environment` does not choose which rules apply: SignalVault uses the environment of the API key you pass as `apiKey`. Use a key created for the environment you want.
|
|
265
|
+
|
|
254
266
|
## Error Handling
|
|
255
267
|
|
|
268
|
+
When a guardrail rule blocks a request, `create()` throws `SignalVaultBlockedError` and the provider is never called. It carries:
|
|
269
|
+
|
|
270
|
+
- `violations` — the rules that matched, each with `rule_id`, `type` (for example `contains_secret`), `severity`, `action` and `details`
|
|
271
|
+
- `requestId` — the ID the SDK sent to SignalVault as `request_id` for this request
|
|
272
|
+
- `dashboardUrl` — a link to the blocked request, when the API provides one
|
|
273
|
+
|
|
274
|
+
The message lists only the violation types, never the matched content.
|
|
275
|
+
|
|
276
|
+
`SignalVaultUnavailableError` (with `requestId` and the HTTP `status`, if any) is thrown only with `failMode: 'closed'`, when no decision could be obtained.
|
|
277
|
+
|
|
256
278
|
```typescript
|
|
257
279
|
import { SignalVaultBlockedError, SignalVaultUnavailableError } from '@signalvaultio/node';
|
|
258
280
|
|
package/dist/index.d.mts
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import OpenAI from 'openai';
|
|
1
|
+
import type OpenAI from 'openai';
|
|
2
2
|
import { ToolsAPI } from './tools';
|
|
3
3
|
export type { ToolRecordOptions, ToolsAPI } from './tools';
|
|
4
4
|
/** Kept in sync with package.json by a unit test. */
|
|
5
|
-
export declare const SDK_VERSION = "0.
|
|
5
|
+
export declare const SDK_VERSION = "0.5.1";
|
|
6
6
|
/**
|
|
7
7
|
* Normalizes the base URL and refuses plaintext HTTP to anything but the local
|
|
8
8
|
* machine. The API key and full prompts travel in every request, so an
|
|
@@ -209,7 +209,9 @@ export declare class SignalVaultClient {
|
|
|
209
209
|
/**
|
|
210
210
|
* Yields chunks through unchanged and records the response when the stream
|
|
211
211
|
* ends — including when the consumer breaks out early or the stream throws,
|
|
212
|
-
* in which case the partial output is recorded.
|
|
212
|
+
* in which case the partial output is recorded. When the SDK added
|
|
213
|
+
* `include_usage` itself (`hideUsageChunk`), the usage-only final chunk is
|
|
214
|
+
* consumed here rather than passed on.
|
|
213
215
|
*/
|
|
214
216
|
private wrapStream;
|
|
215
217
|
private wrapAnthropicStream;
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.SignalVaultClient = exports.SignalVaultUnavailableError = exports.SignalVaultBlockedError = exports.SDK_VERSION = void 0;
|
|
7
4
|
exports.normalizeBaseUrl = normalizeBaseUrl;
|
|
8
5
|
const crypto_1 = require("crypto");
|
|
9
|
-
const openai_1 = __importDefault(require("openai"));
|
|
10
6
|
const tools_1 = require("./tools");
|
|
11
7
|
/** Kept in sync with package.json by a unit test. */
|
|
12
|
-
exports.SDK_VERSION = '0.
|
|
8
|
+
exports.SDK_VERSION = '0.5.1';
|
|
13
9
|
const DEFAULT_BASE_URL = 'https://api.signalvault.io';
|
|
14
10
|
const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
|
|
15
11
|
/** Longest Retry-After the SDK will wait out before retrying a background event once. */
|
|
@@ -42,25 +38,67 @@ function assertNodeEnvironment() {
|
|
|
42
38
|
* `http://` URL would expose both before the server could redirect to HTTPS.
|
|
43
39
|
*/
|
|
44
40
|
function normalizeBaseUrl(raw) {
|
|
41
|
+
// Errors never quote `raw`: it may contain credentials.
|
|
45
42
|
let url;
|
|
46
43
|
try {
|
|
47
44
|
url = new URL(raw);
|
|
48
45
|
}
|
|
49
46
|
catch {
|
|
50
|
-
throw new Error(
|
|
47
|
+
throw new Error('[SignalVault] baseUrl is not a valid URL.');
|
|
48
|
+
}
|
|
49
|
+
if (url.username || url.password) {
|
|
50
|
+
throw new Error('[SignalVault] baseUrl must not contain credentials (user:password@). Pass the key as apiKey.');
|
|
51
51
|
}
|
|
52
52
|
if (url.protocol === 'http:' && !LOCAL_HOSTS.has(url.hostname)) {
|
|
53
53
|
throw new Error('[SignalVault] baseUrl must use https:// (plain http:// is only allowed for localhost). ' +
|
|
54
54
|
'Your API key and prompts would otherwise be sent unencrypted.');
|
|
55
55
|
}
|
|
56
56
|
if (url.search || url.hash) {
|
|
57
|
-
throw new Error(
|
|
57
|
+
throw new Error('[SignalVault] baseUrl must not contain a query or fragment.');
|
|
58
58
|
}
|
|
59
59
|
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
|
60
60
|
throw new Error(`[SignalVault] baseUrl must be an https:// URL, got ${url.protocol}`);
|
|
61
61
|
}
|
|
62
62
|
return raw.replace(/\/+$/, '');
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Strips surrounding whitespace and refuses keys that can't go in a header.
|
|
66
|
+
* A key read from a file or a secret often ends in a newline. Sent as is,
|
|
67
|
+
* every request fails before it leaves the process, so with failMode 'open'
|
|
68
|
+
* every call would go through unchecked. Errors never include the key.
|
|
69
|
+
*/
|
|
70
|
+
function normalizeApiKey(raw) {
|
|
71
|
+
if (typeof raw !== 'string') {
|
|
72
|
+
throw new TypeError(`[SignalVault] apiKey must be a string, got ${raw === null ? 'null' : typeof raw}.`);
|
|
73
|
+
}
|
|
74
|
+
const key = raw.trim();
|
|
75
|
+
if (!key) {
|
|
76
|
+
throw new Error('[SignalVault] apiKey is empty.');
|
|
77
|
+
}
|
|
78
|
+
if (/[^\x21-\x7e]/.test(key)) {
|
|
79
|
+
throw new Error('[SignalVault] apiKey contains whitespace, control or non-ASCII characters. Check how the key is loaded.');
|
|
80
|
+
}
|
|
81
|
+
return key;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* One line describing an error and its causes, for debug output. Never
|
|
85
|
+
* includes `secret`, and never throws.
|
|
86
|
+
*/
|
|
87
|
+
function describeError(error, secret) {
|
|
88
|
+
const parts = [];
|
|
89
|
+
let current = error;
|
|
90
|
+
for (let depth = 0; current !== undefined && current !== null && depth < 5; depth++) {
|
|
91
|
+
try {
|
|
92
|
+
parts.push(current instanceof Error ? `${current.name}: ${current.message}` : String(current));
|
|
93
|
+
current = current instanceof Error ? current.cause : undefined;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
parts.push('<unprintable error>');
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return parts.join(' <- ').split(secret).join('[redacted]');
|
|
101
|
+
}
|
|
64
102
|
/** Thrown when a guardrail rule blocks the request. */
|
|
65
103
|
class SignalVaultBlockedError extends Error {
|
|
66
104
|
constructor(requestId, violations, dashboardUrl) {
|
|
@@ -134,6 +172,76 @@ function parseRetryAfterMs(response) {
|
|
|
134
172
|
return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : null;
|
|
135
173
|
}
|
|
136
174
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
175
|
+
/**
|
|
176
|
+
* The provider SDKs are optional peers, loaded only when their API key is
|
|
177
|
+
* configured, so installing just the one you use is enough. The requires are
|
|
178
|
+
* literal strings inside try blocks on purpose: bundlers can only follow a
|
|
179
|
+
* literal require, and treat one inside a try as optional.
|
|
180
|
+
*/
|
|
181
|
+
function loadOpenAI(apiKey) {
|
|
182
|
+
let mod;
|
|
183
|
+
try {
|
|
184
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
185
|
+
mod = require('openai');
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
throw notInstalled(error, 'openai');
|
|
189
|
+
}
|
|
190
|
+
return construct(mod, apiKey);
|
|
191
|
+
}
|
|
192
|
+
function loadAnthropic(apiKey) {
|
|
193
|
+
let mod;
|
|
194
|
+
try {
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
196
|
+
mod = require('@anthropic-ai/sdk');
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
throw notInstalled(error, '@anthropic-ai/sdk');
|
|
200
|
+
}
|
|
201
|
+
return construct(mod, apiKey);
|
|
202
|
+
}
|
|
203
|
+
function construct(mod, apiKey) {
|
|
204
|
+
const Client = mod.default ?? mod;
|
|
205
|
+
return new Client({ apiKey });
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Only a missing package is reported as "not installed"; anything else (a
|
|
209
|
+
* broken install, a missing transitive dependency) is returned unchanged.
|
|
210
|
+
*/
|
|
211
|
+
function notInstalled(error, pkg) {
|
|
212
|
+
// Read fields directly: `instanceof Error` fails across realms (e.g. under Jest).
|
|
213
|
+
const { code, message } = (error ?? {});
|
|
214
|
+
if (code === 'MODULE_NOT_FOUND' && String(message).includes(`'${pkg}'`)) {
|
|
215
|
+
return new Error(`[SignalVault] ${pkg} is not installed. Run: npm install ${pkg}`);
|
|
216
|
+
}
|
|
217
|
+
return error;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Asks OpenAI to report token usage on a stream, unless the caller already
|
|
221
|
+
* decided either way. OpenAI then sends one extra final chunk with an empty
|
|
222
|
+
* `choices` array; `injected` tells the stream wrapper to consume that chunk
|
|
223
|
+
* instead of passing it to a caller who never asked for it.
|
|
224
|
+
*
|
|
225
|
+
* `stream_options: null` opts out entirely: the key is removed, for
|
|
226
|
+
* OpenAI-compatible servers that reject `stream_options`. (The OpenAI API
|
|
227
|
+
* treats null and absent the same.)
|
|
228
|
+
*/
|
|
229
|
+
function withStreamUsage(params) {
|
|
230
|
+
if (!params.stream)
|
|
231
|
+
return { params, injected: false };
|
|
232
|
+
const options = params.stream_options;
|
|
233
|
+
if (options === null) {
|
|
234
|
+
const { stream_options: _omitted, ...rest } = params;
|
|
235
|
+
return { params: rest, injected: false };
|
|
236
|
+
}
|
|
237
|
+
if (options && options.include_usage !== undefined) {
|
|
238
|
+
return { params, injected: false };
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
params: { ...params, stream_options: { ...(options ?? {}), include_usage: true } },
|
|
242
|
+
injected: true,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
137
245
|
// ---------------------------------------------------------------------------
|
|
138
246
|
// SignalVaultClient
|
|
139
247
|
// ---------------------------------------------------------------------------
|
|
@@ -170,7 +278,7 @@ class SignalVaultClient {
|
|
|
170
278
|
if (config.failMode !== undefined && config.failMode !== 'open' && config.failMode !== 'closed') {
|
|
171
279
|
throw new Error("[SignalVault] failMode must be 'open' or 'closed'.");
|
|
172
280
|
}
|
|
173
|
-
this.svApiKey = config.apiKey;
|
|
281
|
+
this.svApiKey = normalizeApiKey(config.apiKey);
|
|
174
282
|
this.svBaseUrl = normalizeBaseUrl(config.baseUrl || DEFAULT_BASE_URL);
|
|
175
283
|
this.svEnvironment = config.environment || 'production';
|
|
176
284
|
this.debugMode = config.debug || false;
|
|
@@ -179,23 +287,8 @@ class SignalVaultClient {
|
|
|
179
287
|
this.preflightTimeout = config.preflightTimeout ?? 2000;
|
|
180
288
|
this.bgTimeout = config.timeout ?? 10000;
|
|
181
289
|
this.defaultMetadata = config.metadata ?? {};
|
|
182
|
-
this.openai = config.openaiApiKey
|
|
183
|
-
|
|
184
|
-
: null;
|
|
185
|
-
if (config.anthropicApiKey) {
|
|
186
|
-
try {
|
|
187
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
188
|
-
const mod = require('@anthropic-ai/sdk');
|
|
189
|
-
const Anthropic = mod.default ?? mod;
|
|
190
|
-
this.anthropic = new Anthropic({ apiKey: config.anthropicApiKey });
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
throw new Error('[SignalVault] @anthropic-ai/sdk is not installed. Run: npm install @anthropic-ai/sdk');
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
this.anthropic = null;
|
|
198
|
-
}
|
|
290
|
+
this.openai = config.openaiApiKey ? loadOpenAI(config.openaiApiKey) : null;
|
|
291
|
+
this.anthropic = config.anthropicApiKey ? loadAnthropic(config.anthropicApiKey) : null;
|
|
199
292
|
}
|
|
200
293
|
// ---------------------------------------------------------------------------
|
|
201
294
|
// Lifecycle
|
|
@@ -231,8 +324,9 @@ class SignalVaultClient {
|
|
|
231
324
|
track(promise) {
|
|
232
325
|
const tracked = promise
|
|
233
326
|
.catch((err) => {
|
|
234
|
-
if (this.debugMode)
|
|
235
|
-
console.error('[SignalVault] Background event failed:', err);
|
|
327
|
+
if (this.debugMode) {
|
|
328
|
+
console.error('[SignalVault] Background event failed:', describeError(err, this.svApiKey));
|
|
329
|
+
}
|
|
236
330
|
})
|
|
237
331
|
.finally(() => this.pending.delete(tracked));
|
|
238
332
|
this.pending.add(tracked);
|
|
@@ -406,11 +500,12 @@ class SignalVaultClient {
|
|
|
406
500
|
// OpenAI — mirror mode
|
|
407
501
|
// ---------------------------------------------------------------------------
|
|
408
502
|
async handleOpenAIMirror(requestId, params, metadata) {
|
|
409
|
-
const
|
|
503
|
+
const usage = withStreamUsage(params);
|
|
504
|
+
const response = await this.openai.chat.completions.create(usage.params);
|
|
410
505
|
const model = params.model;
|
|
411
506
|
const messages = params.messages;
|
|
412
507
|
if (params.stream) {
|
|
413
|
-
return this.wrapStream(response, (output, promptTokens, completionTokens) => this.sendAuditEvents(requestId, model, messages, output, promptTokens, completionTokens, metadata, 'openai'));
|
|
508
|
+
return this.wrapStream(response, usage.injected, (output, promptTokens, completionTokens) => this.sendAuditEvents(requestId, model, messages, output, promptTokens, completionTokens, metadata, 'openai'));
|
|
414
509
|
}
|
|
415
510
|
const completion = response;
|
|
416
511
|
this.track(this.sendAuditEvents(requestId, model, messages, completion.choices[0]?.message?.content || '', completion.usage?.prompt_tokens || 0, completion.usage?.completion_tokens || 0, metadata, 'openai'));
|
|
@@ -424,9 +519,10 @@ class SignalVaultClient {
|
|
|
424
519
|
const messages = params.messages;
|
|
425
520
|
const decision = await this.enforce(requestId, model, messages, metadata, 'openai');
|
|
426
521
|
const fallback = this.fallbackRequest(decision, requestId, model, messages, metadata, 'openai');
|
|
427
|
-
const
|
|
522
|
+
const usage = withStreamUsage(params);
|
|
523
|
+
const response = await this.openai.chat.completions.create(usage.params);
|
|
428
524
|
if (params.stream) {
|
|
429
|
-
return this.wrapStream(response, (output, promptTokens, completionTokens) => this.sendResponseEvent(requestId, model, output, promptTokens, completionTokens, metadata, 'openai', fallback));
|
|
525
|
+
return this.wrapStream(response, usage.injected, (output, promptTokens, completionTokens) => this.sendResponseEvent(requestId, model, output, promptTokens, completionTokens, metadata, 'openai', fallback));
|
|
430
526
|
}
|
|
431
527
|
const completion = response;
|
|
432
528
|
// Background: the audit of the response must not delay returning it.
|
|
@@ -465,9 +561,11 @@ class SignalVaultClient {
|
|
|
465
561
|
/**
|
|
466
562
|
* Yields chunks through unchanged and records the response when the stream
|
|
467
563
|
* ends — including when the consumer breaks out early or the stream throws,
|
|
468
|
-
* in which case the partial output is recorded.
|
|
564
|
+
* in which case the partial output is recorded. When the SDK added
|
|
565
|
+
* `include_usage` itself (`hideUsageChunk`), the usage-only final chunk is
|
|
566
|
+
* consumed here rather than passed on.
|
|
469
567
|
*/
|
|
470
|
-
async *wrapStream(stream, onComplete) {
|
|
568
|
+
async *wrapStream(stream, hideUsageChunk, onComplete) {
|
|
471
569
|
const chunks = [];
|
|
472
570
|
let promptTokens = 0;
|
|
473
571
|
let completionTokens = 0;
|
|
@@ -479,6 +577,9 @@ class SignalVaultClient {
|
|
|
479
577
|
if (chunk.usage) {
|
|
480
578
|
promptTokens = chunk.usage.prompt_tokens || 0;
|
|
481
579
|
completionTokens = chunk.usage.completion_tokens || 0;
|
|
580
|
+
// Missing or empty `choices`: the usage-only chunk the caller didn't ask for.
|
|
581
|
+
if (hideUsageChunk && !chunk.choices?.length)
|
|
582
|
+
continue;
|
|
482
583
|
}
|
|
483
584
|
yield chunk;
|
|
484
585
|
}
|
|
@@ -641,8 +742,9 @@ class SignalVaultClient {
|
|
|
641
742
|
}
|
|
642
743
|
}
|
|
643
744
|
catch (error) {
|
|
644
|
-
if (this.debugMode)
|
|
645
|
-
console.error('[SignalVault] Event send failed:', error);
|
|
745
|
+
if (this.debugMode) {
|
|
746
|
+
console.error('[SignalVault] Event send failed:', describeError(error, this.svApiKey));
|
|
747
|
+
}
|
|
646
748
|
if (!isTimeout(error))
|
|
647
749
|
retryDelayMs = 250 + Math.random() * 500;
|
|
648
750
|
}
|
|
@@ -654,8 +756,9 @@ class SignalVaultClient {
|
|
|
654
756
|
return result;
|
|
655
757
|
}
|
|
656
758
|
catch (error) {
|
|
657
|
-
if (this.debugMode)
|
|
658
|
-
console.error('[SignalVault] Event retry failed:', error);
|
|
759
|
+
if (this.debugMode) {
|
|
760
|
+
console.error('[SignalVault] Event retry failed:', describeError(error, this.svApiKey));
|
|
761
|
+
}
|
|
659
762
|
}
|
|
660
763
|
}
|
|
661
764
|
this.logUndelivered(result);
|
|
@@ -676,8 +779,9 @@ class SignalVaultClient {
|
|
|
676
779
|
}
|
|
677
780
|
catch (error) {
|
|
678
781
|
const timedOut = isTimeout(error);
|
|
679
|
-
if (this.debugMode)
|
|
680
|
-
console.error('[SignalVault] Pre-flight failed:', error);
|
|
782
|
+
if (this.debugMode) {
|
|
783
|
+
console.error('[SignalVault] Pre-flight failed:', describeError(error, this.svApiKey));
|
|
784
|
+
}
|
|
681
785
|
return this.unavailable(requestId, timedOut ? 'timeout' : 'network', timedOut
|
|
682
786
|
? `SignalVault pre-flight check timed out after ${this.preflightTimeout}ms`
|
|
683
787
|
: 'SignalVault API is unreachable');
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// ESM entry point. Re-exports the CommonJS build rather than shipping a
|
|
2
|
+
// second copy of the SDK: two copies would each hold their own module state
|
|
3
|
+
// (the AsyncLocalStorage behind withContext), so context set through `import`
|
|
4
|
+
// would be invisible to code loaded through `require`. Named imports from
|
|
5
|
+
// CommonJS keep default-export interop out of the picture for Node and
|
|
6
|
+
// bundlers alike. Keep this list in sync with src/index.ts — the package test
|
|
7
|
+
// fails if they differ.
|
|
8
|
+
export {
|
|
9
|
+
SDK_VERSION,
|
|
10
|
+
SignalVaultBlockedError,
|
|
11
|
+
SignalVaultClient,
|
|
12
|
+
SignalVaultUnavailableError,
|
|
13
|
+
normalizeBaseUrl,
|
|
14
|
+
} from './index.js';
|
|
15
|
+
import { SignalVaultClient } from './index.js';
|
|
16
|
+
export default SignalVaultClient;
|
package/package.json
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signalvaultio/node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "SignalVault SDK - AI audit logs and guardrails for safe production AI",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
7
21
|
"files": [
|
|
8
22
|
"dist",
|
|
9
23
|
"!dist/__tests__",
|
|
10
24
|
"README.md"
|
|
11
25
|
],
|
|
12
26
|
"scripts": {
|
|
13
|
-
"build": "
|
|
27
|
+
"build": "node scripts/build.mjs",
|
|
14
28
|
"prepublishOnly": "npm run build",
|
|
15
29
|
"test": "jest --passWithNoTests"
|
|
16
30
|
},
|
|
@@ -41,7 +55,7 @@
|
|
|
41
55
|
"homepage": "https://signalvault.io/docs",
|
|
42
56
|
"peerDependencies": {
|
|
43
57
|
"@anthropic-ai/sdk": ">=0.20.0",
|
|
44
|
-
"openai": "^4.0.0"
|
|
58
|
+
"openai": "^4.4.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
45
59
|
},
|
|
46
60
|
"peerDependenciesMeta": {
|
|
47
61
|
"openai": {
|