@altsafe/aidirector 1.2.0 → 1.4.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 AI Director
3
+ Copyright (c) 2026 Hydra
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,27 +1,35 @@
1
- # @altsafe/aidirector - Client SDK
1
+ # hydra-aidirector - Client SDK
2
+ # hydra-aidirector
2
3
 
3
- Production-grade TypeScript SDK for the AI Director API gateway.
4
+ The official Node.js/TypeScript client for [Hydra](https://hydrai.dev).
5
+
6
+ Hydra is a high-performance AI API gateway that provides:
7
+ - 🔄 **Automatic Failover**: Never let an LLM outage break your app
8
+ - ⚡ **God-Tier Caching**: Reduce costs and latency with smart response caching
9
+ - 🛡️ **Self-Healing AI**: Auto-extract JSON, strip markdown, and repair malformed responses with `healingReport`
10
+ - 🧠 **Thinking Mode**: Support for reasoning models like Gemini 2.0 Flash Thinking
11
+ - 📊 **Detailed Usage**: Track token usage, latency, and costs per model
4
12
 
5
13
  ## Installation
6
14
 
7
15
  ```bash
8
- npm install @altsafe/aidirector
16
+ npm install hydra-aidirector
9
17
  # or
10
- pnpm add @altsafe/aidirector
18
+ pnpm add hydra-aidirector
11
19
  ```
12
20
 
13
21
  ## Quick Start
14
22
 
15
23
  ```typescript
16
- import { AIDirector } from '@altsafe/aidirector';
24
+ import { Hydra } from 'hydra-aidirector';
17
25
 
18
- const client = new AIDirector({
19
- secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
26
+ const ai = new Hydra({
27
+ secretKey: process.env.HYDRA_SECRET_KEY!,
20
28
  baseUrl: 'https://your-instance.vercel.app',
21
29
  });
22
30
 
23
31
  // Generate content
24
- const result = await client.generate({
32
+ const result = await ai.generate({
25
33
  chainId: 'my-chain',
26
34
  prompt: 'Generate 5 user profiles',
27
35
  });
@@ -37,8 +45,10 @@ if (result.success) {
37
45
  - ⚡ **3-Step Architecture** - Token → Worker → Complete (minimizes costs)
38
46
  - 📎 **File Attachments** - Upload and process documents
39
47
  - 🔄 **Automatic Retries** - Exponential backoff on failures
40
- - 💾 **Smart Caching** - Hybrid Redis caching with 7-day TTL
41
- - 🎯 **TypeScript** - Full type safety
48
+ - 💾 **Smart Caching** - Two-tier (user/global) cache with AI-directed scoping
49
+ - 🎯 **TypeScript** - Full type safety with comprehensive types
50
+ - 🛑 **Request Cancellation** - Support for AbortSignal
51
+ - 🪝 **Webhooks** - Async notification callbacks
42
52
 
43
53
  ## Streaming (Recommended for Large Responses)
44
54
 
@@ -63,6 +73,64 @@ await client.generateStream(
63
73
  );
64
74
  ```
65
75
 
76
+ ## Batch Generation
77
+
78
+ ```typescript
79
+ const result = await client.generateBatch('my-chain', [
80
+ { id: 'item1', prompt: 'Describe product A' },
81
+ { id: 'item2', prompt: 'Describe product B' },
82
+ { id: 'item3', prompt: 'Describe product C' },
83
+ ]);
84
+
85
+ console.log(`Processed ${result.summary.succeeded}/${result.summary.total}`);
86
+ ```
87
+
88
+ ## Request Cancellation
89
+
90
+ ```typescript
91
+ const controller = new AbortController();
92
+
93
+ // Cancel after 5 seconds
94
+ setTimeout(() => controller.abort(), 5000);
95
+
96
+ try {
97
+ const result = await client.generate({
98
+ chainId: 'my-chain',
99
+ prompt: 'Long running prompt',
100
+ signal: controller.signal,
101
+ });
102
+ } catch (error) {
103
+ if (error instanceof TimeoutError) {
104
+ console.log('Request was cancelled');
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Cache Control
110
+
111
+ ```typescript
112
+ // Global cache (shared across users - default)
113
+ const result = await client.generate({
114
+ chainId: 'my-chain',
115
+ prompt: 'Static content',
116
+ cacheScope: 'global',
117
+ });
118
+
119
+ // User-scoped cache (private to user)
120
+ const userResult = await client.generate({
121
+ chainId: 'my-chain',
122
+ prompt: 'Personalized content',
123
+ cacheScope: 'user',
124
+ });
125
+
126
+ // Skip cache entirely
127
+ const freshResult = await client.generate({
128
+ chainId: 'my-chain',
129
+ prompt: 'Always fresh',
130
+ cacheScope: 'skip',
131
+ });
132
+ ```
133
+
66
134
  ## File Attachments
67
135
 
68
136
  ```typescript
@@ -81,16 +149,62 @@ const result = await client.generate({
81
149
  });
82
150
  ```
83
151
 
152
+ ## Webhooks
153
+
154
+ ```typescript
155
+ // Register a webhook
156
+ await client.registerWebhook({
157
+ requestId: 'req_123',
158
+ url: 'https://your-domain.com/webhooks/hydra',
159
+ secret: 'your-webhook-secret',
160
+ retryCount: 3,
161
+ });
162
+
163
+ // List webhooks
164
+ const webhooks = await client.listWebhooks();
165
+
166
+ // Update webhook
167
+ await client.updateWebhook('webhook_id', { retryCount: 5 });
168
+
169
+ // Unregister webhook
170
+ await client.unregisterWebhook('webhook_id');
171
+ ```
172
+
173
+ ## Thinking Mode (Reasoning Models)
174
+
175
+ ```typescript
176
+ const result = await client.generate({
177
+ chainId: 'reasoning-chain',
178
+ prompt: 'Solve this complex problem step by step',
179
+ options: {
180
+ thinkingMode: true, // Shows model reasoning process
181
+ },
182
+ });
183
+ ```
184
+
84
185
  ## Configuration
85
186
 
86
187
  | Option | Type | Default | Description |
87
188
  |--------|------|---------|-------------|
88
- | `secretKey` | `string` | **required** | Your API key (`aid_sk_...`) |
189
+ | `secretKey` | `string` | **required** | Your API key (`hyd_sk_...`) |
89
190
  | `baseUrl` | `string` | `http://localhost:3000` | API base URL |
90
191
  | `timeout` | `number` | `600000` | Request timeout (10 min) |
91
192
  | `maxRetries` | `number` | `3` | Max retry attempts |
92
193
  | `debug` | `boolean` | `false` | Enable debug logging |
93
- | `useOptimized` | `boolean` | `true` | Use 3-step Worker flow |
194
+
195
+ ## Generate Options
196
+
197
+ | Option | Type | Default | Description |
198
+ |--------|------|---------|-------------|
199
+ | `chainId` | `string` | **required** | Fallback chain ID |
200
+ | `prompt` | `string` | **required** | The prompt to send |
201
+ | `schema` | `object` | - | JSON schema for validation |
202
+ | `cacheScope` | `'global' \| 'user' \| 'skip'` | `'global'` | Cache behavior |
203
+ | `signal` | `AbortSignal` | - | Cancellation signal |
204
+ | `maxRetries` | `number` | Client setting | Override retries |
205
+ | `requestId` | `string` | Auto-generated | Custom request ID |
206
+ | `files` | `FileAttachment[]` | - | File attachments |
207
+ | `useOptimized` | `boolean` | `true` | Use 3-step flow |
94
208
 
95
209
  ## API Methods
96
210
 
@@ -98,10 +212,16 @@ const result = await client.generate({
98
212
  |--------|-------------|
99
213
  | `generate(options)` | Generate content with fallback chain |
100
214
  | `generateStream(options, callbacks)` | Stream JSON objects in real-time |
215
+ | `generateBatch(chainId, items)` | Process multiple prompts |
101
216
  | `listModels()` | List available AI models |
102
217
  | `listChains()` | List your fallback chains |
103
- | `getUsageStats(options)` | Get usage statistics |
104
- | `healthCheck()` | Check API health |
218
+ | `getUsage(options)` | Get usage statistics |
219
+ | `health()` | Check API health |
220
+ | `healthDetailed()` | Get detailed component health |
221
+ | `registerWebhook(config)` | Register async webhook |
222
+ | `unregisterWebhook(id)` | Remove a webhook |
223
+ | `listWebhooks()` | List all webhooks |
224
+ | `updateWebhook(id, updates)` | Modify webhook config |
105
225
 
106
226
  ## Error Handling
107
227
 
@@ -110,21 +230,62 @@ import {
110
230
  RateLimitError,
111
231
  TimeoutError,
112
232
  AuthenticationError,
113
- } from '@altsafe/aidirector';
233
+ QuotaExceededError,
234
+ WorkerError,
235
+ FileProcessingError,
236
+ } from 'hydra-aidirector';
114
237
 
115
238
  try {
116
239
  const result = await client.generate({ ... });
117
240
  } catch (error) {
118
241
  if (error instanceof RateLimitError) {
119
242
  console.log(`Retry after ${error.retryAfterMs}ms`);
243
+ } else if (error instanceof QuotaExceededError) {
244
+ console.log(`Quota exceeded: ${error.used}/${error.limit} (${error.tier})`);
120
245
  } else if (error instanceof TimeoutError) {
121
- console.log('Request timed out');
246
+ console.log(`Request timed out after ${error.timeoutMs}ms`);
122
247
  } else if (error instanceof AuthenticationError) {
123
248
  console.log('Invalid API key');
249
+ } else if (error instanceof WorkerError) {
250
+ console.log('Worker processing failed - will retry');
251
+ } else if (error instanceof FileProcessingError) {
252
+ console.log(`File error: ${error.reason} - ${error.filename}`);
124
253
  }
125
254
  }
126
255
  ```
127
256
 
257
+ ## Self-Healing Reports
258
+
259
+ When `hydra-aidirector` fixes a malformed JSON response, it includes a `healingReport` in the data object:
260
+
261
+ ```typescript
262
+ const result = await client.generate({ ... });
263
+
264
+ if (result.success && result.meta.recovered) {
265
+ console.log('JSON was malformed but healed!');
266
+ console.log(result.data.healingReport);
267
+ // [{ original: "{name: 'foo'", healed: {name: "foo"}, fixes: ["Added missing brace"] }]
268
+ }
269
+ ```
270
+
271
+ ## AI-Directed Caching
272
+
273
+ The AI can control caching by including a `_cache` directive in its JSON output:
274
+
275
+ ```json
276
+ {
277
+ "data": { ... },
278
+ "_cache": { "scope": "user" }
279
+ }
280
+ ```
281
+
282
+ Scopes:
283
+ - `global` - Share response across all users (default)
284
+ - `user` - Cache per-user only
285
+ - `skip` - Do not cache this response
286
+
287
+ The directive is automatically stripped from the final response.
288
+
128
289
  ## Pricing
129
290
 
130
291
  BYOK (Bring Your Own Key) - You pay for AI costs directly to providers.
package/dist/index.d.mts CHANGED
@@ -1,21 +1,21 @@
1
1
  /**
2
- * AI Director Client Types
2
+ * Hydra Client Types
3
3
  * Type definitions for the SDK
4
4
  */
5
5
  /**
6
- * Configuration for AI Director client
6
+ * Configuration for Hydra client
7
7
  */
8
- interface AIDirectorConfig {
8
+ interface HydraConfig {
9
9
  /**
10
- * Your AI Director secret key
11
- * Get this from your dashboard at https://aidirector.dev/dashboard/keys
12
- * Format: aid_sk_<random>
10
+ * Your Hydra secret key
11
+ * Get this from your dashboard at https://hydrai.dev/dashboard/keys
12
+ * Format: hyd_sk_<random>
13
13
  */
14
14
  secretKey: string;
15
15
  /**
16
16
  * API base URL
17
17
  * @default 'http://localhost:3000' in development
18
- * @example 'https://api.aidirector.dev'
18
+ * @example 'https://api.hydrai.dev'
19
19
  */
20
20
  baseUrl?: string;
21
21
  /**
@@ -92,6 +92,35 @@ interface GenerateOptions {
92
92
  * @default true
93
93
  */
94
94
  useOptimized?: boolean;
95
+ /**
96
+ * Cache scope for this request.
97
+ * - 'global': Cache is shared across all users (default)
98
+ * - 'user': Cache is scoped to the authenticated user
99
+ * - 'skip': Do not cache this response
100
+ * @default 'global'
101
+ */
102
+ cacheScope?: 'global' | 'user' | 'skip';
103
+ /**
104
+ * AbortSignal for request cancellation.
105
+ * Pass a signal from an AbortController to cancel the request.
106
+ * @example
107
+ * ```typescript
108
+ * const controller = new AbortController();
109
+ * setTimeout(() => controller.abort(), 5000);
110
+ * await client.generate({ chainId: 'x', prompt: 'y', signal: controller.signal });
111
+ * ```
112
+ */
113
+ signal?: AbortSignal;
114
+ /**
115
+ * Override max retries for this specific request.
116
+ * Set to 0 to disable retries for idempotent-sensitive operations.
117
+ */
118
+ maxRetries?: number;
119
+ /**
120
+ * Client-generated request ID for tracing and debugging.
121
+ * If not provided, one will be generated automatically.
122
+ */
123
+ requestId?: string;
95
124
  /**
96
125
  * Generation parameters
97
126
  */
@@ -142,6 +171,12 @@ interface GenerationParameters {
142
171
  * System prompt to prepend
143
172
  */
144
173
  systemPrompt?: string;
174
+ /**
175
+ * Enable thinking/reasoning mode for supported models.
176
+ * When enabled, the model will show its reasoning process.
177
+ * Only works with models that support thinking (e.g., Gemini 2.0 Flash Thinking).
178
+ */
179
+ thinkingMode?: boolean;
145
180
  }
146
181
  /**
147
182
  * Result from the generate method
@@ -180,6 +215,15 @@ interface GenerateData {
180
215
  * Raw AI response text (useful for debugging)
181
216
  */
182
217
  rawContent?: string;
218
+ /**
219
+ * Report of auto-healed validation errors
220
+ * detailed log of how the system fixed invalid AI output
221
+ */
222
+ healingReport?: Array<{
223
+ original: unknown;
224
+ healed: unknown;
225
+ fixes: string[];
226
+ }>;
183
227
  }
184
228
  /**
185
229
  * Generation metadata
@@ -579,9 +623,9 @@ interface WebhookConfig {
579
623
  }
580
624
 
581
625
  /**
582
- * AI Director Client SDK
626
+ * Hydra Client SDK
583
627
  *
584
- * Production-grade client for the AI Director API gateway.
628
+ * Production-grade client for the Hydra API gateway.
585
629
  * Use in Next.js API routes, Server Actions, or any Node.js/Edge environment.
586
630
  *
587
631
  * Features:
@@ -593,11 +637,11 @@ interface WebhookConfig {
593
637
  *
594
638
  * @example
595
639
  * ```typescript
596
- * import { AIDirector } from '@aidirector/client';
640
+ * import { Hydra } from 'hydra-aidirector';
597
641
  *
598
- * const client = new AIDirector({
599
- * secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
600
- * baseUrl: 'https://api.aidirector.dev',
642
+ * const client = new Hydra({
643
+ * secretKey: process.env.HYDRA_SECRET_KEY!,
644
+ * baseUrl: 'https://api.hydrai.dev',
601
645
  * });
602
646
  *
603
647
  * const result = await client.generate({
@@ -611,20 +655,20 @@ interface WebhookConfig {
611
655
  * }
612
656
  * ```
613
657
  */
614
- declare class AIDirector {
658
+ declare class Hydra {
615
659
  private readonly secretKey;
616
660
  private readonly baseUrl;
617
661
  private readonly timeout;
618
662
  private readonly maxRetries;
619
663
  private readonly keyPrefix;
620
664
  private readonly debug;
621
- constructor(config: AIDirectorConfig);
665
+ constructor(config: HydraConfig);
622
666
  /**
623
667
  * Generate content using your fallback chain
624
668
  *
625
669
  * @param options - Generation options
626
670
  * @returns Promise resolving to GenerateResult
627
- * @throws AIDirectorError subclasses on failure
671
+ * @throws HydraError subclasses on failure
628
672
  *
629
673
  * @example
630
674
  * ```typescript
@@ -711,9 +755,9 @@ declare class AIDirector {
711
755
  * Useful for testing or switching environments
712
756
  *
713
757
  * @param overrides - Configuration overrides
714
- * @returns New AIDirector instance
758
+ * @returns New Hydra instance
715
759
  */
716
- withConfig(overrides: Partial<AIDirectorConfig>): AIDirector;
760
+ withConfig(overrides: Partial<HydraConfig>): Hydra;
717
761
  /**
718
762
  * Register a webhook for async notifications
719
763
  *
@@ -729,6 +773,43 @@ declare class AIDirector {
729
773
  registered: boolean;
730
774
  message: string;
731
775
  }>;
776
+ /**
777
+ * Unregister a webhook
778
+ *
779
+ * @param webhookId - ID of the webhook to unregister
780
+ * @returns Unregistration result
781
+ */
782
+ unregisterWebhook(webhookId: string): Promise<{
783
+ unregistered: boolean;
784
+ message: string;
785
+ }>;
786
+ /**
787
+ * List all registered webhooks
788
+ *
789
+ * @returns Array of webhook configurations
790
+ */
791
+ listWebhooks(): Promise<Array<{
792
+ id: string;
793
+ requestId: string;
794
+ url: string;
795
+ status: 'pending' | 'delivered' | 'failed';
796
+ retryCount: number;
797
+ createdAt: string;
798
+ }>>;
799
+ /**
800
+ * Update a webhook configuration
801
+ *
802
+ * @param webhookId - ID of the webhook to update
803
+ * @param updates - Fields to update
804
+ * @returns Updated webhook info
805
+ */
806
+ updateWebhook(webhookId: string, updates: {
807
+ url?: string;
808
+ retryCount?: number;
809
+ }): Promise<{
810
+ updated: boolean;
811
+ message: string;
812
+ }>;
732
813
  /**
733
814
  * Get detailed health information including component status
734
815
  *
@@ -802,13 +883,13 @@ declare class AIDirector {
802
883
  }
803
884
 
804
885
  /**
805
- * AI Director Error Classes
886
+ * Hydra Error Classes
806
887
  * Structured error types for better error handling
807
888
  */
808
889
  /**
809
- * Base error class for AI Director errors
890
+ * Base error class for Hydra errors
810
891
  */
811
- declare class AIDirectorError extends Error {
892
+ declare class HydraError extends Error {
812
893
  readonly code: string;
813
894
  readonly retryable: boolean;
814
895
  readonly statusCode?: number;
@@ -822,59 +903,99 @@ declare class AIDirectorError extends Error {
822
903
  /**
823
904
  * Configuration error - invalid client setup
824
905
  */
825
- declare class ConfigurationError extends AIDirectorError {
906
+ declare class ConfigurationError extends HydraError {
826
907
  constructor(message: string);
827
908
  }
828
909
  /**
829
910
  * Authentication error - invalid or expired credentials
830
911
  */
831
- declare class AuthenticationError extends AIDirectorError {
912
+ declare class AuthenticationError extends HydraError {
832
913
  constructor(message: string, statusCode?: number);
833
914
  }
834
915
  /**
835
916
  * Rate limit error - too many requests
836
917
  */
837
- declare class RateLimitError extends AIDirectorError {
918
+ declare class RateLimitError extends HydraError {
838
919
  readonly retryAfterMs: number;
839
920
  constructor(message: string, retryAfterMs?: number);
840
921
  }
841
922
  /**
842
923
  * Timeout error - request took too long
843
924
  */
844
- declare class TimeoutError extends AIDirectorError {
925
+ declare class TimeoutError extends HydraError {
845
926
  readonly timeoutMs: number;
846
927
  constructor(timeoutMs: number);
847
928
  }
848
929
  /**
849
930
  * Network error - connection failed
850
931
  */
851
- declare class NetworkError extends AIDirectorError {
932
+ declare class NetworkError extends HydraError {
852
933
  constructor(message: string, cause?: Error);
853
934
  }
854
935
  /**
855
936
  * Chain error - fallback chain execution failed
856
937
  */
857
- declare class ChainExecutionError extends AIDirectorError {
938
+ declare class ChainExecutionError extends HydraError {
858
939
  readonly attemptedModels: string[];
859
940
  constructor(message: string, attemptedModels?: string[]);
860
941
  }
861
942
  /**
862
943
  * Validation error - schema validation failed
863
944
  */
864
- declare class ValidationError extends AIDirectorError {
945
+ declare class ValidationError extends HydraError {
865
946
  readonly validationErrors: unknown[];
866
947
  constructor(message: string, validationErrors?: unknown[]);
867
948
  }
868
949
  /**
869
950
  * Server error - internal server error
870
951
  */
871
- declare class ServerError extends AIDirectorError {
952
+ declare class ServerError extends HydraError {
872
953
  constructor(message: string, statusCode?: number);
873
954
  }
874
955
  /**
875
- * Check if error is an AI Director error
956
+ * Quota exceeded error - monthly request limit reached
957
+ */
958
+ declare class QuotaExceededError extends HydraError {
959
+ readonly tier: string;
960
+ readonly limit: number;
961
+ readonly used: number;
962
+ constructor(message: string, options: {
963
+ tier: string;
964
+ limit: number;
965
+ used: number;
966
+ });
967
+ }
968
+ /**
969
+ * Worker error - Cloudflare Worker execution failed
970
+ */
971
+ declare class WorkerError extends HydraError {
972
+ readonly workerDurationMs?: number;
973
+ constructor(message: string, workerDurationMs?: number);
974
+ }
975
+ /**
976
+ * File processing error - attachment handling failed
977
+ */
978
+ declare class FileProcessingError extends HydraError {
979
+ readonly filename?: string;
980
+ readonly mimeType?: string;
981
+ readonly reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
982
+ constructor(message: string, options: {
983
+ filename?: string;
984
+ mimeType?: string;
985
+ reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
986
+ });
987
+ }
988
+ /**
989
+ * Invalid schema error - provided schema is malformed
990
+ */
991
+ declare class InvalidSchemaError extends HydraError {
992
+ readonly schemaPath?: string;
993
+ constructor(message: string, schemaPath?: string);
994
+ }
995
+ /**
996
+ * Check if error is an Hydra error
876
997
  */
877
- declare function isAIDirectorError(error: unknown): error is AIDirectorError;
998
+ declare function isHydraError(error: unknown): error is HydraError;
878
999
  /**
879
1000
  * Check if error is retryable
880
1001
  */
@@ -904,4 +1025,4 @@ declare function getKeyPrefix(secretKey: string): string;
904
1025
  */
905
1026
  declare function isValidSecretKey(secretKey: string): boolean;
906
1027
 
907
- export { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, type ModelInfo, NetworkError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
1028
+ export { AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, FileProcessingError, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, Hydra, type HydraConfig, HydraError, InvalidSchemaError, type ModelInfo, NetworkError, QuotaExceededError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, WorkerError, generateSignature, getKeyPrefix, isHydraError, isRetryableError, isValidSecretKey };
package/dist/index.d.ts CHANGED
@@ -1,21 +1,21 @@
1
1
  /**
2
- * AI Director Client Types
2
+ * Hydra Client Types
3
3
  * Type definitions for the SDK
4
4
  */
5
5
  /**
6
- * Configuration for AI Director client
6
+ * Configuration for Hydra client
7
7
  */
8
- interface AIDirectorConfig {
8
+ interface HydraConfig {
9
9
  /**
10
- * Your AI Director secret key
11
- * Get this from your dashboard at https://aidirector.dev/dashboard/keys
12
- * Format: aid_sk_<random>
10
+ * Your Hydra secret key
11
+ * Get this from your dashboard at https://hydrai.dev/dashboard/keys
12
+ * Format: hyd_sk_<random>
13
13
  */
14
14
  secretKey: string;
15
15
  /**
16
16
  * API base URL
17
17
  * @default 'http://localhost:3000' in development
18
- * @example 'https://api.aidirector.dev'
18
+ * @example 'https://api.hydrai.dev'
19
19
  */
20
20
  baseUrl?: string;
21
21
  /**
@@ -92,6 +92,35 @@ interface GenerateOptions {
92
92
  * @default true
93
93
  */
94
94
  useOptimized?: boolean;
95
+ /**
96
+ * Cache scope for this request.
97
+ * - 'global': Cache is shared across all users (default)
98
+ * - 'user': Cache is scoped to the authenticated user
99
+ * - 'skip': Do not cache this response
100
+ * @default 'global'
101
+ */
102
+ cacheScope?: 'global' | 'user' | 'skip';
103
+ /**
104
+ * AbortSignal for request cancellation.
105
+ * Pass a signal from an AbortController to cancel the request.
106
+ * @example
107
+ * ```typescript
108
+ * const controller = new AbortController();
109
+ * setTimeout(() => controller.abort(), 5000);
110
+ * await client.generate({ chainId: 'x', prompt: 'y', signal: controller.signal });
111
+ * ```
112
+ */
113
+ signal?: AbortSignal;
114
+ /**
115
+ * Override max retries for this specific request.
116
+ * Set to 0 to disable retries for idempotent-sensitive operations.
117
+ */
118
+ maxRetries?: number;
119
+ /**
120
+ * Client-generated request ID for tracing and debugging.
121
+ * If not provided, one will be generated automatically.
122
+ */
123
+ requestId?: string;
95
124
  /**
96
125
  * Generation parameters
97
126
  */
@@ -142,6 +171,12 @@ interface GenerationParameters {
142
171
  * System prompt to prepend
143
172
  */
144
173
  systemPrompt?: string;
174
+ /**
175
+ * Enable thinking/reasoning mode for supported models.
176
+ * When enabled, the model will show its reasoning process.
177
+ * Only works with models that support thinking (e.g., Gemini 2.0 Flash Thinking).
178
+ */
179
+ thinkingMode?: boolean;
145
180
  }
146
181
  /**
147
182
  * Result from the generate method
@@ -180,6 +215,15 @@ interface GenerateData {
180
215
  * Raw AI response text (useful for debugging)
181
216
  */
182
217
  rawContent?: string;
218
+ /**
219
+ * Report of auto-healed validation errors
220
+ * detailed log of how the system fixed invalid AI output
221
+ */
222
+ healingReport?: Array<{
223
+ original: unknown;
224
+ healed: unknown;
225
+ fixes: string[];
226
+ }>;
183
227
  }
184
228
  /**
185
229
  * Generation metadata
@@ -579,9 +623,9 @@ interface WebhookConfig {
579
623
  }
580
624
 
581
625
  /**
582
- * AI Director Client SDK
626
+ * Hydra Client SDK
583
627
  *
584
- * Production-grade client for the AI Director API gateway.
628
+ * Production-grade client for the Hydra API gateway.
585
629
  * Use in Next.js API routes, Server Actions, or any Node.js/Edge environment.
586
630
  *
587
631
  * Features:
@@ -593,11 +637,11 @@ interface WebhookConfig {
593
637
  *
594
638
  * @example
595
639
  * ```typescript
596
- * import { AIDirector } from '@aidirector/client';
640
+ * import { Hydra } from 'hydra-aidirector';
597
641
  *
598
- * const client = new AIDirector({
599
- * secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
600
- * baseUrl: 'https://api.aidirector.dev',
642
+ * const client = new Hydra({
643
+ * secretKey: process.env.HYDRA_SECRET_KEY!,
644
+ * baseUrl: 'https://api.hydrai.dev',
601
645
  * });
602
646
  *
603
647
  * const result = await client.generate({
@@ -611,20 +655,20 @@ interface WebhookConfig {
611
655
  * }
612
656
  * ```
613
657
  */
614
- declare class AIDirector {
658
+ declare class Hydra {
615
659
  private readonly secretKey;
616
660
  private readonly baseUrl;
617
661
  private readonly timeout;
618
662
  private readonly maxRetries;
619
663
  private readonly keyPrefix;
620
664
  private readonly debug;
621
- constructor(config: AIDirectorConfig);
665
+ constructor(config: HydraConfig);
622
666
  /**
623
667
  * Generate content using your fallback chain
624
668
  *
625
669
  * @param options - Generation options
626
670
  * @returns Promise resolving to GenerateResult
627
- * @throws AIDirectorError subclasses on failure
671
+ * @throws HydraError subclasses on failure
628
672
  *
629
673
  * @example
630
674
  * ```typescript
@@ -711,9 +755,9 @@ declare class AIDirector {
711
755
  * Useful for testing or switching environments
712
756
  *
713
757
  * @param overrides - Configuration overrides
714
- * @returns New AIDirector instance
758
+ * @returns New Hydra instance
715
759
  */
716
- withConfig(overrides: Partial<AIDirectorConfig>): AIDirector;
760
+ withConfig(overrides: Partial<HydraConfig>): Hydra;
717
761
  /**
718
762
  * Register a webhook for async notifications
719
763
  *
@@ -729,6 +773,43 @@ declare class AIDirector {
729
773
  registered: boolean;
730
774
  message: string;
731
775
  }>;
776
+ /**
777
+ * Unregister a webhook
778
+ *
779
+ * @param webhookId - ID of the webhook to unregister
780
+ * @returns Unregistration result
781
+ */
782
+ unregisterWebhook(webhookId: string): Promise<{
783
+ unregistered: boolean;
784
+ message: string;
785
+ }>;
786
+ /**
787
+ * List all registered webhooks
788
+ *
789
+ * @returns Array of webhook configurations
790
+ */
791
+ listWebhooks(): Promise<Array<{
792
+ id: string;
793
+ requestId: string;
794
+ url: string;
795
+ status: 'pending' | 'delivered' | 'failed';
796
+ retryCount: number;
797
+ createdAt: string;
798
+ }>>;
799
+ /**
800
+ * Update a webhook configuration
801
+ *
802
+ * @param webhookId - ID of the webhook to update
803
+ * @param updates - Fields to update
804
+ * @returns Updated webhook info
805
+ */
806
+ updateWebhook(webhookId: string, updates: {
807
+ url?: string;
808
+ retryCount?: number;
809
+ }): Promise<{
810
+ updated: boolean;
811
+ message: string;
812
+ }>;
732
813
  /**
733
814
  * Get detailed health information including component status
734
815
  *
@@ -802,13 +883,13 @@ declare class AIDirector {
802
883
  }
803
884
 
804
885
  /**
805
- * AI Director Error Classes
886
+ * Hydra Error Classes
806
887
  * Structured error types for better error handling
807
888
  */
808
889
  /**
809
- * Base error class for AI Director errors
890
+ * Base error class for Hydra errors
810
891
  */
811
- declare class AIDirectorError extends Error {
892
+ declare class HydraError extends Error {
812
893
  readonly code: string;
813
894
  readonly retryable: boolean;
814
895
  readonly statusCode?: number;
@@ -822,59 +903,99 @@ declare class AIDirectorError extends Error {
822
903
  /**
823
904
  * Configuration error - invalid client setup
824
905
  */
825
- declare class ConfigurationError extends AIDirectorError {
906
+ declare class ConfigurationError extends HydraError {
826
907
  constructor(message: string);
827
908
  }
828
909
  /**
829
910
  * Authentication error - invalid or expired credentials
830
911
  */
831
- declare class AuthenticationError extends AIDirectorError {
912
+ declare class AuthenticationError extends HydraError {
832
913
  constructor(message: string, statusCode?: number);
833
914
  }
834
915
  /**
835
916
  * Rate limit error - too many requests
836
917
  */
837
- declare class RateLimitError extends AIDirectorError {
918
+ declare class RateLimitError extends HydraError {
838
919
  readonly retryAfterMs: number;
839
920
  constructor(message: string, retryAfterMs?: number);
840
921
  }
841
922
  /**
842
923
  * Timeout error - request took too long
843
924
  */
844
- declare class TimeoutError extends AIDirectorError {
925
+ declare class TimeoutError extends HydraError {
845
926
  readonly timeoutMs: number;
846
927
  constructor(timeoutMs: number);
847
928
  }
848
929
  /**
849
930
  * Network error - connection failed
850
931
  */
851
- declare class NetworkError extends AIDirectorError {
932
+ declare class NetworkError extends HydraError {
852
933
  constructor(message: string, cause?: Error);
853
934
  }
854
935
  /**
855
936
  * Chain error - fallback chain execution failed
856
937
  */
857
- declare class ChainExecutionError extends AIDirectorError {
938
+ declare class ChainExecutionError extends HydraError {
858
939
  readonly attemptedModels: string[];
859
940
  constructor(message: string, attemptedModels?: string[]);
860
941
  }
861
942
  /**
862
943
  * Validation error - schema validation failed
863
944
  */
864
- declare class ValidationError extends AIDirectorError {
945
+ declare class ValidationError extends HydraError {
865
946
  readonly validationErrors: unknown[];
866
947
  constructor(message: string, validationErrors?: unknown[]);
867
948
  }
868
949
  /**
869
950
  * Server error - internal server error
870
951
  */
871
- declare class ServerError extends AIDirectorError {
952
+ declare class ServerError extends HydraError {
872
953
  constructor(message: string, statusCode?: number);
873
954
  }
874
955
  /**
875
- * Check if error is an AI Director error
956
+ * Quota exceeded error - monthly request limit reached
957
+ */
958
+ declare class QuotaExceededError extends HydraError {
959
+ readonly tier: string;
960
+ readonly limit: number;
961
+ readonly used: number;
962
+ constructor(message: string, options: {
963
+ tier: string;
964
+ limit: number;
965
+ used: number;
966
+ });
967
+ }
968
+ /**
969
+ * Worker error - Cloudflare Worker execution failed
970
+ */
971
+ declare class WorkerError extends HydraError {
972
+ readonly workerDurationMs?: number;
973
+ constructor(message: string, workerDurationMs?: number);
974
+ }
975
+ /**
976
+ * File processing error - attachment handling failed
977
+ */
978
+ declare class FileProcessingError extends HydraError {
979
+ readonly filename?: string;
980
+ readonly mimeType?: string;
981
+ readonly reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
982
+ constructor(message: string, options: {
983
+ filename?: string;
984
+ mimeType?: string;
985
+ reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
986
+ });
987
+ }
988
+ /**
989
+ * Invalid schema error - provided schema is malformed
990
+ */
991
+ declare class InvalidSchemaError extends HydraError {
992
+ readonly schemaPath?: string;
993
+ constructor(message: string, schemaPath?: string);
994
+ }
995
+ /**
996
+ * Check if error is an Hydra error
876
997
  */
877
- declare function isAIDirectorError(error: unknown): error is AIDirectorError;
998
+ declare function isHydraError(error: unknown): error is HydraError;
878
999
  /**
879
1000
  * Check if error is retryable
880
1001
  */
@@ -904,4 +1025,4 @@ declare function getKeyPrefix(secretKey: string): string;
904
1025
  */
905
1026
  declare function isValidSecretKey(secretKey: string): boolean;
906
1027
 
907
- export { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, type ModelInfo, NetworkError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
1028
+ export { AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, FileProcessingError, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, Hydra, type HydraConfig, HydraError, InvalidSchemaError, type ModelInfo, NetworkError, QuotaExceededError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, WorkerError, generateSignature, getKeyPrefix, isHydraError, isRetryableError, isValidSecretKey };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var M=typeof process<"u"&&process.versions?.node;async function j(o){let{createHash:e}=await import('crypto');return e("sha256").update(o).digest("hex")}async function K(o,e,t,r,a){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,a.toString(),r].join(`
2
- `),u=await j(o);return s("sha256",u).update(c).digest("hex")}async function N(o){let t=new TextEncoder().encode(o),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function G(o,e,t,r,a){let s=new TextEncoder,c=[e.toUpperCase(),t,a.toString(),r].join(`
3
- `),u=await N(o),n=s.encode(u),i=await crypto.subtle.importKey("raw",n,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(h=>h.toString(16).padStart(2,"0")).join("")}async function A(o,e,t,r,a){return M?K(o,e,t,r,a):G(o,e,t,r,a)}function T(o){return o.slice(0,12)}function x(o){return typeof o=="string"&&o.startsWith("aid_sk_")&&o.length>=20}var d=class extends Error{constructor(e,t,r){super(e),this.name="AIDirectorError",this.code=t,this.retryable=r?.retryable??false,this.statusCode=r?.statusCode,this.originalError=r?.cause;}},b=class extends d{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},E=class extends d{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},w=class extends d{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},k=class extends d{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},y=class extends d{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},g=class extends d{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},U=class extends d{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},p=class extends d{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}};function v(o){return o instanceof d}function _(o){return v(o)?o.retryable:false}var L="http://localhost:3000",W=6e5,H=3,O=[1e3,2e3,5e3],I=class o{constructor(e){if(!e.secretKey)throw new b("secretKey is required");if(!x(e.secretKey))throw new b("Invalid secret key format. Expected format: aid_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||L).replace(/\/$/,""),this.timeout=e.timeout??W,this.maxRetries=e.maxRetries??H,this.keyPrefix=T(e.secretKey),this.debug=e.debug??false,this.debug&&this.log("Initialized",{baseUrl:this.baseUrl,timeout:this.timeout});}async generate(e){if(e.useOptimized!==false)try{return await this.generateOptimized(e)}catch(r){this.debug&&this.log("3-step generation failed, falling back to legacy",{error:r instanceof Error?r.message:String(r)});}return this.generateLegacy(e)}async generateOptimized(e){let t=Date.now(),r="/api/v1/generate/token",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK,files:e.files}),s=await this.makeAuthenticatedRequest(r,"POST",a);if(s.cached&&s.data)return this.debug&&this.log("generate cache hit (optimized)",{latencyMs:Date.now()-t}),{success:true,data:s.data,meta:{cached:true,modelUsed:s.meta?.modelUsed||"",tokensUsed:{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[]}};if(!s.token||!s.workerUrl)throw new g("Token generation failed - no token or worker URL returned",[]);let c=JSON.stringify({token:s.token,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,timeout:e.timeout??this.timeout}),n=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!n.success||!n.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:n.modelId||"",tokensUsed:n.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[n.modelId||""]},error:{code:n.errorCode||"GENERATION_FAILED",message:n.errorMessage||"Generation failed",retryable:false}};let i=[],m=[];try{let h=JSON.parse(n.content);Array.isArray(h)?i=h:i=[h];}catch{i=[n.content];}n.completionToken&&this.cacheCompletionAsync({completionToken:n.completionToken,content:n.content,tokensUsed:n.tokensUsed||{input:0,output:0},finishReason:n.finishReason||"stop",chainId:e.chainId,modelUsed:n.modelId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK}).catch(h=>{this.debug&&this.log("cacheCompletion failed (non-blocking)",{error:h});});let f=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:f,modelUsed:n.modelId,tokensUsed:n.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:n.modelId,tokensUsed:n.tokensUsed||{input:0,output:0},latencyMs:f,attemptedModels:[n.modelId]}}}async cacheCompletionAsync(e){let t="/api/v1/generate/complete",r=JSON.stringify(e);await this.makeAuthenticatedRequest(t,"POST",r);}async generateLegacy(e){let t=Date.now(),r="/api/v1/generate",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,noCache:e.noCache,files:e.files,options:{...e.options,timeout:e.timeout??this.timeout}}),s=null;for(let n=0;n<=this.maxRetries;n++)try{let i=await this.makeAuthenticatedRequest(r,"POST",a,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:n+1}),i.meta&&(i.meta.latencyMs=m),i}catch(i){if(s=i instanceof Error?i:new Error(String(i)),this.debug&&this.log(`generate attempt ${n+1} failed`,{error:s.message}),!this.isRetryable(i)||n>=this.maxRetries)break;let m=O[Math.min(n,O.length-1)];i instanceof w&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,u=s instanceof k;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:u?"TIMEOUT":"REQUEST_FAILED",message:s?.message||"Request failed after all retries",retryable:false}}}async generateStream(e,t){let r="/api/v1/generate/stream",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await A(this.secretKey,"POST",r,a,s),u=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:a},e.timeout??this.timeout);if(!u.ok){let h=await u.json();throw this.parseError(u.status,h)}let n=u.body?.getReader();if(!n)throw new y("Response body is not readable");let i=new TextDecoder,m="",f=[];for(;;){let{done:h,value:D}=await n.read();if(h)break;m+=i.decode(D,{stream:!0});let P=m.split(`
4
- `);m=P.pop()||"";let S="";for(let R of P){if(R.startsWith("event: ")){S=R.slice(7).trim();continue}if(R.startsWith("data: ")){let C=R.slice(6);if(C==="[DONE]")break;try{let l=JSON.parse(C);switch(S){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(f.push(l.object),t.onObject?.(l.object,l.index)),t.onChunk&&l.object&&t.onChunk(JSON.stringify(l.object));break;case "array_start":t.onArrayStart?.();break;case "complete":t.onComplete&&t.onComplete({objects:f,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new g(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}S="";}}}}catch(s){if(t.onError)t.onError(s instanceof Error?s:new Error(String(s)));else throw s}}async listModels(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/models`,{method:"GET",headers:{Accept:"application/json"}})).json();if(!t.success)throw new p(t.error?.message||"Failed to list models");return t.data}async listChains(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/chains`,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!t.success)throw new p(t.error?.message||"Failed to list chains");return t.data}async getUsage(e){let t=new URLSearchParams;e?.startDate&&t.set("startDate",e.startDate.toISOString()),e?.endDate&&t.set("endDate",e.endDate.toISOString());let r=`${this.baseUrl}/api/v1/usage${t.toString()?"?"+t:""}`,s=await(await this.fetchWithTimeout(r,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!s.success)throw new p(s.error?.message||"Failed to get usage");return s.data}async health(){let e=Date.now();try{let t=await this.fetchWithTimeout(`${this.baseUrl}/api/health`,{method:"GET"},5e3),r=Date.now()-e,a=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:a.version}}catch{return {ok:false,latencyMs:Date.now()-e}}}withConfig(e){return new o({secretKey:this.secretKey,baseUrl:this.baseUrl,timeout:this.timeout,maxRetries:this.maxRetries,debug:this.debug,...e})}async registerWebhook(e){let t=await this.makeAuthenticatedRequest("/api/v1/webhooks","POST",JSON.stringify(e));if(!t.success||!t.data)throw new p(t.error?.message||"Failed to register webhook");return t.data}async healthDetailed(){return (await fetch(`${this.baseUrl}/api/v1/health/detailed`,{headers:{Accept:"application/json"}})).json()}async generateBatch(e,t,r={}){let a=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!a.success||!a.data)throw new g(a.error?.message||"Batch generation failed",[]);return a.data}async makeAuthenticatedRequest(e,t,r,a){let s=Date.now(),c=await A(this.secretKey,t,e,r,s),u=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:r},a??this.timeout),n=await u.json();if(!u.ok)throw this.parseError(u.status,n);return n}async fetchWithTimeout(e,t,r=this.timeout){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{return await fetch(e,{...t,signal:a.signal})}catch(c){throw c instanceof Error&&c.name==="AbortError"?new k(r):c instanceof Error?new y(c.message,c):new y(String(c))}finally{clearTimeout(s);}}parseError(e,t){let r=t?.error?.message||`HTTP ${e}`,a=t?.error?.code||"UNKNOWN";switch(e){case 401:return new E(r,e);case 429:let s=t?.retryAfterMs||6e4;return new w(r,s);case 500:case 502:case 503:case 504:return new p(r,e);default:return a==="CHAIN_FAILED"?new g(r,t?.error?.attemptedModels||[]):new d(r,a,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof d)return e.retryable;if(e instanceof Error){let t=e.message.toLowerCase();return t.includes("network")||t.includes("timeout")||e.name==="AbortError"}return false}sleep(e){return new Promise(t=>setTimeout(t,e))}log(e,t){let r="[AIDirector]";t?console.log(r,e,t):console.log(r,e);}};exports.AIDirector=I;exports.AIDirectorError=d;exports.AuthenticationError=E;exports.ChainExecutionError=g;exports.ConfigurationError=b;exports.NetworkError=y;exports.RateLimitError=w;exports.ServerError=p;exports.TimeoutError=k;exports.ValidationError=U;exports.generateSignature=A;exports.getKeyPrefix=T;exports.isAIDirectorError=v;exports.isRetryableError=_;exports.isValidSecretKey=x;
1
+ 'use strict';var K=typeof process<"u"&&process.versions?.node;async function H(n){let{createHash:e}=await import('crypto');return e("sha256").update(n).digest("hex")}async function G(n,e,t,r,o){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,o.toString(),r].join(`
2
+ `),d=await H(n);return s("sha256",d).update(c).digest("hex")}async function L(n){let t=new TextEncoder().encode(n),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function W(n,e,t,r,o){let s=new TextEncoder,c=[e.toUpperCase(),t,o.toString(),r].join(`
3
+ `),d=await L(n),a=s.encode(d),i=await crypto.subtle.importKey("raw",a,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(p=>p.toString(16).padStart(2,"0")).join("")}async function T(n,e,t,r,o){return K?G(n,e,t,r,o):W(n,e,t,r,o)}function U(n){return n.slice(0,12)}function C(n){return typeof n=="string"&&n.startsWith("hyd_sk_")&&n.length>=20}var u=class extends Error{constructor(e,t,r){super(e),this.name="HydraError",this.code=t,this.retryable=r?.retryable??false,this.statusCode=r?.statusCode,this.originalError=r?.cause;}},k=class extends u{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},x=class extends u{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},E=class extends u{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},R=class extends u{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},w=class extends u{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},f=class extends u{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},P=class extends u{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},g=class extends u{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}},v=class extends u{constructor(e,t){super(e,"QUOTA_EXCEEDED",{retryable:false,statusCode:402}),this.name="QuotaExceededError",this.tier=t.tier,this.limit=t.limit,this.used=t.used;}},O=class extends u{constructor(e,t){super(e,"WORKER_ERROR",{retryable:true}),this.name="WorkerError",this.workerDurationMs=t;}},I=class extends u{constructor(e,t){super(e,"FILE_PROCESSING_ERROR",{retryable:false}),this.name="FileProcessingError",this.filename=t.filename,this.mimeType=t.mimeType,this.reason=t.reason;}},M=class extends u{constructor(e,t){super(e,"INVALID_SCHEMA",{retryable:false}),this.name="InvalidSchemaError",this.schemaPath=t;}};function _(n){return n instanceof u}function q(n){return _(n)?n.retryable:false}var F="http://localhost:3000",$=6e5,B=3,N=[1e3,2e3,5e3];function J(n){let e=n.trim(),t=e.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);t&&(e=t[1].trim());let r=e.indexOf("{"),o=e.indexOf("["),s;if(r===-1&&o===-1)return e;r===-1?s=o:o===-1?s=r:s=Math.min(r,o);let c=e[s],d=c==="{"?"}":"]",a=0,i=false,m=false,y=-1;for(let h=s;h<e.length;h++){let b=e[h];if(m){m=false;continue}if(b==="\\"&&i){m=true;continue}if(b==='"'&&!m){i=!i;continue}if(!i){if(b===c)a++;else if(b===d&&(a--,a===0)){y=h;break}}}if(y!==-1)return e.slice(s,y+1);let p=e.lastIndexOf(d);return p>s?e.slice(s,p+1):e}var D=class n{constructor(e){if(!e.secretKey)throw new k("secretKey is required");if(!C(e.secretKey))throw new k("Invalid secret key format. Expected format: hyd_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||F).replace(/\/$/,""),this.timeout=e.timeout??$,this.maxRetries=e.maxRetries??B,this.keyPrefix=U(e.secretKey),this.debug=e.debug??false,this.debug&&this.log("Initialized",{baseUrl:this.baseUrl,timeout:this.timeout});}async generate(e){if(e.useOptimized!==false)try{return await this.generateOptimized(e)}catch(r){this.debug&&this.log("3-step generation failed, falling back to legacy",{error:r instanceof Error?r.message:String(r)});}return this.generateLegacy(e)}async generateOptimized(e){let t=Date.now(),r="/api/v1/generate/token",o=JSON.stringify({chainId:e.chainId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK,files:e.files}),s=await this.makeAuthenticatedRequest(r,"POST",o);if(s.cached&&s.data)return this.debug&&this.log("generate cache hit (optimized)",{latencyMs:Date.now()-t}),{success:true,data:s.data,meta:{cached:true,modelUsed:s.meta?.modelUsed||"",tokensUsed:{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[]}};if(!s.token||!s.workerUrl)throw new f("Token generation failed - no token or worker URL returned",[]);let c=JSON.stringify({token:s.token,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,timeout:e.timeout??this.timeout}),a=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!a.success||!a.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:a.modelId||"",tokensUsed:a.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[a.modelId||""]},error:{code:a.errorCode||"GENERATION_FAILED",message:a.errorMessage||"Generation failed",retryable:false}};let i=[],m=[],y=J(a.content);try{let h=JSON.parse(y);Array.isArray(h)?i=h:i=[h];}catch{i=[y];}a.completionToken&&this.cacheCompletionAsync({completionToken:a.completionToken,content:a.content,tokensUsed:a.tokensUsed||{input:0,output:0},finishReason:a.finishReason||"stop",chainId:e.chainId,modelUsed:a.modelId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK}).catch(h=>{this.debug&&this.log("cacheCompletion failed (non-blocking)",{error:h});});let p=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:p,modelUsed:a.modelId,tokensUsed:a.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:a.modelId,tokensUsed:a.tokensUsed||{input:0,output:0},latencyMs:p,attemptedModels:[a.modelId]}}}async cacheCompletionAsync(e){let t="/api/v1/generate/complete",r=JSON.stringify(e);await this.makeAuthenticatedRequest(t,"POST",r);}async generateLegacy(e){let t=Date.now(),r="/api/v1/generate",o=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,noCache:e.noCache,files:e.files,options:{...e.options,timeout:e.timeout??this.timeout}}),s=null;for(let a=0;a<=this.maxRetries;a++)try{let i=await this.makeAuthenticatedRequest(r,"POST",o,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:a+1}),i.meta&&(i.meta.latencyMs=m),i}catch(i){if(s=i instanceof Error?i:new Error(String(i)),this.debug&&this.log(`generate attempt ${a+1} failed`,{error:s.message}),!this.isRetryable(i)||a>=this.maxRetries)break;let m=N[Math.min(a,N.length-1)];i instanceof E&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,d=s instanceof R;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:d?"TIMEOUT":"REQUEST_FAILED",message:s?.message||"Request failed after all retries",retryable:false}}}async generateStream(e,t){let r="/api/v1/generate/stream",o=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await T(this.secretKey,"POST",r,o,s),d=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-hydra-signature":c,"x-hydra-timestamp":s.toString(),"x-hydra-key":this.keyPrefix},body:o},e.timeout??this.timeout);if(!d.ok){let p=await d.json();throw this.parseError(d.status,p)}let a=d.body?.getReader();if(!a)throw new w("Response body is not readable");let i=new TextDecoder,m="",y=[];for(;;){let{done:p,value:h}=await a.read();if(p)break;m+=i.decode(h,{stream:!0});let b=m.split(`
4
+ `);m=b.pop()||"";let A="";for(let S of b){if(S.startsWith("event: ")){A=S.slice(7).trim();continue}if(S.startsWith("data: ")){let j=S.slice(6);if(j==="[DONE]")break;try{let l=JSON.parse(j);switch(A){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(y.push(l.object),t.onObject?.(l.object,l.index)),t.onChunk&&l.object&&t.onChunk(JSON.stringify(l.object));break;case "array_start":t.onArrayStart?.();break;case "complete":t.onComplete&&t.onComplete({objects:y,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new f(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}A="";}}}}catch(s){if(t.onError)t.onError(s instanceof Error?s:new Error(String(s)));else throw s}}async listModels(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/models`,{method:"GET",headers:{Accept:"application/json"}})).json();if(!t.success)throw new g(t.error?.message||"Failed to list models");return t.data}async listChains(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/chains`,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!t.success)throw new g(t.error?.message||"Failed to list chains");return t.data}async getUsage(e){let t=new URLSearchParams;e?.startDate&&t.set("startDate",e.startDate.toISOString()),e?.endDate&&t.set("endDate",e.endDate.toISOString());let r=`${this.baseUrl}/api/v1/usage${t.toString()?"?"+t:""}`,s=await(await this.fetchWithTimeout(r,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!s.success)throw new g(s.error?.message||"Failed to get usage");return s.data}async health(){let e=Date.now();try{let t=await this.fetchWithTimeout(`${this.baseUrl}/api/health`,{method:"GET"},5e3),r=Date.now()-e,o=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:o.version}}catch{return {ok:false,latencyMs:Date.now()-e}}}withConfig(e){return new n({secretKey:this.secretKey,baseUrl:this.baseUrl,timeout:this.timeout,maxRetries:this.maxRetries,debug:this.debug,...e})}async registerWebhook(e){let t=await this.makeAuthenticatedRequest("/api/v1/webhooks","POST",JSON.stringify(e));if(!t.success||!t.data)throw new g(t.error?.message||"Failed to register webhook");return t.data}async unregisterWebhook(e){let t=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"DELETE","");if(!t.success||!t.data)throw new g(t.error?.message||"Failed to unregister webhook");return t.data}async listWebhooks(){let e=await this.makeAuthenticatedRequest("/api/v1/webhooks","GET","");if(!e.success)throw new g(e.error?.message||"Failed to list webhooks");return e.data||[]}async updateWebhook(e,t){let r=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"PATCH",JSON.stringify(t));if(!r.success||!r.data)throw new g(r.error?.message||"Failed to update webhook");return r.data}async healthDetailed(){return (await fetch(`${this.baseUrl}/api/v1/health/detailed`,{headers:{Accept:"application/json"}})).json()}async generateBatch(e,t,r={}){let o=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!o.success||!o.data)throw new f(o.error?.message||"Batch generation failed",[]);return o.data}async makeAuthenticatedRequest(e,t,r,o){let s=Date.now(),c=await T(this.secretKey,t,e,r,s),d=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-hydra-signature":c,"x-hydra-timestamp":s.toString(),"x-hydra-key":this.keyPrefix},body:r},o??this.timeout),a=await d.json();if(!d.ok)throw this.parseError(d.status,a);return a}async fetchWithTimeout(e,t,r=this.timeout){let o=new AbortController,s=setTimeout(()=>o.abort(),r);try{return await fetch(e,{...t,signal:o.signal})}catch(c){throw c instanceof Error&&c.name==="AbortError"?new R(r):c instanceof Error?new w(c.message,c):new w(String(c))}finally{clearTimeout(s);}}parseError(e,t){let r=t?.error?.message||`HTTP ${e}`,o=t?.error?.code||"UNKNOWN";switch(e){case 401:return new x(r,e);case 429:let s=t?.retryAfterMs||6e4;return new E(r,s);case 500:case 502:case 503:case 504:return new g(r,e);default:return o==="CHAIN_FAILED"?new f(r,t?.error?.attemptedModels||[]):new u(r,o,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof u)return e.retryable;if(e instanceof Error){let t=e.message.toLowerCase();return t.includes("network")||t.includes("timeout")||e.name==="AbortError"}return false}sleep(e){return new Promise(t=>setTimeout(t,e))}log(e,t){let r="[Hydra]";t?console.log(r,e,t):console.log(r,e);}};exports.AuthenticationError=x;exports.ChainExecutionError=f;exports.ConfigurationError=k;exports.FileProcessingError=I;exports.Hydra=D;exports.HydraError=u;exports.InvalidSchemaError=M;exports.NetworkError=w;exports.QuotaExceededError=v;exports.RateLimitError=E;exports.ServerError=g;exports.TimeoutError=R;exports.ValidationError=P;exports.WorkerError=O;exports.generateSignature=T;exports.getKeyPrefix=U;exports.isHydraError=_;exports.isRetryableError=q;exports.isValidSecretKey=C;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- var M=typeof process<"u"&&process.versions?.node;async function j(o){let{createHash:e}=await import('crypto');return e("sha256").update(o).digest("hex")}async function K(o,e,t,r,a){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,a.toString(),r].join(`
2
- `),u=await j(o);return s("sha256",u).update(c).digest("hex")}async function N(o){let t=new TextEncoder().encode(o),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function G(o,e,t,r,a){let s=new TextEncoder,c=[e.toUpperCase(),t,a.toString(),r].join(`
3
- `),u=await N(o),n=s.encode(u),i=await crypto.subtle.importKey("raw",n,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(h=>h.toString(16).padStart(2,"0")).join("")}async function A(o,e,t,r,a){return M?K(o,e,t,r,a):G(o,e,t,r,a)}function T(o){return o.slice(0,12)}function x(o){return typeof o=="string"&&o.startsWith("aid_sk_")&&o.length>=20}var d=class extends Error{constructor(e,t,r){super(e),this.name="AIDirectorError",this.code=t,this.retryable=r?.retryable??false,this.statusCode=r?.statusCode,this.originalError=r?.cause;}},b=class extends d{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},E=class extends d{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},w=class extends d{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},k=class extends d{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},y=class extends d{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},g=class extends d{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},U=class extends d{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},p=class extends d{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}};function v(o){return o instanceof d}function _(o){return v(o)?o.retryable:false}var L="http://localhost:3000",W=6e5,H=3,O=[1e3,2e3,5e3],I=class o{constructor(e){if(!e.secretKey)throw new b("secretKey is required");if(!x(e.secretKey))throw new b("Invalid secret key format. Expected format: aid_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||L).replace(/\/$/,""),this.timeout=e.timeout??W,this.maxRetries=e.maxRetries??H,this.keyPrefix=T(e.secretKey),this.debug=e.debug??false,this.debug&&this.log("Initialized",{baseUrl:this.baseUrl,timeout:this.timeout});}async generate(e){if(e.useOptimized!==false)try{return await this.generateOptimized(e)}catch(r){this.debug&&this.log("3-step generation failed, falling back to legacy",{error:r instanceof Error?r.message:String(r)});}return this.generateLegacy(e)}async generateOptimized(e){let t=Date.now(),r="/api/v1/generate/token",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK,files:e.files}),s=await this.makeAuthenticatedRequest(r,"POST",a);if(s.cached&&s.data)return this.debug&&this.log("generate cache hit (optimized)",{latencyMs:Date.now()-t}),{success:true,data:s.data,meta:{cached:true,modelUsed:s.meta?.modelUsed||"",tokensUsed:{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[]}};if(!s.token||!s.workerUrl)throw new g("Token generation failed - no token or worker URL returned",[]);let c=JSON.stringify({token:s.token,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,timeout:e.timeout??this.timeout}),n=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!n.success||!n.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:n.modelId||"",tokensUsed:n.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[n.modelId||""]},error:{code:n.errorCode||"GENERATION_FAILED",message:n.errorMessage||"Generation failed",retryable:false}};let i=[],m=[];try{let h=JSON.parse(n.content);Array.isArray(h)?i=h:i=[h];}catch{i=[n.content];}n.completionToken&&this.cacheCompletionAsync({completionToken:n.completionToken,content:n.content,tokensUsed:n.tokensUsed||{input:0,output:0},finishReason:n.finishReason||"stop",chainId:e.chainId,modelUsed:n.modelId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK}).catch(h=>{this.debug&&this.log("cacheCompletion failed (non-blocking)",{error:h});});let f=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:f,modelUsed:n.modelId,tokensUsed:n.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:n.modelId,tokensUsed:n.tokensUsed||{input:0,output:0},latencyMs:f,attemptedModels:[n.modelId]}}}async cacheCompletionAsync(e){let t="/api/v1/generate/complete",r=JSON.stringify(e);await this.makeAuthenticatedRequest(t,"POST",r);}async generateLegacy(e){let t=Date.now(),r="/api/v1/generate",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,noCache:e.noCache,files:e.files,options:{...e.options,timeout:e.timeout??this.timeout}}),s=null;for(let n=0;n<=this.maxRetries;n++)try{let i=await this.makeAuthenticatedRequest(r,"POST",a,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:n+1}),i.meta&&(i.meta.latencyMs=m),i}catch(i){if(s=i instanceof Error?i:new Error(String(i)),this.debug&&this.log(`generate attempt ${n+1} failed`,{error:s.message}),!this.isRetryable(i)||n>=this.maxRetries)break;let m=O[Math.min(n,O.length-1)];i instanceof w&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,u=s instanceof k;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:u?"TIMEOUT":"REQUEST_FAILED",message:s?.message||"Request failed after all retries",retryable:false}}}async generateStream(e,t){let r="/api/v1/generate/stream",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await A(this.secretKey,"POST",r,a,s),u=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:a},e.timeout??this.timeout);if(!u.ok){let h=await u.json();throw this.parseError(u.status,h)}let n=u.body?.getReader();if(!n)throw new y("Response body is not readable");let i=new TextDecoder,m="",f=[];for(;;){let{done:h,value:D}=await n.read();if(h)break;m+=i.decode(D,{stream:!0});let P=m.split(`
4
- `);m=P.pop()||"";let S="";for(let R of P){if(R.startsWith("event: ")){S=R.slice(7).trim();continue}if(R.startsWith("data: ")){let C=R.slice(6);if(C==="[DONE]")break;try{let l=JSON.parse(C);switch(S){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(f.push(l.object),t.onObject?.(l.object,l.index)),t.onChunk&&l.object&&t.onChunk(JSON.stringify(l.object));break;case "array_start":t.onArrayStart?.();break;case "complete":t.onComplete&&t.onComplete({objects:f,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new g(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}S="";}}}}catch(s){if(t.onError)t.onError(s instanceof Error?s:new Error(String(s)));else throw s}}async listModels(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/models`,{method:"GET",headers:{Accept:"application/json"}})).json();if(!t.success)throw new p(t.error?.message||"Failed to list models");return t.data}async listChains(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/chains`,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!t.success)throw new p(t.error?.message||"Failed to list chains");return t.data}async getUsage(e){let t=new URLSearchParams;e?.startDate&&t.set("startDate",e.startDate.toISOString()),e?.endDate&&t.set("endDate",e.endDate.toISOString());let r=`${this.baseUrl}/api/v1/usage${t.toString()?"?"+t:""}`,s=await(await this.fetchWithTimeout(r,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!s.success)throw new p(s.error?.message||"Failed to get usage");return s.data}async health(){let e=Date.now();try{let t=await this.fetchWithTimeout(`${this.baseUrl}/api/health`,{method:"GET"},5e3),r=Date.now()-e,a=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:a.version}}catch{return {ok:false,latencyMs:Date.now()-e}}}withConfig(e){return new o({secretKey:this.secretKey,baseUrl:this.baseUrl,timeout:this.timeout,maxRetries:this.maxRetries,debug:this.debug,...e})}async registerWebhook(e){let t=await this.makeAuthenticatedRequest("/api/v1/webhooks","POST",JSON.stringify(e));if(!t.success||!t.data)throw new p(t.error?.message||"Failed to register webhook");return t.data}async healthDetailed(){return (await fetch(`${this.baseUrl}/api/v1/health/detailed`,{headers:{Accept:"application/json"}})).json()}async generateBatch(e,t,r={}){let a=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!a.success||!a.data)throw new g(a.error?.message||"Batch generation failed",[]);return a.data}async makeAuthenticatedRequest(e,t,r,a){let s=Date.now(),c=await A(this.secretKey,t,e,r,s),u=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:r},a??this.timeout),n=await u.json();if(!u.ok)throw this.parseError(u.status,n);return n}async fetchWithTimeout(e,t,r=this.timeout){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{return await fetch(e,{...t,signal:a.signal})}catch(c){throw c instanceof Error&&c.name==="AbortError"?new k(r):c instanceof Error?new y(c.message,c):new y(String(c))}finally{clearTimeout(s);}}parseError(e,t){let r=t?.error?.message||`HTTP ${e}`,a=t?.error?.code||"UNKNOWN";switch(e){case 401:return new E(r,e);case 429:let s=t?.retryAfterMs||6e4;return new w(r,s);case 500:case 502:case 503:case 504:return new p(r,e);default:return a==="CHAIN_FAILED"?new g(r,t?.error?.attemptedModels||[]):new d(r,a,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof d)return e.retryable;if(e instanceof Error){let t=e.message.toLowerCase();return t.includes("network")||t.includes("timeout")||e.name==="AbortError"}return false}sleep(e){return new Promise(t=>setTimeout(t,e))}log(e,t){let r="[AIDirector]";t?console.log(r,e,t):console.log(r,e);}};export{I as AIDirector,d as AIDirectorError,E as AuthenticationError,g as ChainExecutionError,b as ConfigurationError,y as NetworkError,w as RateLimitError,p as ServerError,k as TimeoutError,U as ValidationError,A as generateSignature,T as getKeyPrefix,v as isAIDirectorError,_ as isRetryableError,x as isValidSecretKey};
1
+ var K=typeof process<"u"&&process.versions?.node;async function H(n){let{createHash:e}=await import('crypto');return e("sha256").update(n).digest("hex")}async function G(n,e,t,r,o){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,o.toString(),r].join(`
2
+ `),d=await H(n);return s("sha256",d).update(c).digest("hex")}async function L(n){let t=new TextEncoder().encode(n),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function W(n,e,t,r,o){let s=new TextEncoder,c=[e.toUpperCase(),t,o.toString(),r].join(`
3
+ `),d=await L(n),a=s.encode(d),i=await crypto.subtle.importKey("raw",a,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(p=>p.toString(16).padStart(2,"0")).join("")}async function T(n,e,t,r,o){return K?G(n,e,t,r,o):W(n,e,t,r,o)}function U(n){return n.slice(0,12)}function C(n){return typeof n=="string"&&n.startsWith("hyd_sk_")&&n.length>=20}var u=class extends Error{constructor(e,t,r){super(e),this.name="HydraError",this.code=t,this.retryable=r?.retryable??false,this.statusCode=r?.statusCode,this.originalError=r?.cause;}},k=class extends u{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},x=class extends u{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},E=class extends u{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},R=class extends u{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},w=class extends u{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},f=class extends u{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},P=class extends u{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},g=class extends u{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}},v=class extends u{constructor(e,t){super(e,"QUOTA_EXCEEDED",{retryable:false,statusCode:402}),this.name="QuotaExceededError",this.tier=t.tier,this.limit=t.limit,this.used=t.used;}},O=class extends u{constructor(e,t){super(e,"WORKER_ERROR",{retryable:true}),this.name="WorkerError",this.workerDurationMs=t;}},I=class extends u{constructor(e,t){super(e,"FILE_PROCESSING_ERROR",{retryable:false}),this.name="FileProcessingError",this.filename=t.filename,this.mimeType=t.mimeType,this.reason=t.reason;}},M=class extends u{constructor(e,t){super(e,"INVALID_SCHEMA",{retryable:false}),this.name="InvalidSchemaError",this.schemaPath=t;}};function _(n){return n instanceof u}function q(n){return _(n)?n.retryable:false}var F="http://localhost:3000",$=6e5,B=3,N=[1e3,2e3,5e3];function J(n){let e=n.trim(),t=e.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);t&&(e=t[1].trim());let r=e.indexOf("{"),o=e.indexOf("["),s;if(r===-1&&o===-1)return e;r===-1?s=o:o===-1?s=r:s=Math.min(r,o);let c=e[s],d=c==="{"?"}":"]",a=0,i=false,m=false,y=-1;for(let h=s;h<e.length;h++){let b=e[h];if(m){m=false;continue}if(b==="\\"&&i){m=true;continue}if(b==='"'&&!m){i=!i;continue}if(!i){if(b===c)a++;else if(b===d&&(a--,a===0)){y=h;break}}}if(y!==-1)return e.slice(s,y+1);let p=e.lastIndexOf(d);return p>s?e.slice(s,p+1):e}var D=class n{constructor(e){if(!e.secretKey)throw new k("secretKey is required");if(!C(e.secretKey))throw new k("Invalid secret key format. Expected format: hyd_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||F).replace(/\/$/,""),this.timeout=e.timeout??$,this.maxRetries=e.maxRetries??B,this.keyPrefix=U(e.secretKey),this.debug=e.debug??false,this.debug&&this.log("Initialized",{baseUrl:this.baseUrl,timeout:this.timeout});}async generate(e){if(e.useOptimized!==false)try{return await this.generateOptimized(e)}catch(r){this.debug&&this.log("3-step generation failed, falling back to legacy",{error:r instanceof Error?r.message:String(r)});}return this.generateLegacy(e)}async generateOptimized(e){let t=Date.now(),r="/api/v1/generate/token",o=JSON.stringify({chainId:e.chainId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK,files:e.files}),s=await this.makeAuthenticatedRequest(r,"POST",o);if(s.cached&&s.data)return this.debug&&this.log("generate cache hit (optimized)",{latencyMs:Date.now()-t}),{success:true,data:s.data,meta:{cached:true,modelUsed:s.meta?.modelUsed||"",tokensUsed:{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[]}};if(!s.token||!s.workerUrl)throw new f("Token generation failed - no token or worker URL returned",[]);let c=JSON.stringify({token:s.token,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,timeout:e.timeout??this.timeout}),a=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!a.success||!a.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:a.modelId||"",tokensUsed:a.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[a.modelId||""]},error:{code:a.errorCode||"GENERATION_FAILED",message:a.errorMessage||"Generation failed",retryable:false}};let i=[],m=[],y=J(a.content);try{let h=JSON.parse(y);Array.isArray(h)?i=h:i=[h];}catch{i=[y];}a.completionToken&&this.cacheCompletionAsync({completionToken:a.completionToken,content:a.content,tokensUsed:a.tokensUsed||{input:0,output:0},finishReason:a.finishReason||"stop",chainId:e.chainId,modelUsed:a.modelId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK}).catch(h=>{this.debug&&this.log("cacheCompletion failed (non-blocking)",{error:h});});let p=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:p,modelUsed:a.modelId,tokensUsed:a.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:a.modelId,tokensUsed:a.tokensUsed||{input:0,output:0},latencyMs:p,attemptedModels:[a.modelId]}}}async cacheCompletionAsync(e){let t="/api/v1/generate/complete",r=JSON.stringify(e);await this.makeAuthenticatedRequest(t,"POST",r);}async generateLegacy(e){let t=Date.now(),r="/api/v1/generate",o=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,noCache:e.noCache,files:e.files,options:{...e.options,timeout:e.timeout??this.timeout}}),s=null;for(let a=0;a<=this.maxRetries;a++)try{let i=await this.makeAuthenticatedRequest(r,"POST",o,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:a+1}),i.meta&&(i.meta.latencyMs=m),i}catch(i){if(s=i instanceof Error?i:new Error(String(i)),this.debug&&this.log(`generate attempt ${a+1} failed`,{error:s.message}),!this.isRetryable(i)||a>=this.maxRetries)break;let m=N[Math.min(a,N.length-1)];i instanceof E&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,d=s instanceof R;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:d?"TIMEOUT":"REQUEST_FAILED",message:s?.message||"Request failed after all retries",retryable:false}}}async generateStream(e,t){let r="/api/v1/generate/stream",o=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await T(this.secretKey,"POST",r,o,s),d=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-hydra-signature":c,"x-hydra-timestamp":s.toString(),"x-hydra-key":this.keyPrefix},body:o},e.timeout??this.timeout);if(!d.ok){let p=await d.json();throw this.parseError(d.status,p)}let a=d.body?.getReader();if(!a)throw new w("Response body is not readable");let i=new TextDecoder,m="",y=[];for(;;){let{done:p,value:h}=await a.read();if(p)break;m+=i.decode(h,{stream:!0});let b=m.split(`
4
+ `);m=b.pop()||"";let A="";for(let S of b){if(S.startsWith("event: ")){A=S.slice(7).trim();continue}if(S.startsWith("data: ")){let j=S.slice(6);if(j==="[DONE]")break;try{let l=JSON.parse(j);switch(A){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(y.push(l.object),t.onObject?.(l.object,l.index)),t.onChunk&&l.object&&t.onChunk(JSON.stringify(l.object));break;case "array_start":t.onArrayStart?.();break;case "complete":t.onComplete&&t.onComplete({objects:y,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new f(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}A="";}}}}catch(s){if(t.onError)t.onError(s instanceof Error?s:new Error(String(s)));else throw s}}async listModels(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/models`,{method:"GET",headers:{Accept:"application/json"}})).json();if(!t.success)throw new g(t.error?.message||"Failed to list models");return t.data}async listChains(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/chains`,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!t.success)throw new g(t.error?.message||"Failed to list chains");return t.data}async getUsage(e){let t=new URLSearchParams;e?.startDate&&t.set("startDate",e.startDate.toISOString()),e?.endDate&&t.set("endDate",e.endDate.toISOString());let r=`${this.baseUrl}/api/v1/usage${t.toString()?"?"+t:""}`,s=await(await this.fetchWithTimeout(r,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!s.success)throw new g(s.error?.message||"Failed to get usage");return s.data}async health(){let e=Date.now();try{let t=await this.fetchWithTimeout(`${this.baseUrl}/api/health`,{method:"GET"},5e3),r=Date.now()-e,o=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:o.version}}catch{return {ok:false,latencyMs:Date.now()-e}}}withConfig(e){return new n({secretKey:this.secretKey,baseUrl:this.baseUrl,timeout:this.timeout,maxRetries:this.maxRetries,debug:this.debug,...e})}async registerWebhook(e){let t=await this.makeAuthenticatedRequest("/api/v1/webhooks","POST",JSON.stringify(e));if(!t.success||!t.data)throw new g(t.error?.message||"Failed to register webhook");return t.data}async unregisterWebhook(e){let t=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"DELETE","");if(!t.success||!t.data)throw new g(t.error?.message||"Failed to unregister webhook");return t.data}async listWebhooks(){let e=await this.makeAuthenticatedRequest("/api/v1/webhooks","GET","");if(!e.success)throw new g(e.error?.message||"Failed to list webhooks");return e.data||[]}async updateWebhook(e,t){let r=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"PATCH",JSON.stringify(t));if(!r.success||!r.data)throw new g(r.error?.message||"Failed to update webhook");return r.data}async healthDetailed(){return (await fetch(`${this.baseUrl}/api/v1/health/detailed`,{headers:{Accept:"application/json"}})).json()}async generateBatch(e,t,r={}){let o=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!o.success||!o.data)throw new f(o.error?.message||"Batch generation failed",[]);return o.data}async makeAuthenticatedRequest(e,t,r,o){let s=Date.now(),c=await T(this.secretKey,t,e,r,s),d=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-hydra-signature":c,"x-hydra-timestamp":s.toString(),"x-hydra-key":this.keyPrefix},body:r},o??this.timeout),a=await d.json();if(!d.ok)throw this.parseError(d.status,a);return a}async fetchWithTimeout(e,t,r=this.timeout){let o=new AbortController,s=setTimeout(()=>o.abort(),r);try{return await fetch(e,{...t,signal:o.signal})}catch(c){throw c instanceof Error&&c.name==="AbortError"?new R(r):c instanceof Error?new w(c.message,c):new w(String(c))}finally{clearTimeout(s);}}parseError(e,t){let r=t?.error?.message||`HTTP ${e}`,o=t?.error?.code||"UNKNOWN";switch(e){case 401:return new x(r,e);case 429:let s=t?.retryAfterMs||6e4;return new E(r,s);case 500:case 502:case 503:case 504:return new g(r,e);default:return o==="CHAIN_FAILED"?new f(r,t?.error?.attemptedModels||[]):new u(r,o,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof u)return e.retryable;if(e instanceof Error){let t=e.message.toLowerCase();return t.includes("network")||t.includes("timeout")||e.name==="AbortError"}return false}sleep(e){return new Promise(t=>setTimeout(t,e))}log(e,t){let r="[Hydra]";t?console.log(r,e,t):console.log(r,e);}};export{x as AuthenticationError,f as ChainExecutionError,k as ConfigurationError,I as FileProcessingError,D as Hydra,u as HydraError,M as InvalidSchemaError,w as NetworkError,v as QuotaExceededError,E as RateLimitError,g as ServerError,R as TimeoutError,P as ValidationError,O as WorkerError,T as generateSignature,U as getKeyPrefix,_ as isHydraError,q as isRetryableError,C as isValidSecretKey};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@altsafe/aidirector",
3
- "version": "1.2.0",
4
- "description": "Official TypeScript SDK for AI Director - Intelligent AI API Gateway with automatic failover, caching, and JSON extraction",
3
+ "version": "1.4.0",
4
+ "description": "Official TypeScript SDK for Hydra - Intelligent AI API Gateway with automatic failover, caching, and JSON extraction",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -32,7 +32,7 @@
32
32
  "prepublishOnly": "npm run build",
33
33
  "clean": "rimraf dist",
34
34
  "publish:check": "npm pack --dry-run",
35
- "release": "npm run build && npm publish",
35
+ "release": "npm run build && npm publish --access public",
36
36
  "release:beta": "npm run build && npm publish --tag beta"
37
37
  },
38
38
  "keywords": [
@@ -51,17 +51,17 @@
51
51
  "hmac"
52
52
  ],
53
53
  "author": {
54
- "name": "AI Director",
55
- "url": "https://aidirector.dev"
54
+ "name": "Hydra",
55
+ "url": "https://hydrai.dev"
56
56
  },
57
57
  "license": "MIT",
58
- "homepage": "https://aidirector.dev",
58
+ "homepage": "https://hydrai.dev",
59
59
  "bugs": {
60
- "url": "https://github.com/aidirector/client/issues"
60
+ "url": "https://github.com/hydrai/client/issues"
61
61
  },
62
62
  "repository": {
63
63
  "type": "git",
64
- "url": "git+https://github.com/aidirector/client.git"
64
+ "url": "git+https://github.com/hydrai/client.git"
65
65
  },
66
66
  "publishConfig": {
67
67
  "access": "public",