oblien 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,10 +7,10 @@ Server-side SDK for building AI-powered applications with Oblien platform.
7
7
  - 🔐 **Direct header authentication** - Uses `x-client-id` and `x-client-secret` headers
8
8
  - 👤 **Dual-layer guest identification** - IP + fingerprint for better guest tracking
9
9
  - 🔄 **Smart guest matching** - Detects same guest even when IP or fingerprint changes
10
- - 📊 **Namespace support** - Pass user ID for authenticated session tracking
10
+ - 📊 **Namespace management** - Multi-tenant workspaces with service configurations
11
11
  - ⚡ **Automatic rate limiting** - Built-in limits for guest sessions
12
12
  - 💾 **Flexible storage** - NodeCache (default), Redis, or custom adapters
13
- - 🎯 **Single function for guest lookup** - `getGuest(ip, fingerprint)` handles both
13
+ - 🎯 **Complete usage tracking** - Monitor credits, quotas, and activity
14
14
 
15
15
  ## Installation
16
16
 
@@ -164,6 +164,102 @@ The package automatically tracks guests using both IP and fingerprint:
164
164
 
165
165
  This provides better continuity for users on mobile networks or using VPNs.
166
166
 
167
+ ## Namespace Management
168
+
169
+ Manage multi-tenant workspaces with service configurations, usage tracking, and quotas.
170
+
171
+ ```javascript
172
+ import { OblienNamespaces } from 'oblien/namespaces';
173
+
174
+ const namespaces = new OblienNamespaces(client);
175
+
176
+ // Create a namespace
177
+ const namespace = await namespaces.create({
178
+ name: 'Production Environment',
179
+ type: 'production',
180
+ metadata: { region: 'us-east-1' },
181
+ tags: ['production', 'critical'],
182
+ });
183
+
184
+ // Configure services
185
+ await namespaces.configureService(namespace.id, {
186
+ service: 'ai',
187
+ enabled: true,
188
+ config: { model: 'gpt-4', maxTokens: 4000 },
189
+ rateLimitRequests: 1000,
190
+ rateLimitPeriod: 'hour',
191
+ });
192
+
193
+ // Get usage statistics
194
+ const usage = await namespaces.getUsage(namespace.id, { days: 30 });
195
+ console.log(usage.summary); // Credits, tokens, requests per service
196
+
197
+ // List all namespaces
198
+ const result = await namespaces.list({
199
+ status: 'active',
200
+ type: 'production',
201
+ });
202
+ ```
203
+
204
+ ### Namespace Features:
205
+
206
+ - ✅ **Full CRUD** - Create, read, update, delete namespaces
207
+ - ✅ **Service configuration** - Enable/disable services per namespace
208
+ - ✅ **Usage tracking** - Monitor credits, tokens, and requests
209
+ - ✅ **Quota management** - Set limits per service
210
+ - ✅ **Activity logging** - Complete audit trail
211
+ - ✅ **Rich metadata** - Custom fields and tags
212
+
213
+ [📖 Full Namespaces Documentation](./docs/NAMESPACES.md)
214
+
215
+ ## Credits Management
216
+
217
+ Manage credits, quotas, usage tracking, and purchases.
218
+
219
+ ```javascript
220
+ import { OblienCredits } from 'oblien/credits';
221
+
222
+ const credits = new OblienCredits(client);
223
+
224
+ // Get balance
225
+ const balance = await credits.getBalance();
226
+
227
+ // Set namespace quota
228
+ await credits.setQuota({
229
+ namespace: 'production',
230
+ service: 'ai',
231
+ quotaLimit: 10000,
232
+ period: 'monthly',
233
+ });
234
+
235
+ // Get usage statistics
236
+ const stats = await credits.getUsageStats({ days: 7 });
237
+
238
+ // Purchase credits
239
+ const checkout = await credits.createCheckout({
240
+ packageId: 'pro',
241
+ });
242
+ console.log('Checkout URL:', checkout.checkoutUrl);
243
+
244
+ // Get transaction history
245
+ const history = await credits.getHistory({
246
+ namespace: 'production',
247
+ service: 'ai',
248
+ limit: 50,
249
+ });
250
+ ```
251
+
252
+ ### Credits Features:
253
+
254
+ - ✅ **Balance management** - Check and add credits
255
+ - ✅ **Quota system** - Set limits per namespace and service
256
+ - ✅ **Usage tracking** - Complete transaction history
257
+ - ✅ **Statistics** - Daily/monthly aggregated data
258
+ - ✅ **Purchase integration** - Stripe checkout for buying credits
259
+ - ✅ **Package management** - Predefined credit packages
260
+
261
+ [📖 Full Credits Documentation](./docs/CREDITS.md)
262
+
167
263
  ## Guest Storage Options
168
264
 
169
265
  ### Default: NodeCache (Recommended)
package/credits.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Credits Module Entry Point
3
+ * Import this for credits management only
4
+ */
5
+
6
+ export { OblienCredits } from './src/credits/index.js';
7
+
8
+ export default {
9
+ OblienCredits
10
+ };
11
+
package/index.d.ts CHANGED
@@ -19,6 +19,7 @@ export class OblienClient {
19
19
  get(path: string, params?: Record<string, any>): Promise<any>;
20
20
  post(path: string, body?: Record<string, any>): Promise<any>;
21
21
  put(path: string, body?: Record<string, any>): Promise<any>;
22
+ patch(path: string, body?: Record<string, any>): Promise<any>;
22
23
  delete(path: string): Promise<any>;
23
24
  }
24
25
 
@@ -167,12 +168,272 @@ export class OblienChat {
167
168
  cleanupGuests(): Promise<number>;
168
169
  }
169
170
 
