@realtimex/sdk 1.0.8 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,8 +1,128 @@
1
+ // src/modules/api.ts
2
+ var PermissionDeniedError = class extends Error {
3
+ constructor(permission, message) {
4
+ super(message || `Permission '${permission}' was denied`);
5
+ this.name = "PermissionDeniedError";
6
+ this.permission = permission;
7
+ }
8
+ };
9
+ var PermissionRequiredError = class extends Error {
10
+ constructor(permission, message) {
11
+ super(message || `Permission '${permission}' is required`);
12
+ this.name = "PermissionRequiredError";
13
+ this.permission = permission;
14
+ }
15
+ };
16
+ var ApiModule = class {
17
+ constructor(realtimexUrl, appId, appName) {
18
+ this.realtimexUrl = realtimexUrl.replace(/\/$/, "");
19
+ this.appId = appId;
20
+ this.appName = appName || process.env.RTX_APP_NAME || "Local App";
21
+ }
22
+ getHeaders() {
23
+ return {
24
+ "Content-Type": "application/json",
25
+ "x-app-id": this.appId
26
+ };
27
+ }
28
+ /**
29
+ * Request a single permission from Electron via internal API
30
+ */
31
+ async requestPermission(permission) {
32
+ try {
33
+ const response = await fetch(`${this.realtimexUrl}/api/local-apps/request-permission`, {
34
+ method: "POST",
35
+ headers: { "Content-Type": "application/json" },
36
+ body: JSON.stringify({
37
+ app_id: this.appId,
38
+ app_name: this.appName,
39
+ permission
40
+ })
41
+ });
42
+ const data = await response.json();
43
+ return data.granted === true;
44
+ } catch (error) {
45
+ return false;
46
+ }
47
+ }
48
+ /**
49
+ * Make an API call with automatic permission handling
50
+ */
51
+ async apiCall(method, endpoint, options) {
52
+ const url = `${this.realtimexUrl}${endpoint}`;
53
+ const response = await fetch(url, {
54
+ method,
55
+ headers: this.getHeaders(),
56
+ ...options
57
+ });
58
+ const data = await response.json();
59
+ if (response.status === 403) {
60
+ const errorCode = data.error;
61
+ const permission = data.permission;
62
+ const message = data.message;
63
+ if (errorCode === "PERMISSION_REQUIRED" && permission) {
64
+ const granted = await this.requestPermission(permission);
65
+ if (granted) {
66
+ return this.apiCall(method, endpoint, options);
67
+ } else {
68
+ throw new PermissionDeniedError(permission, message);
69
+ }
70
+ }
71
+ if (errorCode === "PERMISSION_DENIED") {
72
+ throw new PermissionDeniedError(permission, message);
73
+ }
74
+ throw new Error(data.error || "Permission denied");
75
+ }
76
+ if (!response.ok) {
77
+ throw new Error(data.error || `API call failed: ${response.status}`);
78
+ }
79
+ return data;
80
+ }
81
+ async getAgents() {
82
+ const data = await this.apiCall("GET", "/agents");
83
+ return data.agents;
84
+ }
85
+ async getWorkspaces() {
86
+ const data = await this.apiCall("GET", "/workspaces");
87
+ return data.workspaces;
88
+ }
89
+ async getThreads(workspaceSlug) {
90
+ const data = await this.apiCall("GET", `/workspaces/${encodeURIComponent(workspaceSlug)}/threads`);
91
+ return data.threads;
92
+ }
93
+ async getTask(taskUuid) {
94
+ const data = await this.apiCall("GET", `/task/${encodeURIComponent(taskUuid)}`);
95
+ return { ...data.task, runs: data.runs };
96
+ }
97
+ };
98
+
1
99
  // src/modules/activities.ts
