@palveron/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/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/dist/index.d.mts +225 -0
- package/dist/index.d.ts +225 -0
- package/dist/index.js +444 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +402 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// ============================================================\n// @palveron/sdk — Official TypeScript SDK for Palveron AI Governance\n// ============================================================\n// Zero dependencies. Works in Node.js 18+, Deno, Bun, Edge Runtimes.\n// ============================================================\n\n// ─── Types ──────────────────────────────────────────────────\n\nexport type Decision = 'ALLOWED' | 'BLOCKED' | 'MODIFIED' | 'ERROR';\nexport type RiskLevel = 'minimal' | 'limited' | 'high' | 'unacceptable';\nexport type Sensitivity = 'low' | 'medium' | 'high';\n\nexport interface PalveronConfig {\n /** API key (starts with pv_live_ or pv_test_) */\n apiKey: string;\n /** Gateway base URL (default: https://gateway.palveron.com) */\n baseUrl?: string;\n /** Request timeout in ms (default: 30000) */\n timeout?: number;\n /** Max retry attempts on transient failures (default: 3) */\n maxRetries?: number;\n /** Base delay for exponential backoff in ms (default: 500) */\n retryBaseDelay?: number;\n /** Custom logger (default: console) */\n logger?: PalveronLogger;\n /** Custom headers added to every request */\n headers?: Record<string, string>;\n /** Circuit breaker: max consecutive failures before opening (default: 5) */\n circuitBreakerThreshold?: number;\n /** Circuit breaker: cooldown in ms before half-open retry (default: 30000) */\n circuitBreakerCooldown?: number;\n}\n\nexport interface PalveronLogger {\n debug(message: string, meta?: Record<string, unknown>): void;\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n}\n\nexport interface Attachment {\n /** MIME type (e.g. \"image/png\", \"audio/wav\", \"application/pdf\") */\n contentType: string;\n /** Base64-encoded data */\n data: string;\n /** Optional filename */\n filename?: string;\n /** Optional per-attachment metadata (GPS, resolution, etc.) */\n metadata?: Record<string, unknown>;\n}\n\nexport interface RequestContext {\n /** MCP server URL if applicable */\n mcpServer?: string;\n /** MCP tool name */\n toolName?: string;\n /** Agent chain depth for recursive calls */\n chainDepth?: number;\n /** Source system identifier (\"ros2\", \"unity\", \"cursor-ide\", etc.) */\n sourceSystem?: string;\n /** Session ID for conversation tracking */\n sessionId?: string;\n}\n\nexport interface VerifyRequest {\n /** The prompt or input text to verify */\n prompt: string;\n /** Pre-extracted text from attachments (optional, server extracts if absent) */\n extractedText?: string;\n /** Arbitrary metadata passed through to the trace */\n metadata?: Record<string, unknown>;\n /** Multi-modal attachments (images, audio, documents, code) */\n attachments?: Attachment[];\n /** Agentic context (MCP, tool chains, source systems) */\n context?: RequestContext;\n}\n\nexport interface Finding {\n risk: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\n category: string;\n description: string;\n confidence: number;\n}\n\nexport interface VerifyResponse {\n /** Governance decision */\n decision: Decision;\n /** Modified/sanitized output (present when decision is MODIFIED) */\n output: string;\n /** Human-readable reason for the decision */\n reason: string;\n /** Unique trace ID for audit trail */\n traceId: string;\n /** SHA-256 integrity hash of the governance decision */\n integrityHash: string;\n /** Whether this trace will be anchored to Flare blockchain */\n shouldAnchor: boolean;\n /** Flare blockchain status */\n flareStatus: string;\n /** Flare transaction hash (populated after anchoring) */\n flareTxHash: string | null;\n /** Detected content type */\n contentType: string;\n /** Security findings (secrets, PII, policy violations) */\n findings: Finding[];\n /** Server-side latency in milliseconds */\n latencyMs: number;\n}\n\nexport interface PolicyListResponse {\n policies: Array<{\n id: string;\n name: string;\n prompt: string;\n environment: string;\n contentTypes: string[];\n createdAt: string;\n updatedAt: string;\n }>;\n}\n\nexport interface HealthResponse {\n status: 'healthy' | 'degraded' | 'unhealthy';\n version: string;\n uptime: number;\n checks: Record<string, { status: string; latencyMs: number }>;\n}\n\n// ─── Errors ─────────────────────────────────────────────────\n\nexport class PalveronError extends Error {\n public readonly code: string;\n public readonly statusCode: number;\n public readonly requestId: string | null;\n public readonly retryable: boolean;\n\n constructor(message: string, opts: {\n code: string;\n statusCode: number;\n requestId?: string | null;\n retryable?: boolean;\n }) {\n super(message);\n this.name = 'PalveronError';\n this.code = opts.code;\n this.statusCode = opts.statusCode;\n this.requestId = opts.requestId ?? null;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nexport class PalveronAuthenticationError extends PalveronError {\n constructor(message: string, requestId?: string | null) {\n super(message, { code: 'AUTHENTICATION_FAILED', statusCode: 401, requestId, retryable: false });\n this.name = 'PalveronAuthenticationError';\n }\n}\n\nexport class PalveronRateLimitError extends PalveronError {\n public readonly retryAfterMs: number;\n\n constructor(message: string, retryAfterMs: number, requestId?: string | null) {\n super(message, { code: 'RATE_LIMITED', statusCode: 429, requestId, retryable: true });\n this.name = 'PalveronRateLimitError';\n this.retryAfterMs = retryAfterMs;\n }\n}\n\nexport class PalveronValidationError extends PalveronError {\n public readonly field: string | null;\n\n constructor(message: string, field?: string, requestId?: string | null) {\n super(message, { code: 'VALIDATION_ERROR', statusCode: 400, requestId, retryable: false });\n this.name = 'PalveronValidationError';\n this.field = field ?? null;\n }\n}\n\nexport class PalveronCircuitOpenError extends PalveronError {\n constructor() {\n super('Circuit breaker is open — too many consecutive failures. Retry later.', {\n code: 'CIRCUIT_OPEN', statusCode: 503, retryable: false,\n });\n this.name = 'PalveronCircuitOpenError';\n }\n}\n\nexport class PalveronTimeoutError extends PalveronError {\n constructor(timeoutMs: number, requestId?: string | null) {\n super(`Request timed out after ${timeoutMs}ms`, {\n code: 'TIMEOUT', statusCode: 408, requestId, retryable: true,\n });\n this.name = 'PalveronTimeoutError';\n }\n}\n\n// ─── Circuit Breaker ────────────────────────────────────────\n\nclass CircuitBreaker {\n private failures = 0;\n private lastFailure = 0;\n private state: 'closed' | 'open' | 'half-open' = 'closed';\n\n constructor(\n private threshold: number,\n private cooldownMs: number,\n ) {}\n\n canRequest(): boolean {\n if (this.state === 'closed') return true;\n if (this.state === 'open') {\n if (Date.now() - this.lastFailure >= this.cooldownMs) {\n this.state = 'half-open';\n return true;\n }\n return false;\n }\n return true; // half-open: allow one request\n }\n\n onSuccess(): void {\n this.failures = 0;\n this.state = 'closed';\n }\n\n onFailure(): void {\n this.failures++;\n this.lastFailure = Date.now();\n if (this.failures >= this.threshold) {\n this.state = 'open';\n }\n }\n\n getState(): string {\n return this.state;\n }\n}\n\n// ─── Client ─────────────────────────────────────────────────\n\nconst DEFAULT_BASE_URL = 'https://gateway.palveron.com';\nconst DEFAULT_TIMEOUT = 30_000;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY = 500;\nconst SDK_VERSION = '1.0.0';\n\nexport class Palveron {\n private readonly config: Required<Pick<PalveronConfig,\n 'apiKey' | 'baseUrl' | 'timeout' | 'maxRetries' | 'retryBaseDelay'\n >> & Pick<PalveronConfig, 'logger' | 'headers'>;\n\n private readonly circuit: CircuitBreaker;\n\n constructor(config: PalveronConfig) {\n if (!config.apiKey) throw new PalveronValidationError('apiKey is required');\n\n this.config = {\n apiKey: config.apiKey,\n baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, ''),\n timeout: config.timeout ?? DEFAULT_TIMEOUT,\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n retryBaseDelay: config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY,\n logger: config.logger,\n headers: config.headers,\n };\n\n this.circuit = new CircuitBreaker(\n config.circuitBreakerThreshold ?? 5,\n config.circuitBreakerCooldown ?? 30_000,\n );\n }\n\n // ── Core: Verify ────────────────────────────────────────\n\n /**\n * Send a governance verification request.\n * This is the primary method — every LLM call should go through this.\n *\n * @example\n * ```typescript\n * const result = await palveron.verify({ prompt: 'User input here' });\n * if (result.decision === 'BLOCKED') {\n * throw new Error(result.reason);\n * }\n * ```\n */\n async verify(request: VerifyRequest): Promise<VerifyResponse> {\n const body = {\n prompt: request.prompt,\n extracted_text: request.extractedText,\n metadata: request.metadata,\n attachments: request.attachments?.map(a => ({\n content_type: a.contentType,\n data: a.data,\n filename: a.filename,\n metadata: a.metadata,\n })),\n context: request.context ? {\n mcp_server: request.context.mcpServer,\n tool_name: request.context.toolName,\n chain_depth: request.context.chainDepth,\n source_system: request.context.sourceSystem,\n session_id: request.context.sessionId,\n } : undefined,\n };\n\n const start = Date.now();\n const raw = await this.request<Record<string, unknown>>('POST', '/api/v1/verify', body);\n const latency = Date.now() - start;\n\n return {\n decision: (raw.decision as Decision) ?? 'ERROR',\n output: (raw.output as string) ?? '',\n reason: (raw.reason as string) ?? '',\n traceId: (raw.trace_id as string) ?? '',\n integrityHash: (raw.integrity_hash as string) ?? '',\n shouldAnchor: (raw.should_anchor as boolean) ?? false,\n flareStatus: (raw.flare_status as string) ?? '',\n flareTxHash: (raw.flare_tx_hash as string | null) ?? null,\n contentType: (raw.content_type as string) ?? 'text',\n findings: (raw.findings as Finding[]) ?? [],\n latencyMs: latency,\n };\n }\n\n // ── Convenience: Quick verify (string-only) ─────────────\n\n /**\n * Quick verification for text-only prompts.\n *\n * @example\n * ```typescript\n * const result = await palveron.check('Is this prompt safe?');\n * console.log(result.decision); // 'ALLOWED'\n * ```\n */\n async check(prompt: string): Promise<VerifyResponse> {\n return this.verify({ prompt });\n }\n\n // ── Convenience: Verify with file ───────────────────────\n\n /**\n * Verify a prompt with a file attachment.\n * Reads the file, Base64-encodes it, and sends it with the correct MIME type.\n * Node.js only — use verify() with pre-encoded data in browsers.\n *\n * @example\n * ```typescript\n * const result = await palveron.verifyWithFile(\n * 'Analyze this document',\n * '/path/to/report.pdf'\n * );\n * ```\n */\n async verifyWithFile(prompt: string, filePath: string): Promise<VerifyResponse> {\n // Dynamic import to keep SDK isomorphic\n const { readFile } = await import('node:fs/promises');\n const { basename } = await import('node:path');\n\n const buffer = await readFile(filePath);\n const base64 = buffer.toString('base64');\n const filename = basename(filePath);\n const contentType = this.inferMimeType(filename);\n\n return this.verify({\n prompt,\n attachments: [{ contentType, data: base64, filename }],\n });\n }\n\n // ── Policies ────────────────────────────────────────────\n\n /**\n * List all active policies for the project.\n */\n async listPolicies(env: string = 'prod'): Promise<PolicyListResponse> {\n return this.request<PolicyListResponse>('GET', `/api/v1/policies?env=${env}`);\n }\n\n // ── Health ──────────────────────────────────────────────\n\n /**\n * Check gateway health status.\n */\n async health(): Promise<HealthResponse> {\n return this.request<HealthResponse>('GET', '/health');\n }\n\n // ── Diagnostics ─────────────────────────────────────────\n\n /**\n * Get SDK and connection diagnostics.\n */\n diagnostics(): {\n sdkVersion: string;\n baseUrl: string;\n timeout: number;\n maxRetries: number;\n circuitState: string;\n } {\n return {\n sdkVersion: SDK_VERSION,\n baseUrl: this.config.baseUrl,\n timeout: this.config.timeout,\n maxRetries: this.config.maxRetries,\n circuitState: this.circuit.getState(),\n };\n }\n\n // ─── Internal: HTTP with retry + circuit breaker ────────\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n if (!this.circuit.canRequest()) {\n throw new PalveronCircuitOpenError();\n }\n\n let lastError: Error | null = null;\n const maxAttempts = this.config.maxRetries + 1;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (attempt > 0) {\n const delay = this.backoffDelay(attempt);\n this.config.logger?.debug(`Retry attempt ${attempt}/${this.config.maxRetries}`, { delay, path });\n await this.sleep(delay);\n }\n\n const requestId = this.generateRequestId();\n\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeout);\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'User-Agent': `palveron-sdk-typescript/${SDK_VERSION}`,\n 'X-Request-ID': requestId,\n ...this.config.headers,\n };\n\n const response = await fetch(`${this.config.baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n const responseRequestId = response.headers.get('x-request-id') ?? requestId;\n\n if (response.ok) {\n this.circuit.onSuccess();\n return await response.json() as T;\n }\n\n // Handle specific error codes\n if (response.status === 401) {\n this.circuit.onSuccess(); // auth errors are not circuit failures\n throw new PalveronAuthenticationError(\n 'Invalid API key or expired token',\n responseRequestId,\n );\n }\n\n if (response.status === 429) {\n const retryAfter = parseInt(response.headers.get('retry-after') ?? '5', 10) * 1000;\n throw new PalveronRateLimitError(\n 'Rate limit exceeded',\n retryAfter,\n responseRequestId,\n );\n }\n\n if (response.status === 400) {\n const errorBody = await response.json().catch(() => ({})) as Record<string, string>;\n throw new PalveronValidationError(\n errorBody.error ?? 'Invalid request',\n errorBody.field,\n responseRequestId,\n );\n }\n\n // Retryable server errors (500, 502, 503)\n if (response.status >= 500) {\n this.circuit.onFailure();\n const errorBody = await response.text().catch(() => '');\n lastError = new PalveronError(\n `Server error: ${response.status} ${response.statusText}`,\n { code: 'SERVER_ERROR', statusCode: response.status, requestId: responseRequestId, retryable: true },\n );\n this.config.logger?.warn(`Server error on attempt ${attempt + 1}`, {\n status: response.status,\n requestId: responseRequestId,\n body: errorBody.slice(0, 200),\n });\n continue; // retry\n }\n\n // Non-retryable client errors\n const errorBody = await response.json().catch(() => ({})) as Record<string, string>;\n throw new PalveronError(\n errorBody.error ?? `HTTP ${response.status}`,\n { code: 'CLIENT_ERROR', statusCode: response.status, requestId: responseRequestId, retryable: false },\n );\n\n } catch (error) {\n if (error instanceof PalveronError && !error.retryable) throw error;\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n this.circuit.onFailure();\n lastError = new PalveronTimeoutError(this.config.timeout, requestId);\n continue;\n }\n\n if (error instanceof TypeError && error.message.includes('fetch')) {\n this.circuit.onFailure();\n lastError = new PalveronError('Network error — could not reach gateway', {\n code: 'NETWORK_ERROR', statusCode: 0, requestId, retryable: true,\n });\n continue;\n }\n\n if (error instanceof PalveronError) {\n lastError = error;\n continue;\n }\n\n throw error;\n }\n }\n\n throw lastError ?? new PalveronError('Max retries exceeded', {\n code: 'MAX_RETRIES', statusCode: 0, retryable: false,\n });\n }\n\n // ─── Helpers ────────────────────────────────────────────\n\n private backoffDelay(attempt: number): number {\n const base = this.config.retryBaseDelay * Math.pow(2, attempt - 1);\n const jitter = base * 0.2 * Math.random();\n return Math.min(base + jitter, 30_000);\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private generateRequestId(): string {\n const ts = Date.now().toString(36);\n const rand = Math.random().toString(36).slice(2, 8);\n return `pv_${ts}_${rand}`;\n }\n\n private inferMimeType(filename: string): string {\n const ext = filename.split('.').pop()?.toLowerCase();\n const map: Record<string, string> = {\n png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif',\n webp: 'image/webp', svg: 'image/svg+xml', pdf: 'application/pdf',\n wav: 'audio/wav', mp3: 'audio/mpeg', ogg: 'audio/ogg', mp4: 'video/mp4',\n py: 'text/x-python', js: 'text/javascript', ts: 'text/typescript',\n rs: 'text/x-rust', go: 'text/x-go', java: 'text/x-java',\n c: 'text/x-c', cpp: 'text/x-c++', txt: 'text/plain',\n json: 'application/json', csv: 'text/csv', xml: 'application/xml',\n };\n return map[ext ?? ''] ?? 'application/octet-stream';\n }\n}\n\n// ─── Factory ──────────────────────────────────────────────\n\n/**\n * Create a Palveron client instance.\n *\n * @example\n * ```typescript\n * import { createClient } from '@palveron/sdk';\n *\n * const palveron = createClient({\n * apiKey: process.env.PALVERON_API_KEY!,\n * baseUrl: 'https://gateway.acme.corp:8080', // on-prem\n * });\n *\n * const result = await palveron.verify({ prompt: userInput });\n * ```\n */\nexport function createClient(config: PalveronConfig): Palveron {\n return new Palveron(config);\n}\n\n// ─── Re-exports ─────────────────────────────────────────\n\nexport default Palveron;\n"],"mappings":"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","names":["errorBody"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var PalveronError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
statusCode;
|
|
5
|
+
requestId;
|
|
6
|
+
retryable;
|
|
7
|
+
constructor(message, opts) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "PalveronError";
|
|
10
|
+
this.code = opts.code;
|
|
11
|
+
this.statusCode = opts.statusCode;
|
|
12
|
+
this.requestId = opts.requestId ?? null;
|
|
13
|
+
this.retryable = opts.retryable ?? false;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var PalveronAuthenticationError = class extends PalveronError {
|
|
17
|
+
constructor(message, requestId) {
|
|
18
|
+
super(message, { code: "AUTHENTICATION_FAILED", statusCode: 401, requestId, retryable: false });
|
|
19
|
+
this.name = "PalveronAuthenticationError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var PalveronRateLimitError = class extends PalveronError {
|
|
23
|
+
retryAfterMs;
|
|
24
|
+
constructor(message, retryAfterMs, requestId) {
|
|
25
|
+
super(message, { code: "RATE_LIMITED", statusCode: 429, requestId, retryable: true });
|
|
26
|
+
this.name = "PalveronRateLimitError";
|
|
27
|
+
this.retryAfterMs = retryAfterMs;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var PalveronValidationError = class extends PalveronError {
|
|
31
|
+
field;
|
|
32
|
+
constructor(message, field, requestId) {
|
|
33
|
+
super(message, { code: "VALIDATION_ERROR", statusCode: 400, requestId, retryable: false });
|
|
34
|
+
this.name = "PalveronValidationError";
|
|
35
|
+
this.field = field ?? null;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var PalveronCircuitOpenError = class extends PalveronError {
|
|
39
|
+
constructor() {
|
|
40
|
+
super("Circuit breaker is open \u2014 too many consecutive failures. Retry later.", {
|
|
41
|
+
code: "CIRCUIT_OPEN",
|
|
42
|
+
statusCode: 503,
|
|
43
|
+
retryable: false
|
|
44
|
+
});
|
|
45
|
+
this.name = "PalveronCircuitOpenError";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var PalveronTimeoutError = class extends PalveronError {
|
|
49
|
+
constructor(timeoutMs, requestId) {
|
|
50
|
+
super(`Request timed out after ${timeoutMs}ms`, {
|
|
51
|
+
code: "TIMEOUT",
|
|
52
|
+
statusCode: 408,
|
|
53
|
+
requestId,
|
|
54
|
+
retryable: true
|
|
55
|
+
});
|
|
56
|
+
this.name = "PalveronTimeoutError";
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var CircuitBreaker = class {
|
|
60
|
+
constructor(threshold, cooldownMs) {
|
|
61
|
+
this.threshold = threshold;
|
|
62
|
+
this.cooldownMs = cooldownMs;
|
|
63
|
+
}
|
|
64
|
+
threshold;
|
|
65
|
+
cooldownMs;
|
|
66
|
+
failures = 0;
|
|
67
|
+
lastFailure = 0;
|
|
68
|
+
state = "closed";
|
|
69
|
+
canRequest() {
|
|
70
|
+
if (this.state === "closed") return true;
|
|
71
|
+
if (this.state === "open") {
|
|
72
|
+
if (Date.now() - this.lastFailure >= this.cooldownMs) {
|
|
73
|
+
this.state = "half-open";
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
onSuccess() {
|
|
81
|
+
this.failures = 0;
|
|
82
|
+
this.state = "closed";
|
|
83
|
+
}
|
|
84
|
+
onFailure() {
|
|
85
|
+
this.failures++;
|
|
86
|
+
this.lastFailure = Date.now();
|
|
87
|
+
if (this.failures >= this.threshold) {
|
|
88
|
+
this.state = "open";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
getState() {
|
|
92
|
+
return this.state;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var DEFAULT_BASE_URL = "https://gateway.palveron.com";
|
|
96
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
97
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
98
|
+
var DEFAULT_RETRY_BASE_DELAY = 500;
|
|
99
|
+
var SDK_VERSION = "1.0.0";
|
|
100
|
+
var Palveron = class {
|
|
101
|
+
config;
|
|
102
|
+
circuit;
|
|
103
|
+
constructor(config) {
|
|
104
|
+
if (!config.apiKey) throw new PalveronValidationError("apiKey is required");
|
|
105
|
+
this.config = {
|
|
106
|
+
apiKey: config.apiKey,
|
|
107
|
+
baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
|
|
108
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT,
|
|
109
|
+
maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
110
|
+
retryBaseDelay: config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY,
|
|
111
|
+
logger: config.logger,
|
|
112
|
+
headers: config.headers
|
|
113
|
+
};
|
|
114
|
+
this.circuit = new CircuitBreaker(
|
|
115
|
+
config.circuitBreakerThreshold ?? 5,
|
|
116
|
+
config.circuitBreakerCooldown ?? 3e4
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
// ── Core: Verify ────────────────────────────────────────
|
|
120
|
+
/**
|
|
121
|
+
* Send a governance verification request.
|
|
122
|
+
* This is the primary method — every LLM call should go through this.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const result = await palveron.verify({ prompt: 'User input here' });
|
|
127
|
+
* if (result.decision === 'BLOCKED') {
|
|
128
|
+
* throw new Error(result.reason);
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
async verify(request) {
|
|
133
|
+
const body = {
|
|
134
|
+
prompt: request.prompt,
|
|
135
|
+
extracted_text: request.extractedText,
|
|
136
|
+
metadata: request.metadata,
|
|
137
|
+
attachments: request.attachments?.map((a) => ({
|
|
138
|
+
content_type: a.contentType,
|
|
139
|
+
data: a.data,
|
|
140
|
+
filename: a.filename,
|
|
141
|
+
metadata: a.metadata
|
|
142
|
+
})),
|
|
143
|
+
context: request.context ? {
|
|
144
|
+
mcp_server: request.context.mcpServer,
|
|
145
|
+
tool_name: request.context.toolName,
|
|
146
|
+
chain_depth: request.context.chainDepth,
|
|
147
|
+
source_system: request.context.sourceSystem,
|
|
148
|
+
session_id: request.context.sessionId
|
|
149
|
+
} : void 0
|
|
150
|
+
};
|
|
151
|
+
const start = Date.now();
|
|
152
|
+
const raw = await this.request("POST", "/api/v1/verify", body);
|
|
153
|
+
const latency = Date.now() - start;
|
|
154
|
+
return {
|
|
155
|
+
decision: raw.decision ?? "ERROR",
|
|
156
|
+
output: raw.output ?? "",
|
|
157
|
+
reason: raw.reason ?? "",
|
|
158
|
+
traceId: raw.trace_id ?? "",
|
|
159
|
+
integrityHash: raw.integrity_hash ?? "",
|
|
160
|
+
shouldAnchor: raw.should_anchor ?? false,
|
|
161
|
+
flareStatus: raw.flare_status ?? "",
|
|
162
|
+
flareTxHash: raw.flare_tx_hash ?? null,
|
|
163
|
+
contentType: raw.content_type ?? "text",
|
|
164
|
+
findings: raw.findings ?? [],
|
|
165
|
+
latencyMs: latency
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// ── Convenience: Quick verify (string-only) ─────────────
|
|
169
|
+
/**
|
|
170
|
+
* Quick verification for text-only prompts.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* const result = await palveron.check('Is this prompt safe?');
|
|
175
|
+
* console.log(result.decision); // 'ALLOWED'
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
async check(prompt) {
|
|
179
|
+
return this.verify({ prompt });
|
|
180
|
+
}
|
|
181
|
+
// ── Convenience: Verify with file ───────────────────────
|
|
182
|
+
/**
|
|
183
|
+
* Verify a prompt with a file attachment.
|
|
184
|
+
* Reads the file, Base64-encodes it, and sends it with the correct MIME type.
|
|
185
|
+
* Node.js only — use verify() with pre-encoded data in browsers.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const result = await palveron.verifyWithFile(
|
|
190
|
+
* 'Analyze this document',
|
|
191
|
+
* '/path/to/report.pdf'
|
|
192
|
+
* );
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
async verifyWithFile(prompt, filePath) {
|
|
196
|
+
const { readFile } = await import("fs/promises");
|
|
197
|
+
const { basename } = await import("path");
|
|
198
|
+
const buffer = await readFile(filePath);
|
|
199
|
+
const base64 = buffer.toString("base64");
|
|
200
|
+
const filename = basename(filePath);
|
|
201
|
+
const contentType = this.inferMimeType(filename);
|
|
202
|
+
return this.verify({
|
|
203
|
+
prompt,
|
|
204
|
+
attachments: [{ contentType, data: base64, filename }]
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// ── Policies ────────────────────────────────────────────
|
|
208
|
+
/**
|
|
209
|
+
* List all active policies for the project.
|
|
210
|
+
*/
|
|
211
|
+
async listPolicies(env = "prod") {
|
|
212
|
+
return this.request("GET", `/api/v1/policies?env=${env}`);
|
|
213
|
+
}
|
|
214
|
+
// ── Health ──────────────────────────────────────────────
|
|
215
|
+
/**
|
|
216
|
+
* Check gateway health status.
|
|
217
|
+
*/
|
|
218
|
+
async health() {
|
|
219
|
+
return this.request("GET", "/health");
|
|
220
|
+
}
|
|
221
|
+
// ── Diagnostics ─────────────────────────────────────────
|
|
222
|
+
/**
|
|
223
|
+
* Get SDK and connection diagnostics.
|
|
224
|
+
*/
|
|
225
|
+
diagnostics() {
|
|
226
|
+
return {
|
|
227
|
+
sdkVersion: SDK_VERSION,
|
|
228
|
+
baseUrl: this.config.baseUrl,
|
|
229
|
+
timeout: this.config.timeout,
|
|
230
|
+
maxRetries: this.config.maxRetries,
|
|
231
|
+
circuitState: this.circuit.getState()
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// ─── Internal: HTTP with retry + circuit breaker ────────
|
|
235
|
+
async request(method, path, body) {
|
|
236
|
+
if (!this.circuit.canRequest()) {
|
|
237
|
+
throw new PalveronCircuitOpenError();
|
|
238
|
+
}
|
|
239
|
+
let lastError = null;
|
|
240
|
+
const maxAttempts = this.config.maxRetries + 1;
|
|
241
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
242
|
+
if (attempt > 0) {
|
|
243
|
+
const delay = this.backoffDelay(attempt);
|
|
244
|
+
this.config.logger?.debug(`Retry attempt ${attempt}/${this.config.maxRetries}`, { delay, path });
|
|
245
|
+
await this.sleep(delay);
|
|
246
|
+
}
|
|
247
|
+
const requestId = this.generateRequestId();
|
|
248
|
+
try {
|
|
249
|
+
const controller = new AbortController();
|
|
250
|
+
const timer = setTimeout(() => controller.abort(), this.config.timeout);
|
|
251
|
+
const headers = {
|
|
252
|
+
"Authorization": `Bearer ${this.config.apiKey}`,
|
|
253
|
+
"Content-Type": "application/json",
|
|
254
|
+
"Accept": "application/json",
|
|
255
|
+
"User-Agent": `palveron-sdk-typescript/${SDK_VERSION}`,
|
|
256
|
+
"X-Request-ID": requestId,
|
|
257
|
+
...this.config.headers
|
|
258
|
+
};
|
|
259
|
+
const response = await fetch(`${this.config.baseUrl}${path}`, {
|
|
260
|
+
method,
|
|
261
|
+
headers,
|
|
262
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
263
|
+
signal: controller.signal
|
|
264
|
+
});
|
|
265
|
+
clearTimeout(timer);
|
|
266
|
+
const responseRequestId = response.headers.get("x-request-id") ?? requestId;
|
|
267
|
+
if (response.ok) {
|
|
268
|
+
this.circuit.onSuccess();
|
|
269
|
+
return await response.json();
|
|
270
|
+
}
|
|
271
|
+
if (response.status === 401) {
|
|
272
|
+
this.circuit.onSuccess();
|
|
273
|
+
throw new PalveronAuthenticationError(
|
|
274
|
+
"Invalid API key or expired token",
|
|
275
|
+
responseRequestId
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (response.status === 429) {
|
|
279
|
+
const retryAfter = parseInt(response.headers.get("retry-after") ?? "5", 10) * 1e3;
|
|
280
|
+
throw new PalveronRateLimitError(
|
|
281
|
+
"Rate limit exceeded",
|
|
282
|
+
retryAfter,
|
|
283
|
+
responseRequestId
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (response.status === 400) {
|
|
287
|
+
const errorBody2 = await response.json().catch(() => ({}));
|
|
288
|
+
throw new PalveronValidationError(
|
|
289
|
+
errorBody2.error ?? "Invalid request",
|
|
290
|
+
errorBody2.field,
|
|
291
|
+
responseRequestId
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
if (response.status >= 500) {
|
|
295
|
+
this.circuit.onFailure();
|
|
296
|
+
const errorBody2 = await response.text().catch(() => "");
|
|
297
|
+
lastError = new PalveronError(
|
|
298
|
+
`Server error: ${response.status} ${response.statusText}`,
|
|
299
|
+
{ code: "SERVER_ERROR", statusCode: response.status, requestId: responseRequestId, retryable: true }
|
|
300
|
+
);
|
|
301
|
+
this.config.logger?.warn(`Server error on attempt ${attempt + 1}`, {
|
|
302
|
+
status: response.status,
|
|
303
|
+
requestId: responseRequestId,
|
|
304
|
+
body: errorBody2.slice(0, 200)
|
|
305
|
+
});
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
309
|
+
throw new PalveronError(
|
|
310
|
+
errorBody.error ?? `HTTP ${response.status}`,
|
|
311
|
+
{ code: "CLIENT_ERROR", statusCode: response.status, requestId: responseRequestId, retryable: false }
|
|
312
|
+
);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
if (error instanceof PalveronError && !error.retryable) throw error;
|
|
315
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
316
|
+
this.circuit.onFailure();
|
|
317
|
+
lastError = new PalveronTimeoutError(this.config.timeout, requestId);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
321
|
+
this.circuit.onFailure();
|
|
322
|
+
lastError = new PalveronError("Network error \u2014 could not reach gateway", {
|
|
323
|
+
code: "NETWORK_ERROR",
|
|
324
|
+
statusCode: 0,
|
|
325
|
+
requestId,
|
|
326
|
+
retryable: true
|
|
327
|
+
});
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (error instanceof PalveronError) {
|
|
331
|
+
lastError = error;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
throw lastError ?? new PalveronError("Max retries exceeded", {
|
|
338
|
+
code: "MAX_RETRIES",
|
|
339
|
+
statusCode: 0,
|
|
340
|
+
retryable: false
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
// ─── Helpers ────────────────────────────────────────────
|
|
344
|
+
backoffDelay(attempt) {
|
|
345
|
+
const base = this.config.retryBaseDelay * Math.pow(2, attempt - 1);
|
|
346
|
+
const jitter = base * 0.2 * Math.random();
|
|
347
|
+
return Math.min(base + jitter, 3e4);
|
|
348
|
+
}
|
|
349
|
+
sleep(ms) {
|
|
350
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
351
|
+
}
|
|
352
|
+
generateRequestId() {
|
|
353
|
+
const ts = Date.now().toString(36);
|
|
354
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
355
|
+
return `pv_${ts}_${rand}`;
|
|
356
|
+
}
|
|
357
|
+
inferMimeType(filename) {
|
|
358
|
+
const ext = filename.split(".").pop()?.toLowerCase();
|
|
359
|
+
const map = {
|
|
360
|
+
png: "image/png",
|
|
361
|
+
jpg: "image/jpeg",
|
|
362
|
+
jpeg: "image/jpeg",
|
|
363
|
+
gif: "image/gif",
|
|
364
|
+
webp: "image/webp",
|
|
365
|
+
svg: "image/svg+xml",
|
|
366
|
+
pdf: "application/pdf",
|
|
367
|
+
wav: "audio/wav",
|
|
368
|
+
mp3: "audio/mpeg",
|
|
369
|
+
ogg: "audio/ogg",
|
|
370
|
+
mp4: "video/mp4",
|
|
371
|
+
py: "text/x-python",
|
|
372
|
+
js: "text/javascript",
|
|
373
|
+
ts: "text/typescript",
|
|
374
|
+
rs: "text/x-rust",
|
|
375
|
+
go: "text/x-go",
|
|
376
|
+
java: "text/x-java",
|
|
377
|
+
c: "text/x-c",
|
|
378
|
+
cpp: "text/x-c++",
|
|
379
|
+
txt: "text/plain",
|
|
380
|
+
json: "application/json",
|
|
381
|
+
csv: "text/csv",
|
|
382
|
+
xml: "application/xml"
|
|
383
|
+
};
|
|
384
|
+
return map[ext ?? ""] ?? "application/octet-stream";
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
function createClient(config) {
|
|
388
|
+
return new Palveron(config);
|
|
389
|
+
}
|
|
390
|
+
var index_default = Palveron;
|
|
391
|
+
export {
|
|
392
|
+
Palveron,
|
|
393
|
+
PalveronAuthenticationError,
|
|
394
|
+
PalveronCircuitOpenError,
|
|
395
|
+
PalveronError,
|
|
396
|
+
PalveronRateLimitError,
|
|
397
|
+
PalveronTimeoutError,
|
|
398
|
+
PalveronValidationError,
|
|
399
|
+
createClient,
|
|
400
|
+
index_default as default
|
|
401
|
+
};
|
|
402
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// ============================================================\n// @palveron/sdk — Official TypeScript SDK for Palveron AI Governance\n// ============================================================\n// Zero dependencies. Works in Node.js 18+, Deno, Bun, Edge Runtimes.\n// ============================================================\n\n// ─── Types ──────────────────────────────────────────────────\n\nexport type Decision = 'ALLOWED' | 'BLOCKED' | 'MODIFIED' | 'ERROR';\nexport type RiskLevel = 'minimal' | 'limited' | 'high' | 'unacceptable';\nexport type Sensitivity = 'low' | 'medium' | 'high';\n\nexport interface PalveronConfig {\n /** API key (starts with pv_live_ or pv_test_) */\n apiKey: string;\n /** Gateway base URL (default: https://gateway.palveron.com) */\n baseUrl?: string;\n /** Request timeout in ms (default: 30000) */\n timeout?: number;\n /** Max retry attempts on transient failures (default: 3) */\n maxRetries?: number;\n /** Base delay for exponential backoff in ms (default: 500) */\n retryBaseDelay?: number;\n /** Custom logger (default: console) */\n logger?: PalveronLogger;\n /** Custom headers added to every request */\n headers?: Record<string, string>;\n /** Circuit breaker: max consecutive failures before opening (default: 5) */\n circuitBreakerThreshold?: number;\n /** Circuit breaker: cooldown in ms before half-open retry (default: 30000) */\n circuitBreakerCooldown?: number;\n}\n\nexport interface PalveronLogger {\n debug(message: string, meta?: Record<string, unknown>): void;\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n}\n\nexport interface Attachment {\n /** MIME type (e.g. \"image/png\", \"audio/wav\", \"application/pdf\") */\n contentType: string;\n /** Base64-encoded data */\n data: string;\n /** Optional filename */\n filename?: string;\n /** Optional per-attachment metadata (GPS, resolution, etc.) */\n metadata?: Record<string, unknown>;\n}\n\nexport interface RequestContext {\n /** MCP server URL if applicable */\n mcpServer?: string;\n /** MCP tool name */\n toolName?: string;\n /** Agent chain depth for recursive calls */\n chainDepth?: number;\n /** Source system identifier (\"ros2\", \"unity\", \"cursor-ide\", etc.) */\n sourceSystem?: string;\n /** Session ID for conversation tracking */\n sessionId?: string;\n}\n\nexport interface VerifyRequest {\n /** The prompt or input text to verify */\n prompt: string;\n /** Pre-extracted text from attachments (optional, server extracts if absent) */\n extractedText?: string;\n /** Arbitrary metadata passed through to the trace */\n metadata?: Record<string, unknown>;\n /** Multi-modal attachments (images, audio, documents, code) */\n attachments?: Attachment[];\n /** Agentic context (MCP, tool chains, source systems) */\n context?: RequestContext;\n}\n\nexport interface Finding {\n risk: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\n category: string;\n description: string;\n confidence: number;\n}\n\nexport interface VerifyResponse {\n /** Governance decision */\n decision: Decision;\n /** Modified/sanitized output (present when decision is MODIFIED) */\n output: string;\n /** Human-readable reason for the decision */\n reason: string;\n /** Unique trace ID for audit trail */\n traceId: string;\n /** SHA-256 integrity hash of the governance decision */\n integrityHash: string;\n /** Whether this trace will be anchored to Flare blockchain */\n shouldAnchor: boolean;\n /** Flare blockchain status */\n flareStatus: string;\n /** Flare transaction hash (populated after anchoring) */\n flareTxHash: string | null;\n /** Detected content type */\n contentType: string;\n /** Security findings (secrets, PII, policy violations) */\n findings: Finding[];\n /** Server-side latency in milliseconds */\n latencyMs: number;\n}\n\nexport interface PolicyListResponse {\n policies: Array<{\n id: string;\n name: string;\n prompt: string;\n environment: string;\n contentTypes: string[];\n createdAt: string;\n updatedAt: string;\n }>;\n}\n\nexport interface HealthResponse {\n status: 'healthy' | 'degraded' | 'unhealthy';\n version: string;\n uptime: number;\n checks: Record<string, { status: string; latencyMs: number }>;\n}\n\n// ─── Errors ─────────────────────────────────────────────────\n\nexport class PalveronError extends Error {\n public readonly code: string;\n public readonly statusCode: number;\n public readonly requestId: string | null;\n public readonly retryable: boolean;\n\n constructor(message: string, opts: {\n code: string;\n statusCode: number;\n requestId?: string | null;\n retryable?: boolean;\n }) {\n super(message);\n this.name = 'PalveronError';\n this.code = opts.code;\n this.statusCode = opts.statusCode;\n this.requestId = opts.requestId ?? null;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nexport class PalveronAuthenticationError extends PalveronError {\n constructor(message: string, requestId?: string | null) {\n super(message, { code: 'AUTHENTICATION_FAILED', statusCode: 401, requestId, retryable: false });\n this.name = 'PalveronAuthenticationError';\n }\n}\n\nexport class PalveronRateLimitError extends PalveronError {\n public readonly retryAfterMs: number;\n\n constructor(message: string, retryAfterMs: number, requestId?: string | null) {\n super(message, { code: 'RATE_LIMITED', statusCode: 429, requestId, retryable: true });\n this.name = 'PalveronRateLimitError';\n this.retryAfterMs = retryAfterMs;\n }\n}\n\nexport class PalveronValidationError extends PalveronError {\n public readonly field: string | null;\n\n constructor(message: string, field?: string, requestId?: string | null) {\n super(message, { code: 'VALIDATION_ERROR', statusCode: 400, requestId, retryable: false });\n this.name = 'PalveronValidationError';\n this.field = field ?? null;\n }\n}\n\nexport class PalveronCircuitOpenError extends PalveronError {\n constructor() {\n super('Circuit breaker is open — too many consecutive failures. Retry later.', {\n code: 'CIRCUIT_OPEN', statusCode: 503, retryable: false,\n });\n this.name = 'PalveronCircuitOpenError';\n }\n}\n\nexport class PalveronTimeoutError extends PalveronError {\n constructor(timeoutMs: number, requestId?: string | null) {\n super(`Request timed out after ${timeoutMs}ms`, {\n code: 'TIMEOUT', statusCode: 408, requestId, retryable: true,\n });\n this.name = 'PalveronTimeoutError';\n }\n}\n\n// ─── Circuit Breaker ────────────────────────────────────────\n\nclass CircuitBreaker {\n private failures = 0;\n private lastFailure = 0;\n private state: 'closed' | 'open' | 'half-open' = 'closed';\n\n constructor(\n private threshold: number,\n private cooldownMs: number,\n ) {}\n\n canRequest(): boolean {\n if (this.state === 'closed') return true;\n if (this.state === 'open') {\n if (Date.now() - this.lastFailure >= this.cooldownMs) {\n this.state = 'half-open';\n return true;\n }\n return false;\n }\n return true; // half-open: allow one request\n }\n\n onSuccess(): void {\n this.failures = 0;\n this.state = 'closed';\n }\n\n onFailure(): void {\n this.failures++;\n this.lastFailure = Date.now();\n if (this.failures >= this.threshold) {\n this.state = 'open';\n }\n }\n\n getState(): string {\n return this.state;\n }\n}\n\n// ─── Client ─────────────────────────────────────────────────\n\nconst DEFAULT_BASE_URL = 'https://gateway.palveron.com';\nconst DEFAULT_TIMEOUT = 30_000;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY = 500;\nconst SDK_VERSION = '1.0.0';\n\nexport class Palveron {\n private readonly config: Required<Pick<PalveronConfig,\n 'apiKey' | 'baseUrl' | 'timeout' | 'maxRetries' | 'retryBaseDelay'\n >> & Pick<PalveronConfig, 'logger' | 'headers'>;\n\n private readonly circuit: CircuitBreaker;\n\n constructor(config: PalveronConfig) {\n if (!config.apiKey) throw new PalveronValidationError('apiKey is required');\n\n this.config = {\n apiKey: config.apiKey,\n baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, ''),\n timeout: config.timeout ?? DEFAULT_TIMEOUT,\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n retryBaseDelay: config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY,\n logger: config.logger,\n headers: config.headers,\n };\n\n this.circuit = new CircuitBreaker(\n config.circuitBreakerThreshold ?? 5,\n config.circuitBreakerCooldown ?? 30_000,\n );\n }\n\n // ── Core: Verify ────────────────────────────────────────\n\n /**\n * Send a governance verification request.\n * This is the primary method — every LLM call should go through this.\n *\n * @example\n * ```typescript\n * const result = await palveron.verify({ prompt: 'User input here' });\n * if (result.decision === 'BLOCKED') {\n * throw new Error(result.reason);\n * }\n * ```\n */\n async verify(request: VerifyRequest): Promise<VerifyResponse> {\n const body = {\n prompt: request.prompt,\n extracted_text: request.extractedText,\n metadata: request.metadata,\n attachments: request.attachments?.map(a => ({\n content_type: a.contentType,\n data: a.data,\n filename: a.filename,\n metadata: a.metadata,\n })),\n context: request.context ? {\n mcp_server: request.context.mcpServer,\n tool_name: request.context.toolName,\n chain_depth: request.context.chainDepth,\n source_system: request.context.sourceSystem,\n session_id: request.context.sessionId,\n } : undefined,\n };\n\n const start = Date.now();\n const raw = await this.request<Record<string, unknown>>('POST', '/api/v1/verify', body);\n const latency = Date.now() - start;\n\n return {\n decision: (raw.decision as Decision) ?? 'ERROR',\n output: (raw.output as string) ?? '',\n reason: (raw.reason as string) ?? '',\n traceId: (raw.trace_id as string) ?? '',\n integrityHash: (raw.integrity_hash as string) ?? '',\n shouldAnchor: (raw.should_anchor as boolean) ?? false,\n flareStatus: (raw.flare_status as string) ?? '',\n flareTxHash: (raw.flare_tx_hash as string | null) ?? null,\n contentType: (raw.content_type as string) ?? 'text',\n findings: (raw.findings as Finding[]) ?? [],\n latencyMs: latency,\n };\n }\n\n // ── Convenience: Quick verify (string-only) ─────────────\n\n /**\n * Quick verification for text-only prompts.\n *\n * @example\n * ```typescript\n * const result = await palveron.check('Is this prompt safe?');\n * console.log(result.decision); // 'ALLOWED'\n * ```\n */\n async check(prompt: string): Promise<VerifyResponse> {\n return this.verify({ prompt });\n }\n\n // ── Convenience: Verify with file ───────────────────────\n\n /**\n * Verify a prompt with a file attachment.\n * Reads the file, Base64-encodes it, and sends it with the correct MIME type.\n * Node.js only — use verify() with pre-encoded data in browsers.\n *\n * @example\n * ```typescript\n * const result = await palveron.verifyWithFile(\n * 'Analyze this document',\n * '/path/to/report.pdf'\n * );\n * ```\n */\n async verifyWithFile(prompt: string, filePath: string): Promise<VerifyResponse> {\n // Dynamic import to keep SDK isomorphic\n const { readFile } = await import('node:fs/promises');\n const { basename } = await import('node:path');\n\n const buffer = await readFile(filePath);\n const base64 = buffer.toString('base64');\n const filename = basename(filePath);\n const contentType = this.inferMimeType(filename);\n\n return this.verify({\n prompt,\n attachments: [{ contentType, data: base64, filename }],\n });\n }\n\n // ── Policies ────────────────────────────────────────────\n\n /**\n * List all active policies for the project.\n */\n async listPolicies(env: string = 'prod'): Promise<PolicyListResponse> {\n return this.request<PolicyListResponse>('GET', `/api/v1/policies?env=${env}`);\n }\n\n // ── Health ──────────────────────────────────────────────\n\n /**\n * Check gateway health status.\n */\n async health(): Promise<HealthResponse> {\n return this.request<HealthResponse>('GET', '/health');\n }\n\n // ── Diagnostics ─────────────────────────────────────────\n\n /**\n * Get SDK and connection diagnostics.\n */\n diagnostics(): {\n sdkVersion: string;\n baseUrl: string;\n timeout: number;\n maxRetries: number;\n circuitState: string;\n } {\n return {\n sdkVersion: SDK_VERSION,\n baseUrl: this.config.baseUrl,\n timeout: this.config.timeout,\n maxRetries: this.config.maxRetries,\n circuitState: this.circuit.getState(),\n };\n }\n\n // ─── Internal: HTTP with retry + circuit breaker ────────\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n if (!this.circuit.canRequest()) {\n throw new PalveronCircuitOpenError();\n }\n\n let lastError: Error | null = null;\n const maxAttempts = this.config.maxRetries + 1;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (attempt > 0) {\n const delay = this.backoffDelay(attempt);\n this.config.logger?.debug(`Retry attempt ${attempt}/${this.config.maxRetries}`, { delay, path });\n await this.sleep(delay);\n }\n\n const requestId = this.generateRequestId();\n\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeout);\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'User-Agent': `palveron-sdk-typescript/${SDK_VERSION}`,\n 'X-Request-ID': requestId,\n ...this.config.headers,\n };\n\n const response = await fetch(`${this.config.baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n const responseRequestId = response.headers.get('x-request-id') ?? requestId;\n\n if (response.ok) {\n this.circuit.onSuccess();\n return await response.json() as T;\n }\n\n // Handle specific error codes\n if (response.status === 401) {\n this.circuit.onSuccess(); // auth errors are not circuit failures\n throw new PalveronAuthenticationError(\n 'Invalid API key or expired token',\n responseRequestId,\n );\n }\n\n if (response.status === 429) {\n const retryAfter = parseInt(response.headers.get('retry-after') ?? '5', 10) * 1000;\n throw new PalveronRateLimitError(\n 'Rate limit exceeded',\n retryAfter,\n responseRequestId,\n );\n }\n\n if (response.status === 400) {\n const errorBody = await response.json().catch(() => ({})) as Record<string, string>;\n throw new PalveronValidationError(\n errorBody.error ?? 'Invalid request',\n errorBody.field,\n responseRequestId,\n );\n }\n\n // Retryable server errors (500, 502, 503)\n if (response.status >= 500) {\n this.circuit.onFailure();\n const errorBody = await response.text().catch(() => '');\n lastError = new PalveronError(\n `Server error: ${response.status} ${response.statusText}`,\n { code: 'SERVER_ERROR', statusCode: response.status, requestId: responseRequestId, retryable: true },\n );\n this.config.logger?.warn(`Server error on attempt ${attempt + 1}`, {\n status: response.status,\n requestId: responseRequestId,\n body: errorBody.slice(0, 200),\n });\n continue; // retry\n }\n\n // Non-retryable client errors\n const errorBody = await response.json().catch(() => ({})) as Record<string, string>;\n throw new PalveronError(\n errorBody.error ?? `HTTP ${response.status}`,\n { code: 'CLIENT_ERROR', statusCode: response.status, requestId: responseRequestId, retryable: false },\n );\n\n } catch (error) {\n if (error instanceof PalveronError && !error.retryable) throw error;\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n this.circuit.onFailure();\n lastError = new PalveronTimeoutError(this.config.timeout, requestId);\n continue;\n }\n\n if (error instanceof TypeError && error.message.includes('fetch')) {\n this.circuit.onFailure();\n lastError = new PalveronError('Network error — could not reach gateway', {\n code: 'NETWORK_ERROR', statusCode: 0, requestId, retryable: true,\n });\n continue;\n }\n\n if (error instanceof PalveronError) {\n lastError = error;\n continue;\n }\n\n throw error;\n }\n }\n\n throw lastError ?? new PalveronError('Max retries exceeded', {\n code: 'MAX_RETRIES', statusCode: 0, retryable: false,\n });\n }\n\n // ─── Helpers ────────────────────────────────────────────\n\n private backoffDelay(attempt: number): number {\n const base = this.config.retryBaseDelay * Math.pow(2, attempt - 1);\n const jitter = base * 0.2 * Math.random();\n return Math.min(base + jitter, 30_000);\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private generateRequestId(): string {\n const ts = Date.now().toString(36);\n const rand = Math.random().toString(36).slice(2, 8);\n return `pv_${ts}_${rand}`;\n }\n\n private inferMimeType(filename: string): string {\n const ext = filename.split('.').pop()?.toLowerCase();\n const map: Record<string, string> = {\n png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif',\n webp: 'image/webp', svg: 'image/svg+xml', pdf: 'application/pdf',\n wav: 'audio/wav', mp3: 'audio/mpeg', ogg: 'audio/ogg', mp4: 'video/mp4',\n py: 'text/x-python', js: 'text/javascript', ts: 'text/typescript',\n rs: 'text/x-rust', go: 'text/x-go', java: 'text/x-java',\n c: 'text/x-c', cpp: 'text/x-c++', txt: 'text/plain',\n json: 'application/json', csv: 'text/csv', xml: 'application/xml',\n };\n return map[ext ?? ''] ?? 'application/octet-stream';\n }\n}\n\n// ─── Factory ──────────────────────────────────────────────\n\n/**\n * Create a Palveron client instance.\n *\n * @example\n * ```typescript\n * import { createClient } from '@palveron/sdk';\n *\n * const palveron = createClient({\n * apiKey: process.env.PALVERON_API_KEY!,\n * baseUrl: 'https://gateway.acme.corp:8080', // on-prem\n * });\n *\n * const result = await palveron.verify({ prompt: userInput });\n * ```\n */\nexport function createClient(config: PalveronConfig): Palveron {\n return new Palveron(config);\n}\n\n// ─── Re-exports ─────────────────────────────────────────\n\nexport default Palveron;\n"],"mappings":"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","names":["errorBody"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@palveron/sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Palveron TypeScript/JavaScript SDK — AI Governance Gateway client for policy enforcement, trace verification, and compliance automation.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"CHANGELOG.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean --sourcemap",
|
|
23
|
+
"test": "vitest run --passWithNoTests",
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"lint": "eslint src/",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"palveron",
|
|
31
|
+
"ai-governance",
|
|
32
|
+
"guardrails",
|
|
33
|
+
"eu-ai-act",
|
|
34
|
+
"compliance",
|
|
35
|
+
"llm",
|
|
36
|
+
"agent-governance",
|
|
37
|
+
"policy-enforcement",
|
|
38
|
+
"blockchain-attestation",
|
|
39
|
+
"dora",
|
|
40
|
+
"gdpr",
|
|
41
|
+
"nist",
|
|
42
|
+
"audit-trail",
|
|
43
|
+
"pii-detection",
|
|
44
|
+
"mcp",
|
|
45
|
+
"agentic-ai"
|
|
46
|
+
],
|
|
47
|
+
"author": "Palveron <hello@palveron.com> (https://palveron.com)",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/palveron/sdk-typescript.git"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://docs.palveron.com/sdks",
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/palveron/sdk-typescript/issues"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=18"
|
|
62
|
+
},
|
|
63
|
+
"sideEffects": false,
|
|
64
|
+
"dependencies": {},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/node": "^20.0.0",
|
|
67
|
+
"tsup": "^8.0.0",
|
|
68
|
+
"typescript": "^5.5.0",
|
|
69
|
+
"vitest": "^2.0.0"
|
|
70
|
+
}
|
|
71
|
+
}
|