171
+ // ============ Namespaces ============
172
+
173
+ export interface NamespaceData {
174
+ id: string;
175
+ client_id: string;
176
+ name: string;
177
+ slug: string;
178
+ description?: string;
179
+ end_user_id?: string;
180
+ fingerprint?: string;
181
+ ip_address?: string;
182
+ user_agent?: string;
183
+ country?: string;
184
+ status: 'active' | 'inactive' | 'suspended' | 'archived';
185
+ type: string;
186
+ is_default: boolean;
187
+ metadata?: Record<string, any>;
188
+ tags?: string[];
189
+ created_at: string;
190
+ updated_at: string;
191
+ last_active_at?: string;
192
+ archived_at?: string;
193
+ }
194
+
195
+ export interface CreateNamespaceOptions {
196
+ name: string;
197
+ slug?: string;
198
+ description?: string;
199
+ type?: 'default' | 'production' | 'testing' | 'development';
200
+ isDefault?: boolean;
201
+ metadata?: Record<string, any>;
202
+ tags?: string[];
203
+ endUserId?: string;
204
+ }
205
+
206
+ export interface UpdateNamespaceOptions {
207
+ name?: string;
208
+ description?: string;
209
+ status?: 'active' | 'inactive' | 'suspended' | 'archived';
210
+ type?: string;
211
+ metadata?: Record<string, any>;
212
+ tags?: string[];
213
+ }
214
+
215
+ export interface ListNamespacesOptions {
216
+ limit?: number;
217
+ offset?: number;
218
+ status?: 'active' | 'inactive' | 'suspended' | 'archived';
219
+ type?: string;
220
+ search?: string;
221
+ sortBy?: 'name' | 'created_at' | 'updated_at' | 'last_active_at';
222
+ sortOrder?: 'ASC' | 'DESC';
223
+ }
224
+
225
+ export interface ServiceConfig {
226
+ id: number;
227
+ namespace_id: string;
228
+ service: string;
229
+ enabled: boolean;
230
+ config?: Record<string, any>;
231
+ rate_limit_requests?: number;
232
+ rate_limit_period?: string;
233
+ features?: string[];
234
+ created_at: string;
235
+ updated_at: string;
236
+ }
237
+
238
+ export interface ConfigureServiceOptions {
239
+ service: string;
240
+ enabled?: boolean;
241
+ config?: Record<string, any>;
242
+ rateLimitRequests?: number;
243
+ rateLimitPeriod?: 'minute' | 'hour' | 'day';
244
+ features?: string[];
245
+ }
246
+
247
+ export interface NamespaceUsage {
248
+ usage: Array<{
249
+ service: string;
250
+ date: string;
251
+ requests: number;
252
+ credits: number;
253
+ deductions: number;
254
+ }>;
255
+ summary: Array<{
256
+ service: string;
257
+ total_requests: number;
258
+ total_credits: number;
259
+ first_used: string;
260
+ last_used: string;
261
+ }>;
262
+ quotas: Array<any>;
263
+ active_sessions: number;
264
+ }
265
+
266
+ export class Namespace {
267
+ constructor(options: { client: OblienClient; namespaceId?: string; data?: NamespaceData });
268
+
269
+ namespaceId: string | null;
270
+ data: NamespaceData | null;
271
+
272
+ create(options: CreateNamespaceOptions): Promise<NamespaceData>;
273
+ get(identifier?: string): Promise<NamespaceData>;
274
+ update(updates: UpdateNamespaceOptions, namespaceId?: string): Promise<NamespaceData>;
275
+ delete(namespaceId?: string): Promise<any>;
276
+ getActivity(options?: { limit?: number; offset?: number }, namespaceId?: string): Promise<any[]>;
277
+ getUsage(options?: { service?: string; days?: number }, namespaceId?: string): Promise<NamespaceUsage>;
278
+ configureService(options: ConfigureServiceOptions, namespaceId?: string): Promise<ServiceConfig>;
279
+ listServices(namespaceId?: string): Promise<ServiceConfig[]>;
280
+ getServiceConfig(service: string, namespaceId?: string): Promise<ServiceConfig>;
281
+ toggleService(service: string, enabled: boolean, namespaceId?: string): Promise<ServiceConfig>;
282
+ deleteService(service: string, namespaceId?: string): Promise<any>;
283
+ bulkConfigureServices(services: ConfigureServiceOptions[], namespaceId?: string): Promise<ServiceConfig[]>;
284
+ }
285
+
286
+ export class OblienNamespaces {
287
+ constructor(client: OblienClient);
288
+
289
+ create(options: CreateNamespaceOptions): Promise<NamespaceData>;
290
+ get(identifier: string): Promise<NamespaceData>;
291
+ list(options?: ListNamespacesOptions): Promise<{ success: boolean; data: NamespaceData[]; pagination: any }>;
292
+ update(namespaceId: string, updates: UpdateNamespaceOptions): Promise<NamespaceData>;
293
+ delete(namespaceId: string): Promise<any>;
294
+ getActivity(namespaceId: string, options?: { limit?: number; offset?: number }): Promise<any[]>;
295
+ getUsage(namespaceId: string, options?: { service?: string; days?: number }): Promise<NamespaceUsage>;
296
+ getAvailableServices(): Promise<any[]>;
297
+ configureService(namespaceId: string, options: ConfigureServiceOptions): Promise<ServiceConfig>;
298
+ listServices(namespaceId: string): Promise<ServiceConfig[]>;
299
+ getServiceConfig(namespaceId: string, service: string): Promise<ServiceConfig>;
300
+ toggleService(namespaceId: string, service: string, enabled: boolean): Promise<ServiceConfig>;
301
+ enableService(namespaceId: string, service: string): Promise<ServiceConfig>;
302
+ disableService(namespaceId: string, service: string): Promise<ServiceConfig>;
303
+ deleteService(namespaceId: string, service: string): Promise<any>;
304
+ bulkConfigureServices(namespaceId: string, services: ConfigureServiceOptions[]): Promise<ServiceConfig[]>;
305
+ namespace(namespaceId?: string): Namespace;
306
+ }
307
+
308
+ // ============ Credits ============
309
+
310
+ export interface QuotaData {
311
+ id: number;
312
+ client_id: string;
313
+ namespace: string;
314
+ service: string;
315
+ quota_limit: number | null;
316
+ quota_used: number;
317
+ period: string;
318
+ period_start?: string;
319
+ period_end?: string;
320
+ enabled: boolean;
321
+ created_at: string;
322
+ updated_at: string;
323
+ }
324
+
325
+ export interface CreditTransaction {
326
+ id: number;
327
+ client_id: string;
328
+ namespace: string;
329
+ end_user_id?: string;
330
+ amount: number;
331
+ client_balance_after: number;
332
+ namespace_quota_used?: number;
333
+ type: 'deduction' | 'addition' | 'refund' | 'adjustment';
334
+ service?: string;
335
+ description?: string;
336
+ metadata?: Record<string, any>;
337
+ created_at: string;
338
+ }
339
+
340
+ export interface SetQuotaOptions {
341
+ namespace: string;
342
+ service: string;
343
+ quotaLimit: number;
344
+ period?: 'daily' | 'monthly' | 'unlimited';
345
+ }
346
+
347
+ export interface HistoryOptions {
348
+ namespace?: string;
349
+ endUserId?: string;
350
+ service?: string;
351
+ type?: 'deduction' | 'addition' | 'refund' | 'adjustment';
352
+ startDate?: string;
353
+ endDate?: string;
354
+ limit?: number;
355
+ offset?: number;
356
+ after?: string;
357
+ afterId?: number;
358
+ }
359
+
360
+ export interface SummaryOptions {
361
+ namespace?: string;
362
+ days?: number;
363
+ limit?: number;
364
+ offset?: number;
365
+ after?: number;
366
+ }
367
+
368
+ export interface CreditPackage {
369
+ id: string;
370
+ name: string;
371
+ credits: number;
372
+ price: number;
373
+ currency: string;
374
+ active: boolean;
375
+ }
376
+
377
+ export interface CalculateCostOptions {
378
+ packageId?: string;
379
+ amount?: number;
380
+ credits?: number;
381
+ }
382
+
383
+ export interface PurchaseOptions {
384
+ packageId?: string;
385
+ amount?: number;
386
+ metadata?: Record<string, any>;
387
+ }
388
+
389
+ export interface PurchaseHistoryOptions {
390
+ limit?: number;
391
+ offset?: number;
392
+ light?: boolean;
393
+ }
394
+
395
+ export class OblienCredits {
396
+ constructor(client: OblienClient);
397
+
398
+ // Balance Management
399
+ getBalance(): Promise<number>;
400
+ addCredits(amount: number, reason?: string, metadata?: Record<string, any>): Promise<any>;
401
+
402
+ // Quota Management
403
+ getNamespaceQuotas(options?: { limit?: number; offset?: number; after?: string; search?: string; status?: string }): Promise<any>;
404
+ getNamespaceDetails(namespace: string, options?: { days?: number }): Promise<any>;
405
+ setQuota(options: SetQuotaOptions): Promise<QuotaData>;
406
+ resetQuota(namespace: string, service: string): Promise<any>;
407
+
408
+ // Usage History & Transactions
409
+ getHistory(options?: HistoryOptions): Promise<{ success: boolean; data: CreditTransaction[]; pagination: any }>;
410
+ getHistoryFilters(): Promise<{ namespaces: string[]; services: string[] }>;
411
+ getSummary(options?: SummaryOptions): Promise<any>;
412
+ getUsageStats(options?: { days?: number }): Promise<any>;
413
+
414
+ // Pricing & Packages
415
+ getPackages(): Promise<CreditPackage[]>;
416
+ getPricingInfo(): Promise<any>;
417
+ calculateCost(options: CalculateCostOptions): Promise<any>;
418
+ calculateCredits(amount: number): Promise<any>;
419
+
420
+ // Purchase Management
421
+ createCheckout(options: PurchaseOptions): Promise<any>;
422
+ getPurchaseHistory(options?: PurchaseHistoryOptions): Promise<any>;
423
+ getPurchaseDetails(purchaseId: string): Promise<any>;
424
+ getPurchaseSession(purchaseId: string): Promise<any>;
425
+ cancelPurchase(purchaseId: string): Promise<any>;
426
+ }
427
+
170
428
  // ============ Exports ============