2
100
  var ActivitiesModule = class {
3
- constructor(realtimexUrl, appId) {
101
+ constructor(realtimexUrl, appId, appName) {
4
102
  this.baseUrl = realtimexUrl.replace(/\/$/, "");
5
103
  this.appId = appId;
104
+ this.appName = appName || process.env.RTX_APP_NAME || "Local App";
105
+ }
106
+ /**
107
+ * Request a single permission from Electron via internal API
108
+ */
109
+ async requestPermission(permission) {
110
+ try {
111
+ const response = await fetch(`${this.baseUrl}/api/local-apps/request-permission`, {
112
+ method: "POST",
113
+ headers: { "Content-Type": "application/json" },
114
+ body: JSON.stringify({
115
+ app_id: this.appId,
116
+ app_name: this.appName,
117
+ permission
118
+ })
119
+ });
120
+ const data = await response.json();
121
+ return data.granted === true;
122
+ } catch (error) {
123
+ console.error("[SDK] Permission request failed:", error);
124
+ return false;
125
+ }
6
126
  }
7
127
  async request(path, options = {}) {
8
128
  const url = `${this.baseUrl}${path}`;
@@ -20,6 +140,22 @@ var ActivitiesModule = class {
20
140
  }
21
141
  });
22
142
  const data = await response.json();
143
+ if (response.status === 403) {
144
+ const errorCode = data.error;
145
+ const permission = data.permission;
146
+ const message = data.message;
147
+ if (errorCode === "PERMISSION_REQUIRED" && permission) {
148
+ const granted = await this.requestPermission(permission);
149
+ if (granted) {
150
+ return this.request(path, options);
151
+ } else {
152
+ throw new PermissionDeniedError(permission, message);
153
+ }
154
+ }
155
+ if (errorCode === "PERMISSION_DENIED") {
156
+ throw new PermissionDeniedError(permission, message);
157
+ }
158
+ }
23
159
  if (!response.ok) {
24
160
  throw new Error(data.error || `Request failed: ${response.status}`);
25
161
  }
@@ -86,13 +222,64 @@ var WebhookModule = class {
86
222
  this.appName = appName;
87
223
  this.appId = appId;
88
224
  }
225
+ /**
226
+ * Request a single permission from Electron via internal API
227
+ */
228
+ async requestPermission(permission) {
229
+ try {
230
+ const response = await fetch(`${this.realtimexUrl}/api/local-apps/request-permission`, {
231
+ method: "POST",
232
+ headers: { "Content-Type": "application/json" },
233
+ body: JSON.stringify({
234
+ app_id: this.appId,
235
+ app_name: this.appName,
236
+ permission
237
+ })
238
+ });
239
+ const data = await response.json();
240
+ return data.granted === true;
241
+ } catch (error) {
242
+ console.error("[SDK] Permission request failed:", error);
243
+ return false;
244
+ }
245
+ }
246
+ async request(path, options = {}) {
247
+ const url = `${this.realtimexUrl}${path}`;
248
+ const response = await fetch(url, {
249
+ ...options,
250
+ headers: {
251
+ "Content-Type": "application/json",
252
+ ...options.headers
253
+ }
254
+ });
255
+ const data = await response.json();
256
+ if (response.status === 403) {
257
+ const errorCode = data.error;
258
+ const permission = data.permission;
259
+ const message = data.message;
260
+ if (errorCode === "PERMISSION_REQUIRED" && permission) {
261
+ const granted = await this.requestPermission(permission);
262
+ if (granted) {
263
+ return this.request(path, options);
264
+ } else {
265
+ throw new PermissionDeniedError(permission, message);
266
+ }
267
+ }
268
+ if (errorCode === "PERMISSION_DENIED") {
269
+ throw new PermissionDeniedError(permission, message);
270
+ }
271
+ }
272
+ if (!response.ok) {
273
+ throw new Error(data.error || `Request failed: ${response.status}`);
274
+ }
275
+ return data;
276
+ }
89
277
  async triggerAgent(payload) {
90
278
  if (payload.auto_run && (!payload.agent_name || !payload.workspace_slug)) {
91
279
  throw new Error("auto_run requires agent_name and workspace_slug");
92
280
  }
93
- const response = await fetch(`${this.realtimexUrl}/webhooks/realtimex`, {
281
+ return this.request("/webhooks/realtimex", {
94
282
  method: "POST",
95
- headers: { "Content-Type": "application/json" },
96
283
  body: JSON.stringify({
97
284
  app_name: this.appName,
98
285
  app_id: this.appId,
@@ -107,69 +294,16 @@ var WebhookModule = class {
107
294
  }
108
295
  })
109
296
  });
110
- const data = await response.json();
111
- if (!response.ok) throw new Error(data.error || "Failed to trigger agent");
112
- return data;
113
297
  }
114
298
  async ping() {
115
- const response = await fetch(`${this.realtimexUrl}/webhooks/realtimex`, {
299
+ return this.request("/webhooks/realtimex", {
116
300
  method: "POST",
117
- headers: { "Content-Type": "application/json" },
118
301
  body: JSON.stringify({
119
302
  app_name: this.appName,
120
303
  app_id: this.appId,
121
304
  event: "ping"
122
305
  })
123
306
  });
124
- const data = await response.json();
125
- if (!response.ok) throw new Error(data.error || "Ping failed");
126
- return data;
127
- }
128
- };
129
-
130
- // src/modules/api.ts
131
- var ApiModule = class {
132
- constructor(realtimexUrl, appId) {
133
- this.realtimexUrl = realtimexUrl.replace(/\/$/, "");
134
- this.appId = appId;
135
- }
136
- getHeaders() {
137
- return {
138
- "Content-Type": "application/json",
139
- "x-app-id": this.appId
140
- };
141
- }
142
- async getAgents() {
143
- const response = await fetch(`${this.realtimexUrl}/agents`, {
144
- headers: this.getHeaders()
145
- });
146
- const data = await response.json();
147
- if (!response.ok) throw new Error(data.error || "Failed to get agents");
148
- return data.agents;
149
- }
150
- async getWorkspaces() {
151
- const response = await fetch(`${this.realtimexUrl}/workspaces`, {
152
- headers: this.getHeaders()
153
- });
154
- const data = await response.json();
155
- if (!response.ok) throw new Error(data.error || "Failed to get workspaces");
156
- return data.workspaces;
157
- }
158
- async getThreads(workspaceSlug) {
159
- const response = await fetch(`${this.realtimexUrl}/workspaces/${encodeURIComponent(workspaceSlug)}/threads`, {
160
- headers: this.getHeaders()
161
- });
162
- const data = await response.json();
163
- if (!response.ok) throw new Error(data.error || "Failed to get threads");
164
- return data.threads;
165
- }
166
- async getTask(taskUuid) {
167
- const response = await fetch(`${this.realtimexUrl}/task/${encodeURIComponent(taskUuid)}`, {
168
- headers: this.getHeaders()
169
- });
170
- const data = await response.json();
171
- if (!response.ok) throw new Error(data.error || "Failed to get task");
172
- return { ...data.task, runs: data.runs };
173
307
  }
174
308
  };
175
309
 
@@ -295,6 +429,384 @@ var PortModule = class {
295
429
  }
296
430
  };
297
431
 
432
+ // src/modules/llm.ts
433
+ var LLMPermissionError = class extends Error {
434
+ constructor(permission, code = "PERMISSION_REQUIRED") {
435
+ super(`Permission required: ${permission}`);
436
+ this.permission = permission;
437
+ this.code = code;
438
+ this.name = "LLMPermissionError";
439
+ }
440
+ };
441
+ var LLMProviderError = class extends Error {
442
+ constructor(message, code = "LLM_ERROR") {
443
+ super(message);
444
+ this.code = code;
445
+ this.name = "LLMProviderError";
446
+ }
447
+ };
448
+ var VectorStore = class {
449
+ constructor(baseUrl, appId) {
450
+ this.baseUrl = baseUrl;
451
+ this.appId = appId;
452
+ }
453
+ get headers() {
454
+ return {
455
+ "Content-Type": "application/json",
456
+ "x-app-id": this.appId
457
+ };
458
+ }
459
+ /**
460
+ * Upsert (insert or update) vectors into storage
461
+ *
462
+ * @example
463
+ * ```ts
464
+ * await sdk.llm.vectors.upsert([
465
+ * { id: 'chunk-1', vector: embeddings[0], metadata: { text: 'Hello', documentId: 'doc-1' } }
466
+ * ], { workspaceId: 'ws-123' });
467
+ * ```
468
+ */
469
+ async upsert(vectors, options = {}) {
470
+ const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/upsert`, {
471
+ method: "POST",
472
+ headers: this.headers,
473
+ body: JSON.stringify({
474
+ vectors,
475
+ workspaceId: options.workspaceId
476
+ })
477
+ });
478
+ const data = await response.json();
479
+ if (data.code === "PERMISSION_REQUIRED") {
480
+ throw new LLMPermissionError(data.permission || "vectors.write");
481
+ }
482
+ return data;
483
+ }
484
+ /**
485
+ * Query similar vectors by embedding
486
+ *
487
+ * @example
488
+ * ```ts
489
+ * const results = await sdk.llm.vectors.query(queryVector, {
490
+ * topK: 5,
491
+ * filter: { documentId: 'doc-1' },
492
+ * workspaceId: 'ws-123'
493
+ * });
494
+ * ```
495
+ */
496
+ async query(vector, options = {}) {
497
+ const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/query`, {
498
+ method: "POST",
499
+ headers: this.headers,
500
+ body: JSON.stringify({
501
+ vector,
502
+ topK: options.topK ?? 5,
503
+ filter: options.filter,
504
+ workspaceId: options.workspaceId
505
+ })
506
+ });
507
+ const data = await response.json();
508
+ if (data.code === "PERMISSION_REQUIRED") {
509
+ throw new LLMPermissionError(data.permission || "vectors.read");
510
+ }
511
+ return data;
512
+ }
513
+ /**
514
+ * Delete vectors from storage
515
+ *
516
+ * Note: Currently only supports deleteAll: true
517
+ * Use workspaceId to scope deletion to a specific workspace
518
+ *
519
+ * @example
520
+ * ```ts
521
+ * await sdk.llm.vectors.delete({ deleteAll: true, workspaceId: 'ws-123' });
522
+ * ```
523
+ */
524
+ async delete(options) {
525
+ const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/delete`, {
526
+ method: "POST",
527
+ headers: this.headers,
528
+ body: JSON.stringify(options)
529
+ });
530
+ const data = await response.json();
531
+ if (data.code === "PERMISSION_REQUIRED") {
532
+ throw new LLMPermissionError(data.permission || "vectors.write");
533
+ }
534
+ return data;
535
+ }
536
+ /**
537
+ * List all available workspaces (namespaces) for this app
538
+ *
539
+ * @example
540
+ * ```ts
541
+ * const { workspaces } = await sdk.llm.vectors.listWorkspaces();
542
+ * console.log('Workspaces:', workspaces);
543
+ * ```
544
+ */
545
+ async listWorkspaces() {
546
+ const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/workspaces`, {
547
+ method: "GET",
548
+ headers: this.headers
549
+ });
550
+ const data = await response.json();
551
+ if (data.code === "PERMISSION_REQUIRED") {
552
+ throw new LLMPermissionError(data.permission || "vectors.read");
553
+ }
554
+ return data;
555
+ }
556
+ };
557
+ var LLMModule = class {
558
+ constructor(baseUrl, appId) {
559
+ this.baseUrl = baseUrl;
560
+ this.appId = appId;
561
+ this.vectors = new VectorStore(baseUrl, appId);
562
+ }
563
+ get headers() {
564
+ return {
565
+ "Content-Type": "application/json",
566
+ "x-app-id": this.appId
567
+ };
568
+ }
569
+ /**
570
+ * Get available LLM and embedding providers/models
571
+ *
572
+ * @example
573
+ * ```ts
574
+ * const { llm, embedding } = await sdk.llm.getProviders();
575
+ * console.log('Available LLM models:', llm[0].models);
576
+ * ```
577
+ */
578
+ async getProviders() {
579
+ const response = await fetch(`${this.baseUrl}/sdk/llm/providers`, {
580
+ method: "GET",
581
+ headers: this.headers
582
+ });
583
+ const data = await response.json();
584
+ if (data.code === "PERMISSION_REQUIRED") {
585
+ throw new LLMPermissionError(data.permission || "llm.providers");
586
+ }
587
+ return data;
588
+ }
589
+ /**
590
+ * Send a chat completion request (synchronous)
591
+ *
592
+ * @example
593
+ * ```ts
594
+ * const response = await sdk.llm.chat([
595
+ * { role: 'system', content: 'You are a helpful assistant.' },
596
+ * { role: 'user', content: 'Hello!' }
597
+ * ], { model: 'gpt-4o', temperature: 0.7 });
598
+ *
599
+ * console.log(response.response?.content);
600
+ * ```
601
+ */
602
+ async chat(messages, options = {}) {
603
+ const response = await fetch(`${this.baseUrl}/sdk/llm/chat`, {
604
+ method: "POST",
605
+ headers: this.headers,
606
+ body: JSON.stringify({
607
+ messages,
608
+ model: options.model,
609
+ provider: options.provider,
610
+ temperature: options.temperature ?? 0.7,
611
+ max_tokens: options.max_tokens ?? 1e3
612
+ })
613
+ });
614
+ const data = await response.json();
615
+ if (data.code === "PERMISSION_REQUIRED") {
616
+ throw new LLMPermissionError(data.permission || "llm.chat");
617
+ }
618
+ if (data.code === "LLM_ERROR") {
619
+ throw new LLMProviderError(data.error || "LLM request failed");
620
+ }
621
+ return data;
622
+ }
623
+ /**
624
+ * Send a streaming chat completion request (SSE)
625
+ *
626
+ * @example
627
+ * ```ts
628
+ * for await (const chunk of sdk.llm.chatStream([
629
+ * { role: 'user', content: 'Tell me a story' }
630
+ * ])) {
631
+ * process.stdout.write(chunk.textResponse || '');
632
+ * }
633
+ * ```
634
+ */
635
+ async *chatStream(messages, options = {}) {
636
+ const response = await fetch(`${this.baseUrl}/sdk/llm/chat/stream`, {
637
+ method: "POST",
638
+ headers: {
639
+ ...this.headers,
640
+ "Accept": "text/event-stream"
641
+ },
642
+ body: JSON.stringify({
643
+ messages,
644
+ model: options.model,
645
+ provider: options.provider,
646
+ temperature: options.temperature ?? 0.7,
647
+ max_tokens: options.max_tokens ?? 1e3
648
+ })
649
+ });
650
+ if (!response.ok) {
651
+ const errorData = await response.json();
652
+ if (errorData.code === "PERMISSION_REQUIRED") {
653
+ throw new LLMPermissionError(errorData.permission || "llm.chat");
654
+ }
655
+ throw new LLMProviderError(errorData.error || "Stream request failed");
656
+ }
657
+ const reader = response.body?.getReader();
658
+ if (!reader) {
659
+ throw new LLMProviderError("Response body is not readable");
660
+ }
661
+ const decoder = new TextDecoder();
662
+ let buffer = "";
663
+ let isErrorEvent = false;
664
+ try {
665
+ while (true) {
666
+ const { done, value } = await reader.read();
667
+ if (done) break;
668
+ buffer += decoder.decode(value, { stream: true });
669
+ const lines = buffer.split("\n");
670
+ buffer = lines.pop() || "";
671
+ for (const line of lines) {
672
+ const trimmedLine = line.trim();
673
+ if (!trimmedLine || trimmedLine.startsWith(":")) continue;
674
+ if (trimmedLine.startsWith("event: error")) {
675
+ isErrorEvent = true;
676
+ continue;
677
+ }
678
+ if (trimmedLine.startsWith("data: ")) {
679
+ const jsonStr = trimmedLine.slice(6);
680
+ if (jsonStr === "[DONE]") {
681
+ isErrorEvent = false;
682
+ continue;
683
+ }
684
+ try {
685
+ const data = JSON.parse(jsonStr);
686
+ if (isErrorEvent) {
687
+ isErrorEvent = false;
688
+ throw new LLMProviderError(
689
+ data.error || "Stream error",
690
+ data.code || "LLM_STREAM_ERROR"
691
+ );
692
+ }
693
+ const chunk = data;
694
+ if (chunk.error) {
695
+ throw new LLMProviderError(
696
+ chunk.message || "Stream error"
697
+ );
698
+ }
699
+ yield chunk;
700
+ } catch (parseError) {
701
+ isErrorEvent = false;
702
+ if (jsonStr !== "[DONE]") {
703
+ console.warn("[LLM Stream] Parse error:", jsonStr);
704
+ }
705
+ if (parseError instanceof LLMProviderError) throw parseError;
706
+ }
707
+ }
708
+ }
709
+ }
710
+ } finally {
711
+ reader.releaseLock();
712
+ }
713
+ }
714
+ /**
715
+ * Generate vector embeddings from text
716
+ *
717
+ * @example
718
+ * ```ts
719
+ * // Single text
720
+ * const { embeddings } = await sdk.llm.embed('Hello world');
721
+ *
722
+ * // Multiple texts
723
+ * const { embeddings } = await sdk.llm.embed(['Hello', 'World']);
724
+ * ```
725
+ */
726
+ async embed(input, options = {}) {
727
+ const inputArray = Array.isArray(input) ? input : [input];
728
+ const response = await fetch(`${this.baseUrl}/sdk/llm/embed`, {
729
+ method: "POST",
730
+ headers: this.headers,
731
+ body: JSON.stringify({
732
+ input: inputArray,
733
+ provider: options.provider,
734
+ model: options.model
735
+ })
736
+ });
737
+ const data = await response.json();
738
+ if (data.code === "PERMISSION_REQUIRED") {
739
+ throw new LLMPermissionError(data.permission || "llm.embed");
740
+ }
741
+ if (data.code === "PROVIDER_UNAVAILABLE") {
742
+ throw new LLMProviderError(data.error || "Embedding provider not available");
743
+ }
744
+ return data;
745
+ }
746
+ /**
747
+ * Helper: Embed text and store as vectors in one call
748
+ *
749
+ * @example
750
+ * ```ts
751
+ * await sdk.llm.embedAndStore({
752
+ * texts: ['Hello world', 'Goodbye world'],
753
+ * documentId: 'doc-123',
754
+ * workspaceId: 'ws-456'
755
+ * });
756
+ * ```
757
+ */
758
+ async embedAndStore(params) {
759
+ const { texts, documentId, workspaceId, idPrefix = "chunk", provider, model } = params;
760
+ const embedResult = await this.embed(texts, { provider, model });
761
+ if (!embedResult.success || !embedResult.embeddings) {
762
+ return {
763
+ success: false,
764
+ error: embedResult.error || "Embedding failed",
765
+ code: embedResult.code
766
+ };
767
+ }
768
+ const vectors = texts.map((text, i) => ({
769
+ id: `${idPrefix}_${i}`,
770
+ vector: embedResult.embeddings[i],
771
+ metadata: {
772
+ text,
773
+ documentId,
774
+ workspaceId
775
+ }
776
+ }));
777
+ return this.vectors.upsert(vectors, { workspaceId });
778
+ }
779
+ /**
780
+ * Helper: Search similar documents by text query
781
+ *
782
+ * @example
783
+ * ```ts
784
+ * const results = await sdk.llm.search('What is RealtimeX?', {
785
+ * topK: 5,
786
+ * workspaceId: 'ws-123'
787
+ * });
788
+ *
789
+ * for (const result of results) {
790
+ * console.log(result.metadata?.text, result.score);
791
+ * }
792
+ * ```
793
+ */
794
+ async search(query, options = {}) {
795
+ const embedResult = await this.embed(query, {
796
+ provider: options.provider,
797
+ model: options.model
798
+ });
799
+ if (!embedResult.success || !embedResult.embeddings?.[0]) {
800
+ throw new LLMProviderError("Failed to embed query");
801
+ }
802
+ const queryResult = await this.vectors.query(embedResult.embeddings[0], options);
803
+ if (!queryResult.success) {
804
+ throw new LLMProviderError(queryResult.error || "Vector search failed");
805
+ }
806
+ return queryResult.results || [];
807
+ }
808
+ };
809
+
298
810
  // src/index.ts
299
811
  var _RealtimeXSDK = class _RealtimeXSDK {
300
812
  constructor(config = {}) {
@@ -302,12 +814,45 @@ var _RealtimeXSDK = class _RealtimeXSDK {
302
814
  const envAppName = this.getEnvVar("RTX_APP_NAME");
303
815
  this.appId = config.realtimex?.appId || envAppId || "";
304
816
  this.appName = config.realtimex?.appName || envAppName;
305
- const realtimexUrl = config.realtimex?.url || _RealtimeXSDK.DEFAULT_REALTIMEX_URL;
306
- this.activities = new ActivitiesModule(realtimexUrl, this.appId);
307
- this.webhook = new WebhookModule(realtimexUrl, this.appName, this.appId);
308
- this.api = new ApiModule(realtimexUrl, this.appId);
309
- this.task = new TaskModule(realtimexUrl, this.appName, this.appId);
817
+ this.permissions = config.permissions || [];
818
+ this.realtimexUrl = config.realtimex?.url || _RealtimeXSDK.DEFAULT_REALTIMEX_URL;
819
+ this.activities = new ActivitiesModule(this.realtimexUrl, this.appId, this.appName);
820
+ this.webhook = new WebhookModule(this.realtimexUrl, this.appName, this.appId);
821
+ this.api = new ApiModule(this.realtimexUrl, this.appId, this.appName);
822
+ this.task = new TaskModule(this.realtimexUrl, this.appName, this.appId);
310
823
  this.port = new PortModule(config.defaultPort);
824
+ this.llm = new LLMModule(this.realtimexUrl, this.appId);
825
+ if (this.permissions.length > 0) {
826
+ this.register().catch((err) => {
827
+ console.error("[RealtimeX SDK] Auto-registration failed:", err.message);
828
+ });
829
+ }
830
+ }
831
+ /**
832
+ * Register app with RealtimeX hub and request declared permissions upfront.
833
+ * This is called automatically if permissions are provided in constructor.
834
+ */
835
+ async register(permissions) {
836
+ const perms = permissions || this.permissions;
837
+ if (perms.length === 0) return;
838
+ try {
839
+ const response = await fetch(`${this.realtimexUrl.replace(/\/$/, "")}/sdk/register`, {
840
+ method: "POST",
841
+ headers: { "Content-Type": "application/json" },
842
+ body: JSON.stringify({
843
+ app_id: this.appId,
844
+ app_name: this.appName,
845
+ permissions: perms
846
+ })
847
+ });
848
+ const data = await response.json();
849
+ if (!response.ok) {
850
+ throw new Error(data.error || "Registration failed");
851
+ }
852
+ console.log(`[RealtimeX SDK] App registered successfully (${data.message})`);
853
+ } catch (error) {
854
+ throw new Error(`Failed to register app: ${error.message}`);
855
+ }
311
856
  }
312
857
  /**
313
858
  * Get environment variable (works in Node.js and browser)
@@ -327,8 +872,14 @@ var RealtimeXSDK = _RealtimeXSDK;
327
872
  export {
328
873
  ActivitiesModule,
329
874
  ApiModule,
875
+ LLMModule,
876
+ LLMPermissionError,
877
+ LLMProviderError,
878
+ PermissionDeniedError,
879
+ PermissionRequiredError,
330
880
  PortModule,
331
881
  RealtimeXSDK,
332
882
  TaskModule,
883
+ VectorStore,
333
884
  WebhookModule
334
885
  };