171
429
 
172
430
  declare const _default: {
173
431
  OblienClient: typeof OblienClient;
174
432
  OblienChat: typeof OblienChat;
175
433
  ChatSession: typeof ChatSession;
434
+ OblienNamespaces: typeof OblienNamespaces;
435
+ Namespace: typeof Namespace;
436
+ OblienCredits: typeof OblienCredits;
176
437
  GuestManager: typeof GuestManager;
177
438
  NodeCacheStorage: typeof NodeCacheStorage;
178
439
  InMemoryStorage: typeof InMemoryStorage;
package/index.js CHANGED
@@ -5,6 +5,8 @@
5
5
 
6
6
  import { OblienClient } from './src/client.js';
7
7
  import { OblienChat, ChatSession } from './src/chat/index.js';
8
+ import { OblienNamespaces, Namespace } from './src/namespaces/index.js';
9
+ import { OblienCredits } from './src/credits/index.js';
8
10
  import {
9
11
  GuestManager,
10
12
  NodeCacheStorage,
@@ -15,6 +17,8 @@ import {
15
17
  // Re-export as named exports
16
18
  export { OblienClient };
17
19
  export { OblienChat, ChatSession };
20
+ export { OblienNamespaces, Namespace };
21
+ export { OblienCredits };
18
22
  export {
19
23
  GuestManager,
20
24
  NodeCacheStorage,
@@ -27,6 +31,9 @@ export default {
27
31
  OblienClient,
28
32
  OblienChat,
29
33
  ChatSession,
34
+ OblienNamespaces,
35
+ Namespace,
36
+ OblienCredits,
30
37
  GuestManager,
31
38
  NodeCacheStorage,
32
39
  InMemoryStorage,
package/namespaces.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Namespaces Module Entry Point
3
+ * Import this for namespace management only
4
+ */
5
+
6
+ export { OblienNamespaces, Namespace } from './src/namespaces/index.js';
7
+
8
+ export default {
9
+ OblienNamespaces,
10
+ Namespace
11
+ };
12
+
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "oblien",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "Server-side SDK for Oblien AI Platform - Build AI-powered applications with chat, agents, and workflows",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "types": "index.d.ts",
8
8
  "exports": {
9
9
  ".": "./index.js",
10
- "./chat": "./chat.js"
10
+ "./chat": "./chat.js",
11
+ "./namespaces": "./namespaces.js",
12
+ "./credits": "./credits.js"
11
13
  },
12
14
  "scripts": {
13
15
  "test": "node --test tests/**/*.test.js"
@@ -56,6 +58,8 @@
56
58
  "index.js",
57
59
  "index.d.ts",
58
60
  "chat.js",
61
+ "namespaces.js",
62
+ "credits.js",
59
63
  "agents.js",
60
64
  "workflows.js",
61
65
  "README.md",
package/src/chat/index.js CHANGED
@@ -78,6 +78,9 @@ export class OblienChat {
78
78
  workspace,
79
79
  isGuest: true,
80
80
  namespace: guest.namespace,
81
+ ipAddress: ip,
82
+ userAgent: metadata.userAgent,
83
+ fingerprint: fingerprint,
81
84
  });
82
85
 
83
86
  const sessionData = await session.create();
@@ -13,6 +13,9 @@ export class ChatSession {
13
13
  * @param {boolean} [options.isGuest] - Is this a guest session
14
14
  * @param {string} [options.namespace] - Guest namespace for rate limiting
15
15
  * @param {Object} [options.workspace] - Workspace configuration
16
+ * @param {string} [options.ipAddress] - IP address of the user
17
+ * @param {string} [options.userAgent] - User agent of the user
18
+ * @param {string} [options.fingerprint] - Fingerprint of the user
16
19
  */
17
20
  constructor(options) {
18
21
  if (!options.client) {
@@ -32,6 +35,9 @@ export class ChatSession {
32
35
  this.workspace = options.workspace;
33
36
  this.token = null;
34
37
  this.data = null;
38
+ this.ipAddress = options.ipAddress || null;
39
+ this.userAgent = options.userAgent || null;
40
+ this.fingerprint = options.fingerprint || null;
35
41
  }
36
42
 
37
43
  /**
@@ -45,6 +51,9 @@ export class ChatSession {
45
51
  is_guest: this.isGuest,
46
52
  namespace: this.namespace,
47
53
  workspace: this.workspace,
54
+ ip_address: this.ipAddress,
55
+ user_agent: this.userAgent,
56
+ fingerprint: this.fingerprint,
48
57
  };
49
58
 
50
59
  this.data = await this.client.post('ai/session/create', payload);
package/src/client.js CHANGED
@@ -107,6 +107,24 @@ export class OblienClient {
107
107
  return this._handleResponse(response);
108
108
  }
109
109
 
110
+ /**
111
+ * Make PATCH request
112
+ * @param {string} path - API path
113
+ * @param {Object} [body] - Request body
114
+ * @returns {Promise<any>} Response data
115
+ */
116
+ async patch(path, body = {}) {
117
+ const headers = this.getAuthHeaders();
118
+
119
+ const response = await fetch(this._buildURL(path), {
120
+ method: 'PATCH',
121
+ headers,
122
+ body: JSON.stringify(body),
123
+ });
124
+
125
+ return this._handleResponse(response);
126
+ }
127
+
110
128
  /**
111
129
  * Make DELETE request
112
130
  * @param {string} path - API path
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Credits Module
3
+ * Manages credits, quotas, usage tracking, and transactions
4
+ */
5
+
6
+ export class OblienCredits {
7
+ /**
8
+ * @param {import('../client.js').OblienClient} client - Oblien client instance
9
+ */
10
+ constructor(client) {
11
+ if (!client) {
12
+ throw new Error('Oblien client is required');
13
+ }
14
+
15
+ this.client = client;
16
+ }
17
+
18
+ // =============================================================================
19
+ // Balance Management
20
+ // =============================================================================
21
+
22
+ /**
23
+ * Get client's total credit balance
24
+ * @returns {Promise<number>} Current credit balance
25
+ */
26
+ async getBalance() {
27
+ const response = await this.client.get('credits/balance');
28
+ return response.balance;
29
+ }
30
+
31
+ /**
32
+ * Add credits to client (internal use - requires admin permissions)
33
+ * @param {number} amount - Amount of credits to add
34
+ * @param {string} [reason] - Reason for adding credits
35
+ * @param {Object} [metadata] - Additional metadata
36
+ * @returns {Promise<Object>} Result with new balance
37
+ */
38
+ async addCredits(amount, reason = 'manual', metadata = {}) {
39
+ const response = await this.client.post('credits/add', {
40
+ amount,
41
+ reason,
42
+ metadata
43
+ });
44
+ return response;
45
+ }
46
+
47
+ // =============================================================================
48
+ // Quota Management
49
+ // =============================================================================
50
+
51
+ /**
52
+ * Get all namespace quotas with pagination and filtering
53
+ * @param {Object} [options] - Query options
54
+ * @param {number} [options.limit] - Max results (default: 100, max: 500)
55
+ * @param {number} [options.offset] - Offset for pagination
56
+ * @param {string} [options.after] - Cursor for pagination (format: "namespace:service")
57
+ * @param {string} [options.search] - Search query
58
+ * @param {string} [options.status] - Filter by status: 'active', 'warning', 'exceeded'
59
+ * @returns {Promise<Object>} Namespaces with quotas and pagination info
60
+ */
61
+ async getNamespaceQuotas(options = {}) {
62
+ const response = await this.client.get('credits/namespaces', options);
63
+ return response;
64
+ }
65
+
66
+ /**
67
+ * Get detailed namespace quota information
68
+ * @param {string} namespace - Namespace slug or ID
69
+ * @param {Object} [options] - Query options
70
+ * @param {number} [options.days] - Number of days for stats (default: 7, max: 90)
71
+ * @returns {Promise<Object>} Namespace details with quotas, usage, and transactions
72
+ */
73
+ async getNamespaceDetails(namespace, options = {}) {
74
+ const response = await this.client.get(`credits/namespaces/${namespace}`, options);
75
+ return response;
76
+ }
77
+
78
+ /**
79
+ * Set quota for a namespace and service
80
+ * @param {Object} options - Quota options
81
+ * @param {string} options.namespace - Namespace slug
82
+ * @param {string} options.service - Service name (e.g., 'ai', 'deployment', 'sandbox')
83
+ * @param {number} options.quotaLimit - Quota limit (null or 0 for unlimited)
84
+ * @param {string} [options.period] - Quota period: 'daily', 'monthly', 'unlimited'
85
+ * @returns {Promise<Object>} Created/updated quota
86
+ */
87
+ async setQuota(options) {
88
+ if (!options.namespace || !options.service) {
89
+ throw new Error('namespace and service are required');
90
+ }
91
+
92
+ if (options.quotaLimit === undefined) {
93
+ throw new Error('quotaLimit is required');
94
+ }
95
+
96
+ const response = await this.client.post('credits/namespace-quota', {
97
+ namespace: options.namespace,
98
+ service: options.service,
99
+ quotaLimit: options.quotaLimit,
100
+ period: options.period || 'unlimited'
101
+ });
102
+
103
+ return response;
104
+ }
105
+
106
+ /**
107
+ * Reset namespace quota (e.g., for monthly reset)
108
+ * @param {string} namespace - Namespace slug
109
+ * @param {string} service - Service name
110
+ * @returns {Promise<Object>} Reset result
111
+ */
112
+ async resetQuota(namespace, service) {
113
+ if (!namespace || !service) {
114
+ throw new Error('namespace and service are required');
115
+ }
116
+
117
+ const response = await this.client.post('credits/reset-quota', {
118
+ namespace,
119
+ service
120
+ });
121
+
122
+ return response;
123
+ }
124
+
125
+ // =============================================================================
126
+ // Usage History & Transactions
127
+ // =============================================================================
128
+
129
+ /**
130
+ * Get credit usage history with filtering and pagination
131
+ * @param {Object} [options] - Query options
132
+ * @param {string} [options.namespace] - Filter by namespace
133
+ * @param {string} [options.endUserId] - Filter by end user ID
134
+ * @param {string} [options.service] - Filter by service
135
+ * @param {string} [options.type] - Filter by type: 'deduction', 'addition', 'refund', 'adjustment'
136
+ * @param {string} [options.startDate] - Start date (ISO string)
137
+ * @param {string} [options.endDate] - End date (ISO string)
138
+ * @param {number} [options.limit] - Max results (default: 50)
139
+ * @param {number} [options.offset] - Offset for pagination
140
+ * @param {string} [options.after] - Cursor for pagination (timestamp)
141
+ * @param {number} [options.afterId] - Cursor ID for pagination
142
+ * @returns {Promise<Object>} History with transactions and pagination
143
+ */
144
+ async getHistory(options = {}) {
145
+ const response = await this.client.get('credits/history', options);
146
+ return response;
147
+ }
148
+
149
+ /**
150
+ * Get available filter options for history
151
+ * @returns {Promise<Object>} Available namespaces and services
152
+ */
153
+ async getHistoryFilters() {
154
+ const response = await this.client.get('credits/history/filters');
155
+ return response;
156
+ }
157
+
158
+ /**
159
+ * Get usage summary by namespace/service
160
+ * @param {Object} [options] - Query options
161
+ * @param {string} [options.namespace] - Filter by namespace
162
+ * @param {number} [options.days] - Number of days to look back (default: 30)
163
+ * @param {number} [options.limit] - Max results (default: 50, max: 500)
164
+ * @param {number} [options.offset] - Offset for pagination
165
+ * @param {number} [options.after] - Cursor for pagination (total_spent value)
166
+ * @returns {Promise<Object>} Summary with aggregated usage
167
+ */
168
+ async getSummary(options = {}) {
169
+ const response = await this.client.get('credits/summary', options);
170
+ return response;
171
+ }
172
+
173
+ /**
174
+ * Get daily usage statistics (for charts)
175
+ * @param {Object} [options] - Query options
176
+ * @param {number} [options.days] - Number of days (default: 7)
177
+ * @returns {Promise<Object>} Daily usage statistics
178
+ */
179
+ async getUsageStats(options = {}) {
180
+ const response = await this.client.get('credits/usage-stats', options);
181
+ return response;
182
+ }
183
+
184
+ // =============================================================================
185
+ // Pricing & Packages
186
+ // =============================================================================
187
+
188
+ /**
189
+ * Get available credit packages
190
+ * @returns {Promise<Array>} Array of credit packages
191
+ */
192
+ async getPackages() {
193
+ const response = await this.client.get('credits/packages');
194
+ return response.packages || response.data || response;
195
+ }
196
+
197
+ /**
198
+ * Get pricing information and limits
199
+ * @returns {Promise<Object>} Pricing info with rates and limits
200
+ */
201
+ async getPricingInfo() {
202
+ const response = await this.client.get('credits/pricing-info');
203
+ return response;
204
+ }
205
+
206
+ /**
207
+ * Calculate credits from money or vice versa
208
+ * @param {Object} options - Calculation options
209
+ * @param {string} [options.packageId] - Package ID to calculate
210
+ * @param {number} [options.amount] - Money amount to convert to credits
211
+ * @param {number} [options.credits] - Credits to convert to money
212
+ * @returns {Promise<Object>} Calculation result with amount and credits
213
+ */
214
+ async calculateCost(options) {
215
+ if (!options.packageId && !options.amount && !options.credits) {
216
+ throw new Error('Must provide either packageId, amount, or credits');
217
+ }
218
+
219
+ const response = await this.client.post('credits/calculate-cost', options);
220
+ return response;
221
+ }
222
+
223
+ /**
224
+ * Calculate credits for a given amount (preview)
225
+ * @param {number} amount - Money amount
226
+ * @returns {Promise<Object>} Credits calculation
227
+ */
228
+ async calculateCredits(amount) {
229
+ const response = await this.client.get('credits/calculate', { amount });
230
+ return response;
231
+ }
232
+
233
+ // =============================================================================
234
+ // Purchase Management
235
+ // =============================================================================
236
+
237
+ /**
238
+ * Create Stripe checkout to purchase credits
239
+ * @param {Object} options - Purchase options
240
+ * @param {string} [options.packageId] - Package ID to purchase
241
+ * @param {number} [options.amount] - Custom amount to purchase
242
+ * @param {Object} [options.metadata] - Additional metadata
243
+ * @returns {Promise<Object>} Checkout session with URL
244
+ */
245
+ async createCheckout(options) {
246
+ if (!options.packageId && !options.amount) {
247
+ throw new Error('Must provide either packageId or amount');
248
+ }
249
+
250
+ const response = await this.client.post('credits/purchase', options);
251
+ return response;
252
+ }
253
+
254
+ /**
255
+ * Get purchase history
256
+ * @param {Object} [options] - Query options
257
+ * @param {number} [options.limit] - Max results (default: 50, max: 100)
258
+ * @param {number} [options.offset] - Offset for pagination
259
+ * @param {boolean} [options.light] - If true, returns basic data only (faster)
260
+ * @returns {Promise<Object>} Purchase history with pagination
261
+ */
262
+ async getPurchaseHistory(options = {}) {
263
+ const response = await this.client.get('credits/purchases', options);
264
+ return response;
265
+ }
266
+
267
+ /**
268
+ * Get single purchase details
269
+ * @param {string} purchaseId - Purchase ID
270
+ * @returns {Promise<Object>} Purchase details
271
+ */
272
+ async getPurchaseDetails(purchaseId) {
273
+ const response = await this.client.get(`credits/purchases/${purchaseId}`);
274
+ return response;
275
+ }
276
+
277
+ /**
278
+ * Get Stripe checkout session URL for pending purchase
279
+ * @param {string} purchaseId - Purchase ID
280
+ * @returns {Promise<Object>} Session with checkout URL
281
+ */
282
+ async getPurchaseSession(purchaseId) {
283
+ const response = await this.client.get(`credits/purchases/${purchaseId}/session`);
284
+ return response;
285
+ }
286
+
287
+ /**
288
+ * Cancel a pending purchase
289
+ * @param {string} purchaseId - Purchase ID
290
+ * @returns {Promise<Object>} Cancellation result
291
+ */
292
+ async cancelPurchase(purchaseId) {
293
+ const response = await this.client.post(`credits/purchases/${purchaseId}/cancel`);
294
+ return response;
295
+ }
296
+ }
297
+
298
+ export default OblienCredits;
299
+
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Namespaces Module
3
+ * Manages namespaces, service configurations, and usage tracking
4
+ */
5
+
6
+ import { Namespace } from './namespace.js';
7
+
8
+ export class OblienNamespaces {
9
+ /**
10
+ * @param {import('../client.js').OblienClient} client - Oblien client instance
11
+ */
12
+ constructor(client) {
13
+ if (!client) {
14
+ throw new Error('Oblien client is required');
15
+ }
16
+
17
+ this.client = client;
18
+ }
19
+
20
+ /**
21
+ * Create a new namespace
22
+ * @param {Object} options - Namespace options
23
+ * @param {string} options.name - Namespace name (required)
24
+ * @param {string} [options.slug] - URL-friendly slug
25
+ * @param {string} [options.description] - Description
26
+ * @param {string} [options.type] - Type: 'default', 'production', 'testing', 'development'
27
+ * @param {boolean} [options.isDefault] - Is default namespace
28
+ * @param {Object} [options.metadata] - Custom metadata
29
+ * @param {Array<string>} [options.tags] - Tags
30
+ * @param {string} [options.endUserId] - End user ID
31
+ * @returns {Promise<Object>} Created namespace data
32
+ */
33
+ async create(options) {
34
+ const namespace = new Namespace({ client: this.client });
35
+ return await namespace.create(options);
36
+ }
37
+
38
+ /**
39
+ * Get namespace by ID or slug
40
+ * @param {string} identifier - Namespace ID or slug
41
+ * @returns {Promise<Object>} Namespace data
42
+ */
43
+ async get(identifier) {
44
+ const namespace = new Namespace({ client: this.client });
45
+ return await namespace.get(identifier);
46
+ }
47
+
48
+ /**
49
+ * List all namespaces with filtering and pagination
50
+ * @param {Object} [options] - Query options
51
+ * @param {number} [options.limit] - Max results (default: 50, max: 100)
52
+ * @param {number} [options.offset] - Offset for pagination
53
+ * @param {string} [options.status] - Filter by status: 'active', 'inactive', 'suspended', 'archived'
54
+ * @param {string} [options.type] - Filter by type: 'default', 'production', 'testing', 'development'
55
+ * @param {string} [options.search] - Search in name, slug, description
56
+ * @param {string} [options.sortBy] - Sort by: 'name', 'created_at', 'updated_at', 'last_active_at'
57
+ * @param {string} [options.sortOrder] - Sort order: 'ASC', 'DESC'
58
+ * @returns {Promise<Object>} Namespaces data with pagination info
59
+ */
60
+ async list(options = {}) {
61
+ const response = await this.client.get('namespaces', options);
62
+ return response;
63
+ }
64
+
65
+ /**
66
+ * Update namespace
67
+ * @param {string} namespaceId - Namespace ID
68
+ * @param {Object} updates - Fields to update
69
+ * @returns {Promise<Object>} Updated namespace data
70
+ */
71
+ async update(namespaceId, updates) {
72
+ const namespace = new Namespace({ client: this.client, namespaceId });
73
+ return await namespace.update(updates);
74
+ }
75
+
76
+ /**
77
+ * Delete (archive) namespace
78
+ * @param {string} namespaceId - Namespace ID
79
+ * @returns {Promise<Object>} Deletion result
80
+ */
81
+ async delete(namespaceId) {
82
+ const namespace = new Namespace({ client: this.client, namespaceId });
83
+ return await namespace.delete();
84
+ }
85
+
86
+ /**
87
+ * Get namespace activity log
88
+ * @param {string} namespaceId - Namespace ID
89
+ * @param {Object} [options] - Query options
90
+ * @param {number} [options.limit] - Number of activities (default: 50)
91
+ * @param {number} [options.offset] - Offset for pagination
92
+ * @returns {Promise<Array>} Array of activity logs
93
+ */
94
+ async getActivity(namespaceId, options = {}) {
95
+ const namespace = new Namespace({ client: this.client, namespaceId });
96
+ return await namespace.getActivity(options);
97
+ }
98
+
99
+ /**
100
+ * Get namespace usage statistics
101
+ * @param {string} namespaceId - Namespace ID
102
+ * @param {Object} [options] - Query options
103
+ * @param {string} [options.service] - Filter by service
104
+ * @param {number} [options.days] - Number of days (default: 30)
105
+ * @returns {Promise<Object>} Usage statistics
106
+ */
107
+ async getUsage(namespaceId, options = {}) {
108
+ const namespace = new Namespace({ client: this.client, namespaceId });
109
+ return await namespace.getUsage(options);
110
+ }
111
+
112
+ /**
113
+ * Get available services
114
+ * @returns {Promise<Array>} Array of available services
115
+ */
116
+ async getAvailableServices() {
117
+ const response = await this.client.get('namespaces/services/available');
118
+ return response.data || response;
119
+ }
120
+
121
+ /**
122
+ * Configure a service for a namespace
123
+ * @param {string} namespaceId - Namespace ID
124
+ * @param {Object} options - Service configuration
125
+ * @param {string} options.service - Service name (required)
126
+ * @param {boolean} [options.enabled] - Enable/disable service
127
+ * @param {Object} [options.config] - Service-specific configuration
128
+ * @param {number} [options.rateLimitRequests] - Rate limit requests
129
+ * @param {string} [options.rateLimitPeriod] - Rate limit period
130
+ * @param {Array<string>} [options.features] - Enabled features
131
+ * @returns {Promise<Object>} Service configuration
132
+ */
133
+ async configureService(namespaceId, options) {
134
+ const namespace = new Namespace({ client: this.client, namespaceId });
135
+ return await namespace.configureService(options);
136
+ }
137
+
138
+ /**
139
+ * List all services for a namespace
140
+ * @param {string} namespaceId - Namespace ID
141
+ * @returns {Promise<Array>} Array of service configurations
142
+ */
143
+ async listServices(namespaceId) {
144
+ const namespace = new Namespace({ client: this.client, namespaceId });
145
+ return await namespace.listServices();
146
+ }
147
+
148
+ /**
149
+ * Get specific service configuration
150
+ * @param {string} namespaceId - Namespace ID
151
+ * @param {string} service - Service name
152
+ * @returns {Promise<Object>} Service configuration
153
+ */
154
+ async getServiceConfig(namespaceId, service) {
155
+ const namespace = new Namespace({ client: this.client, namespaceId });
156
+ return await namespace.getServiceConfig(service);
157
+ }
158
+
159
+ /**
160
+ * Toggle service enabled/disabled
161
+ * @param {string} namespaceId - Namespace ID
162
+ * @param {string} service - Service name
163
+ * @param {boolean} enabled - Enable or disable
164
+ * @returns {Promise<Object>} Updated service configuration
165
+ */
166
+ async toggleService(namespaceId, service, enabled) {
167
+ const namespace = new Namespace({ client: this.client, namespaceId });
168
+ return await namespace.toggleService(service, enabled);
169
+ }
170
+
171
+ /**
172
+ * Enable a service for a namespace
173
+ * @param {string} namespaceId - Namespace ID
174
+ * @param {string} service - Service name
175
+ * @returns {Promise<Object>} Updated service configuration
176
+ */
177
+ async enableService(namespaceId, service) {
178
+ return await this.toggleService(namespaceId, service, true);
179
+ }
180
+
181
+ /**
182
+ * Disable a service for a namespace
183
+ * @param {string} namespaceId - Namespace ID
184
+ * @param {string} service - Service name
185
+ * @returns {Promise<Object>} Updated service configuration
186
+ */
187
+ async disableService(namespaceId, service) {
188
+ return await this.toggleService(namespaceId, service, false);
189
+ }
190
+
191
+ /**
192
+ * Delete service configuration
193
+ * @param {string} namespaceId - Namespace ID
194
+ * @param {string} service - Service name
195
+ * @returns {Promise<Object>} Deletion result
196
+ */
197
+ async deleteService(namespaceId, service) {
198
+ const namespace = new Namespace({ client: this.client, namespaceId });
199
+ return await namespace.deleteService(service);
200
+ }
201
+
202
+ /**
203
+ * Bulk configure multiple services
204
+ * @param {string} namespaceId - Namespace ID
205
+ * @param {Array<Object>} services - Array of service configurations
206
+ * @returns {Promise<Array>} Array of configured services
207
+ */
208
+ async bulkConfigureServices(namespaceId, services) {
209
+ const namespace = new Namespace({ client: this.client, namespaceId });
210
+ return await namespace.bulkConfigureServices(services);
211
+ }
212
+
213
+ /**
214
+ * Create a Namespace instance for chaining operations
215
+ * @param {string} [namespaceId] - Namespace ID
216
+ * @returns {Namespace} Namespace instance
217
+ */
218
+ namespace(namespaceId) {
219
+ return new Namespace({ client: this.client, namespaceId });
220
+ }
221
+ }
222
+
223
+ export { Namespace };
224
+ export default OblienNamespaces;
225
+
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Namespace Entity Manager
3
+ * Manages individual namespace operations
4
+ */
5
+
6
+ export class Namespace {
7
+ /**
8
+ * @param {Object} options - Namespace options
9
+ * @param {import('../client.js').OblienClient} options.client - Oblien client instance
10
+ * @param {string} [options.namespaceId] - Existing namespace ID
11
+ * @param {Object} [options.data] - Namespace data
12
+ */
13
+ constructor(options) {
14
+ if (!options.client) {
15
+ throw new Error('Oblien client is required');
16
+ }
17
+
18
+ this.client = options.client;
19
+ this.namespaceId = options.namespaceId || null;
20
+ this.data = options.data || null;
21
+ }
22
+
23
+ /**
24
+ * Create a new namespace
25
+ * @param {Object} options - Namespace options
26
+ * @param {string} options.name - Namespace name (required)
27
+ * @param {string} [options.slug] - URL-friendly slug (auto-generated from name if not provided)
28
+ * @param {string} [options.description] - Namespace description
29
+ * @param {string} [options.type] - Type: 'default', 'production', 'testing', 'development'
30
+ * @param {boolean} [options.isDefault] - Is this the default namespace
31
+ * @param {Object} [options.metadata] - Custom metadata object
32
+ * @param {Array<string>} [options.tags] - Array of tags
33
+ * @param {string} [options.endUserId] - End user ID (creator)
34
+ * @returns {Promise<Object>} Created namespace data
35
+ */
36
+ async create(options) {
37
+ if (!options.name) {
38
+ throw new Error('Namespace name is required');
39
+ }
40
+
41
+ const payload = {
42
+ name: options.name,
43
+ slug: options.slug,
44
+ description: options.description,
45
+ type: options.type || 'default',
46
+ isDefault: options.isDefault || false,
47
+ metadata: options.metadata || {},
48
+ tags: options.tags || [],
49
+ endUserId: options.endUserId,
50
+ };
51
+
52
+ const response = await this.client.post('namespaces', payload);
53
+ this.data = response.data;
54
+ this.namespaceId = this.data.id;
55
+
56
+ return this.data;
57
+ }
58
+
59
+ /**
60
+ * Get namespace details
61
+ * @param {string} [identifier] - Namespace ID or slug (uses instance namespaceId if not provided)
62
+ * @returns {Promise<Object>} Namespace data
63
+ */
64
+ async get(identifier) {
65
+ const id = identifier || this.namespaceId;
66
+ if (!id) {
67
+ throw new Error('Namespace ID or slug is required');
68
+ }
69
+
70
+ const response = await this.client.get(`namespaces/${id}`);
71
+ this.data = response.data;
72
+ this.namespaceId = this.data.id;
73
+
74
+ return this.data;
75
+ }
76
+
77
+ /**
78
+ * Update namespace
79
+ * @param {Object} updates - Fields to update
80
+ * @param {string} [updates.name] - New name
81
+ * @param {string} [updates.description] - New description
82
+ * @param {string} [updates.status] - New status: 'active', 'inactive', 'suspended', 'archived'
83
+ * @param {string} [updates.type] - New type
84
+ * @param {Object} [updates.metadata] - New metadata
85
+ * @param {Array<string>} [updates.tags] - New tags
86
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
87
+ * @returns {Promise<Object>} Updated namespace data
88
+ */
89
+ async update(updates, namespaceId) {
90
+ const id = namespaceId || this.namespaceId;
91
+ if (!id) {
92
+ throw new Error('Namespace ID is required');
93
+ }
94
+
95
+ const response = await this.client.put(`namespaces/${id}`, updates);
96
+ this.data = response.data;
97
+
98
+ return this.data;
99
+ }
100
+
101
+ /**
102
+ * Delete (archive) namespace
103
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
104
+ * @returns {Promise<Object>} Deletion result
105
+ */
106
+ async delete(namespaceId) {
107
+ const id = namespaceId || this.namespaceId;
108
+ if (!id) {
109
+ throw new Error('Namespace ID is required');
110
+ }
111
+
112
+ return await this.client.delete(`namespaces/${id}`);
113
+ }
114
+
115
+ /**
116
+ * Get namespace activity log
117
+ * @param {Object} [options] - Query options
118
+ * @param {number} [options.limit] - Number of activities to return (default: 50)
119
+ * @param {number} [options.offset] - Offset for pagination
120
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
121
+ * @returns {Promise<Array>} Array of activity logs
122
+ */
123
+ async getActivity(options = {}, namespaceId) {
124
+ const id = namespaceId || this.namespaceId;
125
+ if (!id) {
126
+ throw new Error('Namespace ID is required');
127
+ }
128
+
129
+ const response = await this.client.get(`namespaces/${id}/activity`, options);
130
+ return response.data || response;
131
+ }
132
+
133
+ /**
134
+ * Get namespace usage statistics
135
+ * @param {Object} [options] - Query options
136
+ * @param {string} [options.service] - Filter by specific service
137
+ * @param {number} [options.days] - Number of days to include (default: 30)
138
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
139
+ * @returns {Promise<Object>} Usage statistics
140
+ */
141
+ async getUsage(options = {}, namespaceId) {
142
+ const id = namespaceId || this.namespaceId;
143
+ if (!id) {
144
+ throw new Error('Namespace ID is required');
145
+ }
146
+
147
+ const response = await this.client.get(`namespaces/${id}/usage`, options);
148
+ return response.data || response;
149
+ }
150
+
151
+ /**
152
+ * Configure a service for this namespace
153
+ * @param {Object} options - Service configuration
154
+ * @param {string} options.service - Service name (required)
155
+ * @param {boolean} [options.enabled] - Enable/disable service
156
+ * @param {Object} [options.config] - Service-specific configuration
157
+ * @param {number} [options.rateLimitRequests] - Rate limit requests per period
158
+ * @param {string} [options.rateLimitPeriod] - Rate limit period: 'minute', 'hour', 'day'
159
+ * @param {Array<string>} [options.features] - Enabled features
160
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
161
+ * @returns {Promise<Object>} Service configuration
162
+ */
163
+ async configureService(options, namespaceId) {
164
+ const id = namespaceId || this.namespaceId;
165
+ if (!id) {
166
+ throw new Error('Namespace ID is required');
167
+ }
168
+
169
+ if (!options.service) {
170
+ throw new Error('Service name is required');
171
+ }
172
+
173
+ const response = await this.client.post(`namespaces/${id}/services`, options);
174
+ return response.data || response;
175
+ }
176
+
177
+ /**
178
+ * List all services for this namespace
179
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
180
+ * @returns {Promise<Array>} Array of service configurations
181
+ */
182
+ async listServices(namespaceId) {
183
+ const id = namespaceId || this.namespaceId;
184
+ if (!id) {
185
+ throw new Error('Namespace ID is required');
186
+ }
187
+
188
+ const response = await this.client.get(`namespaces/${id}/services`);
189
+ return response.data || response;
190
+ }
191
+
192
+ /**
193
+ * Get specific service configuration
194
+ * @param {string} service - Service name
195
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
196
+ * @returns {Promise<Object>} Service configuration
197
+ */
198
+ async getServiceConfig(service, namespaceId) {
199
+ const id = namespaceId || this.namespaceId;
200
+ if (!id) {
201
+ throw new Error('Namespace ID is required');
202
+ }
203
+
204
+ if (!service) {
205
+ throw new Error('Service name is required');
206
+ }
207
+
208
+ const response = await this.client.get(`namespaces/${id}/services/${service}`);
209
+ return response.data || response;
210
+ }
211
+
212
+ /**
213
+ * Toggle service enabled/disabled
214
+ * @param {string} service - Service name
215
+ * @param {boolean} enabled - Enable or disable
216
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
217
+ * @returns {Promise<Object>} Updated service configuration
218
+ */
219
+ async toggleService(service, enabled, namespaceId) {
220
+ const id = namespaceId || this.namespaceId;
221
+ if (!id) {
222
+ throw new Error('Namespace ID is required');
223
+ }
224
+
225
+ if (!service) {
226
+ throw new Error('Service name is required');
227
+ }
228
+
229
+ const response = await this.client.patch(`namespaces/${id}/services/${service}/toggle`, { enabled });
230
+ return response.data || response;
231
+ }
232
+
233
+ /**
234
+ * Delete service configuration
235
+ * @param {string} service - Service name
236
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
237
+ * @returns {Promise<Object>} Deletion result
238
+ */
239
+ async deleteService(service, namespaceId) {
240
+ const id = namespaceId || this.namespaceId;
241
+ if (!id) {
242
+ throw new Error('Namespace ID is required');
243
+ }
244
+
245
+ if (!service) {
246
+ throw new Error('Service name is required');
247
+ }
248
+
249
+ return await this.client.delete(`namespaces/${id}/services/${service}`);
250
+ }
251
+
252
+ /**
253
+ * Bulk configure multiple services
254
+ * @param {Array<Object>} services - Array of service configurations
255
+ * @param {string} [namespaceId] - Namespace ID (uses instance namespaceId if not provided)
256
+ * @returns {Promise<Array>} Array of service configurations
257
+ */
258
+ async bulkConfigureServices(services, namespaceId) {
259
+ const id = namespaceId || this.namespaceId;
260
+ if (!id) {
261
+ throw new Error('Namespace ID is required');
262
+ }
263
+
264
+ if (!Array.isArray(services)) {
265
+ throw new Error('Services must be an array');
266
+ }
267
+
268
+ const response = await this.client.post(`namespaces/${id}/services/bulk`, { services });
269
+ return response.data || response;
270
+ }
271
+ }
272
+
273
+ export default Namespace;
274
+
@@ -129,10 +129,10 @@ export class GuestManager {
129
129
  const guestIdByIp = await this.storage.get(`ip:${ip}`);
130
130
  if (guestIdByIp) {
131
131
  const guest = await this.getGuest(guestIdByIp);
132
- if (guest) {
133
- guest.lastSeen = new Date().toISOString();
132
+ if (guest) {
133
+ guest.lastSeen = new Date().toISOString();
134
134
  await this.storage.set(`guest:${guest.id}`, guest, this.ttl);
135
- return guest;
135
+ return guest;
136
136
  }
137
137
  }
138
138
  }