@schmitech/chatbot-api 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -69
- package/dist/api.cjs +2 -2
- package/dist/api.cjs.map +1 -1
- package/{api.d.ts → dist/api.d.ts} +20 -1
- package/dist/api.mjs +181 -283
- package/dist/api.mjs.map +1 -1
- package/package.json +14 -15
- package/dist/api.mjs.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🤖 ORBIT Chatbot API Client
|
|
2
2
|
|
|
3
|
-
A TypeScript/JavaScript client for seamless interaction with the ORBIT server, supporting
|
|
3
|
+
A TypeScript/JavaScript client for seamless interaction with the ORBIT server, supporting API key authentication and session tracking.
|
|
4
4
|
|
|
5
5
|
## 📥 Installation
|
|
6
6
|
|
|
@@ -15,13 +15,13 @@ npm install @schmitech/chatbot-api
|
|
|
15
15
|
First, configure the API client with your server details:
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import { configureApi, streamChat
|
|
18
|
+
import { configureApi, streamChat } from '@schmitech/chatbot-api';
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
configureApi({
|
|
21
|
+
apiUrl: 'https://your-api-server.com',
|
|
22
|
+
apiKey: 'your-api-key',
|
|
23
|
+
sessionId: 'optional-session-id' // Optional, for conversation tracking
|
|
24
|
+
});
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
### Streaming Chat Example
|
|
@@ -36,33 +36,73 @@ async function chat() {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
```
|
|
39
|
+
### Local test in Node.js environment
|
|
40
|
+
|
|
41
|
+
First, verify you have `Node.js` and its package manager, `npm`, installed. Then create a new folder.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
node -v
|
|
45
|
+
npm -v
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Initialize a `Node.js` Project
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm init -y
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Modify `package.json`
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"name": "orbit-node",
|
|
59
|
+
"version": "1.0.0",
|
|
60
|
+
"main": "index.js",
|
|
61
|
+
"type": "module",
|
|
62
|
+
"scripts": {
|
|
63
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
64
|
+
},
|
|
65
|
+
"keywords": [],
|
|
66
|
+
"author": "",
|
|
67
|
+
"license": "ISC",
|
|
68
|
+
"description": "",
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@schmitech/chatbot-api": "^0.5.0"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Install chatbot api
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install @schmitech/chatbot-api
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Run this test
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
node test/test-npm-package.js "how many r are in a strawberry?" "http://localhost:3000" "my-session-123"
|
|
85
|
+
```
|
|
39
86
|
|
|
40
87
|
## ⚛️ React Integration
|
|
41
88
|
|
|
42
89
|
Here's how to use the API in a React component:
|
|
43
90
|
|
|
44
91
|
```tsx
|
|
45
|
-
import React, { useState
|
|
46
|
-
import { configureApi, streamChat
|
|
92
|
+
import React, { useState } from 'react';
|
|
93
|
+
import { configureApi, streamChat } from '@schmitech/chatbot-api';
|
|
94
|
+
|
|
95
|
+
// Configure once at app startup
|
|
96
|
+
configureApi({
|
|
97
|
+
apiUrl: 'https://your-api-server.com',
|
|
98
|
+
apiKey: 'your-api-key',
|
|
99
|
+
sessionId: 'user_123_session_456' // Optional
|
|
100
|
+
});
|
|
47
101
|
|
|
48
102
|
function ChatComponent() {
|
|
49
103
|
const [messages, setMessages] = useState<Array<{ text: string; isUser: boolean }>>([]);
|
|
50
104
|
const [input, setInput] = useState('');
|
|
51
105
|
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
// Configure API and initialize secure key exchange
|
|
54
|
-
const setupApi = async () => {
|
|
55
|
-
try {
|
|
56
|
-
configureApi('https://your-api-server.com', 'your-api-key');
|
|
57
|
-
await initializeChatbot('your-api-key', window.location.origin);
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.error('Failed to initialize secure API:', error);
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
setupApi();
|
|
64
|
-
}, []);
|
|
65
|
-
|
|
66
106
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
67
107
|
e.preventDefault();
|
|
68
108
|
setMessages(prev => [...prev, { text: input, isUser: true }]);
|
|
@@ -94,17 +134,14 @@ function ChatComponent() {
|
|
|
94
134
|
### React Native Example
|
|
95
135
|
|
|
96
136
|
```typescript
|
|
97
|
-
import { configureApi, streamChat
|
|
98
|
-
|
|
99
|
-
// Configure
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
console.error('Failed to initialize secure API:', error);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
137
|
+
import { configureApi, streamChat } from '@schmitech/chatbot-api';
|
|
138
|
+
|
|
139
|
+
// Configure once at app startup
|
|
140
|
+
configureApi({
|
|
141
|
+
apiUrl: 'https://your-api-server.com',
|
|
142
|
+
apiKey: 'your-api-key',
|
|
143
|
+
sessionId: 'user_123_session_456' // Optional
|
|
144
|
+
});
|
|
108
145
|
|
|
109
146
|
async function handleChat(message: string) {
|
|
110
147
|
for await (const response of streamChat(message, true)) {
|
|
@@ -123,17 +160,13 @@ You can also use the API directly in the browser via CDN:
|
|
|
123
160
|
|
|
124
161
|
```html
|
|
125
162
|
<script type="module">
|
|
126
|
-
import { configureApi, streamChat
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
} catch (error) {
|
|
134
|
-
console.error('Failed to initialize secure API:', error);
|
|
135
|
-
}
|
|
136
|
-
};
|
|
163
|
+
import { configureApi, streamChat } from 'https://cdn.jsdelivr.net/npm/@schmitech/chatbot-api/dist/api.mjs';
|
|
164
|
+
|
|
165
|
+
configureApi({
|
|
166
|
+
apiUrl: 'https://your-api-server.com',
|
|
167
|
+
apiKey: 'your-api-key',
|
|
168
|
+
sessionId: 'your-session-id' // Optional
|
|
169
|
+
});
|
|
137
170
|
|
|
138
171
|
async function handleChat() {
|
|
139
172
|
for await (const response of streamChat('Hello', true)) {
|
|
@@ -145,25 +178,16 @@ You can also use the API directly in the browser via CDN:
|
|
|
145
178
|
|
|
146
179
|
## 📚 API Reference
|
|
147
180
|
|
|
148
|
-
### `configureApi(
|
|
181
|
+
### `configureApi(config)`
|
|
149
182
|
|
|
150
183
|
Configure the API client with server details.
|
|
151
184
|
|
|
152
185
|
| Parameter | Type | Required | Description |
|
|
153
186
|
|-----------|------|----------|-------------|
|
|
154
187
|
| `apiUrl` | string | Yes | Chatbot API server URL |
|
|
155
|
-
| `apiKey` | string |
|
|
188
|
+
| `apiKey` | string | Yes | API key for authentication |
|
|
156
189
|
| `sessionId` | string | No | Session ID for conversation tracking |
|
|
157
190
|
|
|
158
|
-
### `initializeChatbot(apiKey: string, origin: string)`
|
|
159
|
-
|
|
160
|
-
Initialize secure key exchange for enhanced security.
|
|
161
|
-
|
|
162
|
-
| Parameter | Type | Required | Description |
|
|
163
|
-
|-----------|------|----------|-------------|
|
|
164
|
-
| `apiKey` | string | Yes | Your permanent API key |
|
|
165
|
-
| `origin` | string | Yes | The origin of your application (e.g., window.location.origin) |
|
|
166
|
-
|
|
167
191
|
### `streamChat(message: string, stream: boolean = true)`
|
|
168
192
|
|
|
169
193
|
Stream chat responses from the server.
|
|
@@ -182,19 +206,8 @@ interface StreamResponse {
|
|
|
182
206
|
}
|
|
183
207
|
```
|
|
184
208
|
|
|
185
|
-
## 🔒 Security
|
|
186
|
-
|
|
187
|
-
- **Temporary Key Exchange**: Permanent API keys are exchanged for temporary session keys
|
|
188
|
-
- **Origin Validation**: Server validates the origin of requests for enhanced security
|
|
189
|
-
- **Automatic Key Refresh**: Temporary keys are automatically refreshed before expiration
|
|
190
|
-
- **Request Signing**: Requests are signed for additional security
|
|
191
|
-
- **Rate Limiting**: Built-in rate limiting to prevent abuse
|
|
192
|
-
- **Session Tracking**: Secure session management for conversation continuity
|
|
193
|
-
|
|
194
|
-
### Security Best Practices
|
|
209
|
+
## 🔒 Security
|
|
195
210
|
|
|
196
211
|
- Always use HTTPS for your API URL
|
|
197
212
|
- Keep your API key secure and never expose it in client-side code
|
|
198
|
-
-
|
|
199
|
-
- Implement proper error handling for authentication failures
|
|
200
|
-
- Monitor for security events using the `chatbot:auth:failed` event
|
|
213
|
+
- Consider using environment variables for sensitive configuration
|
package/dist/api.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`,
|
|
1
|
+
"use strict";var K=Object.create;var I=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var L=(t,e,r)=>e in t?I(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var H=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of N(e))!J.call(t,o)&&o!==r&&I(t,o,{get:()=>e[o],enumerable:!(n=$(e,o))||n.enumerable});return t};var R=(t,e,r)=>(r=t!=null?K(F(t)):{},H(e||!t||!t.__esModule?I(r,"default",{value:t,enumerable:!0}):r,t));var w=(t,e,r)=>L(t,typeof e!="symbol"?e+"":e,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let b=null,v=null;typeof window>"u"&&Promise.all([import("http").catch(()=>null),import("https").catch(()=>null)]).then(([t,e])=>{var r,n;(r=t==null?void 0:t.default)!=null&&r.Agent?b=new t.default.Agent({keepAlive:!0}):t!=null&&t.Agent&&(b=new t.Agent({keepAlive:!0})),(n=e==null?void 0:e.default)!=null&&n.Agent?v=new e.default.Agent({keepAlive:!0}):e!=null&&e.Agent&&(v=new e.Agent({keepAlive:!0}))}).catch(t=>{console.warn("Failed to initialize HTTP agents:",t.message)});class j{constructor(e){w(this,"apiUrl");w(this,"apiKey");w(this,"sessionId");if(!e.apiUrl||typeof e.apiUrl!="string")throw new Error("API URL must be a valid string");if(e.apiKey!==void 0&&e.apiKey!==null&&typeof e.apiKey!="string")throw new Error("API key must be a valid string or null");if(e.sessionId!==void 0&&e.sessionId!==null&&typeof e.sessionId!="string")throw new Error("Session ID must be a valid string or null");this.apiUrl=e.apiUrl,this.apiKey=e.apiKey??null,this.sessionId=e.sessionId??null}setSessionId(e){if(e!==null&&typeof e!="string")throw new Error("Session ID must be a valid string or null");this.sessionId=e}getSessionId(){return this.sessionId}getFetchOptions(e={}){const r={};if(typeof window>"u"){const u=this.apiUrl.startsWith("https:")?v:b;u&&(r.agent=u)}else r.headers={Connection:"keep-alive"};const n={"X-Request-ID":Date.now().toString(36)+Math.random().toString(36).substring(2)};return r.headers&&Object.assign(n,r.headers),e.headers&&Object.assign(n,e.headers),this.apiKey&&(n["X-API-Key"]=this.apiKey),this.sessionId&&(n["X-Session-ID"]=this.sessionId),{...e,...r,headers:n}}createMCPRequest(e,r=!0){return{jsonrpc:"2.0",method:"tools/call",params:{name:"chat",arguments:{messages:[{role:"user",content:e}],stream:r}},id:Date.now().toString(36)+Math.random().toString(36).substring(2)}}createMCPToolsRequest(e){return{jsonrpc:"2.0",method:"tools/call",params:{name:"tools",arguments:{tools:e}},id:Date.now().toString(36)+Math.random().toString(36).substring(2)}}async*streamChat(e,r=!0){var n,o,u,P,E,k,C,T,x,S,O;try{const l=new AbortController,D=setTimeout(()=>l.abort(),6e4),c=await fetch(`${this.apiUrl}/v1/chat`,{...this.getFetchOptions({method:"POST",headers:{"Content-Type":"application/json",Accept:r?"text/event-stream":"application/json"},body:JSON.stringify(this.createMCPRequest(e,r))}),signal:l.signal});if(clearTimeout(D),!c.ok){const a=await c.text();throw new Error(`Network response was not ok: ${c.status} ${a}`)}if(!r){const a=await c.json();if(a.error)throw new Error(`MCP Error: ${a.error.message}`);(P=(u=(o=(n=a.result)==null?void 0:n.output)==null?void 0:o.messages)==null?void 0:u[0])!=null&&P.content&&(yield{text:a.result.output.messages[0].content,done:!0});return}const g=(E=c.body)==null?void 0:E.getReader();if(!g)throw new Error("No reader available");const M=new TextDecoder;let i="",d=!1;try{for(;;){const{done:a,value:U}=await g.read();if(a)break;const q=M.decode(U,{stream:!0});i+=q;let h=0,p;for(;(p=i.indexOf(`
|
|
2
|
+
`,h))!==-1;){const y=i.slice(h,p).trim();if(h=p+1,y&&y.startsWith("data: ")){const m=y.slice(6).trim();if(!m||m==="[DONE]"){yield{text:"",done:!0};return}try{const s=JSON.parse(m);if(s.error)throw new Error(`MCP Error: ${s.error.message}`);if(((k=s.result)==null?void 0:k.type)==="chunk"&&((C=s.result.chunk)!=null&&C.content))d=!0,yield{text:s.result.chunk.content,done:!1};else if(((T=s.result)==null?void 0:T.type)==="complete"){const A=((O=(S=(x=s.result.output)==null?void 0:x.messages)==null?void 0:S[0])==null?void 0:O.content)??"";!d&&A?yield{text:A,done:!0}:yield{text:"",done:!0};return}else if("response"in s&&typeof s.response=="string")if("done"in s&&s.done===!0){!d&&s.response?yield{text:s.response,done:!0}:yield{text:"",done:!0};return}else d=!0,yield{text:s.response,done:!1}}catch(s){console.warn("Error parsing JSON chunk:",s)}}}i=i.slice(h),i.length>1e6&&(console.warn("Buffer too large, truncating..."),i=i.slice(-5e5))}d&&(yield{text:"",done:!0})}finally{g.releaseLock()}}catch(l){throw l.name==="AbortError"?new Error("Connection timed out. Please check if the server is running."):l.name==="TypeError"&&l.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):l}}async sendToolsRequest(e){const r=await fetch(`${this.apiUrl}/v1/chat`,this.getFetchOptions({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(this.createMCPToolsRequest(e))}));if(!r.ok){const o=await r.text();throw new Error(`Network response was not ok: ${r.status} ${o}`)}const n=await r.json();if(n.error)throw new Error(`MCP Error: ${n.error.message}`);return n}}let f=null;const X=(t,e=null,r=null)=>{f=new j({apiUrl:t,apiKey:e,sessionId:r})};async function*W(t,e=!0){if(!f)throw new Error("API not configured. Please call configureApi() with your server URL before using any API functions.");yield*f.streamChat(t,e)}async function z(t){if(!f)throw new Error("API not configured. Please call configureApi() with your server URL before using any API functions.");return f.sendToolsRequest(t)}exports.ApiClient=j;exports.configureApi=X;exports.sendToolsRequest=z;exports.streamChat=W;
|
|
3
3
|
//# sourceMappingURL=api.cjs.map
|
package/dist/api.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.cjs","sources":["../api.ts"],"sourcesContent":["// For Node.js environments, we can use http.Agent for connection pooling\nlet httpAgent: any = null;\nlet httpsAgent: any = null;\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n}\n\nexport interface ChatResponse {\n response: string;\n}\n\n// MCP Protocol interfaces\ninterface MCPRequest {\n jsonrpc: \"2.0\";\n method: string;\n params: {\n name: string;\n arguments: {\n messages?: Array<{\n role: string;\n content: string;\n }>;\n stream?: boolean;\n tools?: Array<{\n name: string;\n parameters: Record<string, any>;\n }>;\n };\n };\n id: string;\n}\n\ninterface MCPResponse {\n jsonrpc: \"2.0\";\n id: string;\n result?: {\n type?: \"start\" | \"chunk\" | \"complete\";\n chunk?: {\n content: string;\n };\n output?: {\n messages: Array<{\n role: string;\n content: string;\n }>;\n };\n };\n error?: {\n code: number;\n message: string;\n };\n}\n\n// Store the configured API URL, key, and session ID\nlet configuredApiUrl: string | null = null;\nlet configuredApiKey: string | null = null;\nlet configuredSessionId: string | null = null;\n\n// Temporary key management\nlet sessionId: string | null = null;\nlet keyRefreshTimer: number | null = null;\n\n// Key Exchange Response interface\ninterface KeyExchangeResponse {\n temporaryKey: string;\n sessionId: string;\n expiresIn: number; // seconds\n expiresAt: number; // timestamp\n}\n\n// Configure the API with a custom URL, API key (optional), and session ID (optional)\nexport const configureApi = (apiUrl: string, apiKey: string | null = null, sessionId: string | null = null): void => {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (apiKey !== null && typeof apiKey !== 'string') {\n throw new Error('API key must be a valid string or null');\n }\n if (sessionId !== null && typeof sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n configuredApiUrl = apiUrl;\n configuredApiKey = apiKey;\n configuredSessionId = sessionId;\n}\n\n// Get the configured API URL or throw an error if not configured\nconst getApiUrl = (): string => {\n if (!configuredApiUrl) {\n throw new Error('API URL not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n return configuredApiUrl;\n};\n\n// Get the configured API key or return null if not configured\nconst getApiKey = (): string | null => {\n return configuredApiKey;\n};\n\n// Get the configured session ID or return null if not configured\nconst getSessionId = (): string | null => {\n return configuredSessionId;\n};\n\n// Initialize chatbot with temporary key exchange\nexport const initializeChatbot = async (permanentApiKey: string, customOrigin?: string): Promise<void> => {\n try {\n const API_URL = getApiUrl();\n \n // Exchange permanent key for temporary session key\n const response = await fetch(`${API_URL}/api/v1/exchange-key`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ \n apiKey: permanentApiKey,\n origin: customOrigin || (typeof window !== 'undefined' ? window.location.origin : 'node-client')\n })\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to exchange API key: ${response.status} ${errorText}`);\n }\n \n const data: KeyExchangeResponse = await response.json();\n \n // Store only the temporary key and session\n sessionId = data.sessionId;\n configuredApiKey = data.temporaryKey; // Use temporary key as the main API key\n configuredSessionId = data.sessionId;\n \n // Clear permanent key from memory (security measure)\n permanentApiKey = '';\n \n // Set up auto-refresh (90% of expiry time)\n const refreshInterval = data.expiresIn * 0.9 * 1000;\n if (keyRefreshTimer) {\n clearTimeout(keyRefreshTimer);\n }\n keyRefreshTimer = setTimeout(() => refreshTemporaryKey(), refreshInterval) as any; \n } catch (error) {\n throw error;\n }\n};\n\n// Refresh temporary key before it expires\nconst refreshTemporaryKey = async (): Promise<void> => {\n if (!sessionId) return;\n \n try {\n const API_URL = getApiUrl();\n \n const response = await fetch(`${API_URL}/api/v1/refresh-key`, {\n method: 'POST',\n headers: { \n 'Content-Type': 'application/json',\n 'X-Session-ID': sessionId,\n 'Origin': typeof window !== 'undefined' ? window.location.origin : 'node-client'\n }\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to refresh key: ${response.status} ${errorText}`);\n }\n \n const data: KeyExchangeResponse = await response.json();\n \n // Update keys\n configuredApiKey = data.temporaryKey;\n \n // Reset timer\n if (keyRefreshTimer) {\n clearTimeout(keyRefreshTimer);\n }\n const refreshInterval = data.expiresIn * 0.9 * 1000;\n keyRefreshTimer = setTimeout(() => refreshTemporaryKey(), refreshInterval) as any;\n } catch (error) {\n // Emit event for handling by consumer\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent('chatbot:auth:failed', { detail: error }));\n }\n }\n};\n\n// Helper to get fetch options with connection pooling and enhanced security\nconst getFetchOptions = (apiUrl: string, options: RequestInit = {}): RequestInit | any => {\n const isHttps = apiUrl.startsWith('https:');\n \n // Only use agents in Node.js environment\n if (typeof window === 'undefined') {\n if (isHttps && httpsAgent) {\n return { ...options, agent: httpsAgent } as any;\n } else if (httpAgent) {\n return { ...options, agent: httpAgent } as any;\n }\n }\n \n // Browser environment with enhanced security headers\n const requestId = Date.now().toString(36) + Math.random().toString(36).substring(2);\n const timestamp = Date.now();\n \n // Enhanced security headers\n const headers: Record<string, string> = {\n 'Connection': 'keep-alive',\n 'X-Request-ID': requestId,\n 'X-Timestamp': timestamp.toString()\n };\n \n // Add origin and referrer for security validation\n if (typeof window !== 'undefined') {\n headers['X-Origin'] = window.location.origin;\n headers['X-Referrer'] = document.referrer || 'direct';\n }\n \n // Add API key to headers only if it exists\n const apiKey = getApiKey();\n if (apiKey) {\n headers['X-API-Key'] = apiKey;\n \n // Add request signature for enhanced security\n if (typeof window !== 'undefined') {\n headers['X-Signature'] = generateRequestSignature(\n requestId,\n timestamp,\n options.body\n );\n }\n }\n \n // Add session ID to headers only if it exists\n const sessionId = getSessionId();\n if (sessionId) {\n headers['X-Session-ID'] = sessionId;\n }\n \n return {\n ...options,\n headers: {\n ...options.headers,\n ...headers\n }\n };\n};\n\n// Generate request signature for enhanced security\nconst generateRequestSignature = (\n requestId: string, \n timestamp: number, \n body?: any\n): string => {\n const payload = `${requestId}:${timestamp}:${body ? JSON.stringify(body) : ''}`;\n // Simple base64 encoding - in production, you should use HMAC\n return btoa(payload).substring(0, 16);\n};\n\n// Rate limiter implementation\nclass RateLimiter {\n private requests: Map<string, number[]> = new Map();\n \n canMakeRequest(\n identifier: string, \n maxRequests: number = 20, \n windowMs: number = 60000\n ): boolean {\n const now = Date.now();\n const userRequests = this.requests.get(identifier) || [];\n const recentRequests = userRequests.filter(time => now - time < windowMs);\n \n if (recentRequests.length >= maxRequests) {\n return false;\n }\n \n recentRequests.push(now);\n this.requests.set(identifier, recentRequests);\n \n // Cleanup old entries\n if (this.requests.size > 1000) {\n this.cleanup(windowMs);\n }\n \n return true;\n }\n \n private cleanup(windowMs: number): void {\n const now = Date.now();\n for (const [key, requests] of this.requests.entries()) {\n const recent = requests.filter(time => now - time < windowMs);\n if (recent.length === 0) {\n this.requests.delete(key);\n } else {\n this.requests.set(key, recent);\n }\n }\n }\n}\n\n// Global rate limiter instance\nconst rateLimiter = new RateLimiter();\n\n// Security monitoring (client-side)\nconst securityMonitor = {\n config: {\n monitoring: {\n enabled: true\n }\n },\n\n async loadConfig(): Promise<void> {\n try {\n const API_URL = getApiUrl();\n const response = await fetch(`${API_URL}/api/v1/security/config`);\n if (response.ok) {\n const config = await response.json();\n if (config.monitoring) {\n this.config.monitoring = {\n ...this.config.monitoring,\n ...config.monitoring\n };\n }\n }\n } catch (e) {\n // Fail silently, use default config\n }\n },\n\n detectDevTools(): boolean {\n if (typeof window === 'undefined') return false;\n const threshold = 160;\n return (window.outerHeight - window.innerHeight > threshold) || \n (window.outerWidth - window.innerWidth > threshold);\n },\n \n async init(): Promise<void> {\n if (typeof window === 'undefined') return;\n \n // Load configuration first\n await this.loadConfig();\n }\n};\n\n// Initialize security monitoring if in browser\nif (typeof window !== 'undefined') {\n securityMonitor.init().catch(() => {\n // Fail silently, security monitoring will use default config\n });\n}\n\n// Create MCP request\nconst createMCPRequest = (message: string, stream: boolean = true): MCPRequest => {\n return {\n jsonrpc: \"2.0\",\n method: \"tools/call\",\n params: {\n name: \"chat\",\n arguments: {\n messages: [\n { role: \"user\", content: message }\n ],\n stream\n }\n },\n id: Date.now().toString(36) + Math.random().toString(36).substring(2)\n };\n};\n\n// Create MCP tools request\nconst createMCPToolsRequest = (tools: Array<{ name: string; parameters: Record<string, any> }>): MCPRequest => {\n return {\n jsonrpc: \"2.0\",\n method: \"tools/call\",\n params: {\n name: \"tools\",\n arguments: {\n tools\n }\n },\n id: Date.now().toString(36) + Math.random().toString(36).substring(2)\n };\n};\n\nexport async function* streamChat(\n message: string,\n stream: boolean = true\n): AsyncGenerator<StreamResponse> {\n try {\n // Check rate limiting\n const sessionIdentifier = getSessionId() || 'anonymous';\n \n if (!rateLimiter.canMakeRequest(sessionIdentifier, 20, 60000)) {\n yield { \n text: 'Rate limit exceeded. Please wait a moment before sending more messages.', \n done: true \n };\n return;\n }\n \n const API_URL = getApiUrl();\n \n // Add timeout to the fetch request\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout\n\n const response = await fetch(`${API_URL}/v1/chat`, {\n ...getFetchOptions(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify(createMCPRequest(message, stream)),\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n if (!stream) {\n // Handle non-streaming response\n const data = await response.json() as MCPResponse;\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n if (data.result?.output?.messages?.[0]?.content) {\n yield {\n text: data.result.output.messages[0].content,\n done: true\n };\n }\n return;\n }\n \n const reader = response.body?.getReader();\n if (!reader) throw new Error('No reader available');\n\n const decoder = new TextDecoder();\n let buffer = '';\n let currentFullText = '';\n let hasReceivedContent = false;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n \n // Process complete lines immediately as they arrive\n let lineStartIndex = 0;\n let newlineIndex;\n \n while ((newlineIndex = buffer.indexOf('\\n', lineStartIndex)) !== -1) {\n const line = buffer.slice(lineStartIndex, newlineIndex).trim();\n lineStartIndex = newlineIndex + 1;\n \n if (line && line.startsWith('data: ')) {\n try {\n const jsonText = line.slice(6).trim();\n \n // Check for [DONE] message (legacy format)\n if (jsonText === '[DONE]') {\n yield { text: '', done: true };\n return;\n }\n\n // Skip empty data lines\n if (!jsonText) {\n continue;\n }\n\n const data = JSON.parse(jsonText) as MCPResponse;\n \n // Handle errors\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n \n let content = '';\n let isDone = false;\n \n // Handle MCP protocol format\n if (data.result) {\n if (data.result.type === 'start') {\n continue;\n } else if (data.result.type === 'chunk' && data.result.chunk) {\n content = data.result.chunk.content;\n } else if (data.result.type === 'complete' && data.result.output?.messages?.[0]) {\n // For complete messages, only yield if we haven't received any content yet\n if (!hasReceivedContent) {\n content = data.result.output.messages[0].content;\n isDone = true;\n } else {\n // Skip the complete message if we've already received chunks\n continue;\n }\n }\n }\n \n // Handle direct server response format (from LLM clients)\n // This is what the server actually sends: { \"response\": \"...\", \"done\": false/true }\n if (!content && 'response' in data && typeof data.response === 'string') {\n content = data.response;\n }\n \n // Check for done signal in the data\n if ('done' in data && data.done === true) {\n isDone = true;\n }\n\n if (content) {\n const newText = extractNewText(content, currentFullText);\n if (newText) {\n currentFullText += newText;\n hasReceivedContent = true;\n yield {\n text: newText,\n done: isDone\n };\n }\n }\n \n // If we received a done signal, exit\n if (isDone) {\n if (!hasReceivedContent) {\n // Yield empty response to indicate completion\n yield { text: '', done: true };\n }\n return;\n }\n } catch (parseError) {\n // Don't throw, just continue processing\n }\n }\n }\n \n // Keep remaining incomplete line in buffer\n buffer = buffer.slice(lineStartIndex);\n \n // Prevent buffer from growing too large\n if (buffer.length > 1000000) { // 1MB limit\n buffer = buffer.slice(-500000); // Keep last 500KB\n }\n }\n \n // If we exit the while loop naturally, ensure we send a done signal\n if (hasReceivedContent) {\n yield { text: '', done: true };\n }\n \n } finally {\n reader.releaseLock();\n }\n\n // Handle any remaining buffer (fallback)\n if (buffer && buffer.startsWith('data: ')) {\n try {\n const jsonText = buffer.slice(6).trim();\n if (jsonText && jsonText !== '[DONE]') {\n const data = JSON.parse(jsonText) as MCPResponse;\n if (data.result?.chunk?.content) {\n const newText = extractNewText(data.result.chunk.content, currentFullText);\n if (newText) {\n yield {\n text: newText,\n done: true\n };\n }\n }\n }\n } catch (error) {\n // Fail silently for final buffer parsing\n }\n }\n \n } catch (error: any) {\n if (error.name === 'AbortError') {\n yield { \n text: 'Connection timed out. Please check if the server is running.', \n done: true \n };\n } else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n yield { \n text: 'Could not connect to the server. Please check if the server is running.', \n done: true \n };\n } else {\n yield { \n text: `Error: ${error.message}`, \n done: true \n };\n }\n }\n}\n\n// Helper function to extract only new text from incoming chunks\nfunction extractNewText(incomingText: string, currentText: string): string {\n // Simplified version - just check if we have new content at the end\n if (!currentText) return incomingText;\n \n // If incoming text is longer and starts with current text, return the new part\n if (incomingText.length > currentText.length && incomingText.startsWith(currentText)) {\n return incomingText.slice(currentText.length);\n }\n \n // Otherwise return the full incoming text (fallback)\n return incomingText;\n}\n\n// New function to send tools request\nexport async function sendToolsRequest(tools: Array<{ name: string; parameters: Record<string, any> }>): Promise<MCPResponse> {\n const API_URL = getApiUrl();\n \n const response = await fetch(`${API_URL}/v1/chat`, getFetchOptions(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(createMCPToolsRequest(tools)),\n }));\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n const data = await response.json() as MCPResponse;\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n\n return data;\n}"],"names":["configuredApiUrl","configuredApiKey","configuredSessionId","sessionId","keyRefreshTimer","configureApi","apiUrl","apiKey","getApiUrl","getApiKey","getSessionId","initializeChatbot","permanentApiKey","customOrigin","API_URL","response","errorText","data","refreshInterval","refreshTemporaryKey","error","getFetchOptions","options","requestId","timestamp","headers","generateRequestSignature","body","payload","RateLimiter","__publicField","identifier","maxRequests","windowMs","now","recentRequests","time","key","requests","recent","rateLimiter","securityMonitor","config","threshold","createMCPRequest","message","stream","createMCPToolsRequest","tools","streamChat","sessionIdentifier","controller","timeoutId","_d","_c","_b","_a","reader","_e","decoder","buffer","currentFullText","hasReceivedContent","done","value","chunk","lineStartIndex","newlineIndex","line","jsonText","content","isDone","_g","_f","newText","extractNewText","_i","_h","incomingText","currentText","sendToolsRequest"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api.cjs","sources":["../api.ts"],"sourcesContent":["// For Node.js environments, we can use http.Agent for connection pooling\nlet httpAgent: any = null;\nlet httpsAgent: any = null;\n\n// Initialize agents for connection pooling in Node.js environments\nif (typeof window === 'undefined') {\n // Lazy load to avoid including 'http' in browser bundles\n Promise.all([\n import('http').catch(() => null),\n import('https').catch(() => null)\n ]).then(([http, https]) => {\n if (http?.default?.Agent) {\n httpAgent = new http.default.Agent({ keepAlive: true });\n } else if (http?.Agent) {\n httpAgent = new http.Agent({ keepAlive: true });\n }\n \n if (https?.default?.Agent) {\n httpsAgent = new https.default.Agent({ keepAlive: true });\n } else if (https?.Agent) {\n httpsAgent = new https.Agent({ keepAlive: true });\n }\n }).catch(err => {\n // Silently fail - connection pooling is optional\n console.warn('Failed to initialize HTTP agents:', err.message);\n });\n}\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n}\n\nexport interface ChatResponse {\n response: string;\n}\n\n// MCP Protocol interfaces\ninterface MCPRequest {\n jsonrpc: \"2.0\";\n method: string;\n params: {\n name: string;\n arguments: {\n messages?: Array<{\n role: string;\n content: string;\n }>;\n stream?: boolean;\n tools?: Array<{\n name: string;\n parameters: Record<string, any>;\n }>;\n };\n };\n id: string;\n}\n\ninterface MCPResponse {\n jsonrpc: \"2.0\";\n id: string;\n result?: {\n type?: \"start\" | \"chunk\" | \"complete\";\n chunk?: {\n content: string;\n };\n output?: {\n messages: Array<{\n role: string;\n content: string;\n }>;\n };\n };\n error?: {\n code: number;\n message: string;\n };\n}\n\nexport class ApiClient {\n private readonly apiUrl: string;\n private readonly apiKey: string | null;\n private sessionId: string | null; // Session ID can be mutable\n\n constructor(config: { apiUrl: string; apiKey?: string | null; sessionId?: string | null }) {\n if (!config.apiUrl || typeof config.apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (config.apiKey !== undefined && config.apiKey !== null && typeof config.apiKey !== 'string') {\n throw new Error('API key must be a valid string or null');\n }\n if (config.sessionId !== undefined && config.sessionId !== null && typeof config.sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n \n this.apiUrl = config.apiUrl;\n this.apiKey = config.apiKey ?? null;\n this.sessionId = config.sessionId ?? null;\n }\n\n public setSessionId(sessionId: string | null): void {\n if (sessionId !== null && typeof sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n this.sessionId = sessionId;\n }\n\n public getSessionId(): string | null {\n return this.sessionId;\n }\n\n // Helper to get fetch options with connection pooling if available\n private getFetchOptions(options: RequestInit = {}): RequestInit {\n const baseOptions: RequestInit = {};\n \n // Environment-specific options\n if (typeof window === 'undefined') {\n // Node.js: Use connection pooling agent\n const isHttps = this.apiUrl.startsWith('https:');\n const agent = isHttps ? httpsAgent : httpAgent;\n if (agent) {\n (baseOptions as any).agent = agent;\n }\n } else {\n // Browser: Use keep-alive header\n baseOptions.headers = { 'Connection': 'keep-alive' };\n }\n\n // Common headers\n const headers: Record<string, string> = {\n 'X-Request-ID': Date.now().toString(36) + Math.random().toString(36).substring(2),\n };\n\n // Merge base options headers (for browser keep-alive)\n if (baseOptions.headers) {\n Object.assign(headers, baseOptions.headers);\n }\n\n // Merge original request headers\n if (options.headers) {\n Object.assign(headers, options.headers);\n }\n\n if (this.apiKey) {\n headers['X-API-Key'] = this.apiKey;\n }\n\n if (this.sessionId) {\n headers['X-Session-ID'] = this.sessionId;\n }\n\n return {\n ...options,\n ...baseOptions,\n headers,\n };\n }\n\n // Create MCP request\n private createMCPRequest(message: string, stream: boolean = true): MCPRequest {\n return {\n jsonrpc: \"2.0\",\n method: \"tools/call\",\n params: {\n name: \"chat\",\n arguments: {\n messages: [\n { role: \"user\", content: message }\n ],\n stream\n }\n },\n id: Date.now().toString(36) + Math.random().toString(36).substring(2)\n };\n }\n\n // Create MCP tools request\n private createMCPToolsRequest(tools: Array<{ name: string; parameters: Record<string, any> }>): MCPRequest {\n return {\n jsonrpc: \"2.0\",\n method: \"tools/call\",\n params: {\n name: \"tools\",\n arguments: {\n tools\n }\n },\n id: Date.now().toString(36) + Math.random().toString(36).substring(2)\n };\n }\n\n public async *streamChat(\n message: string,\n stream: boolean = true\n ): AsyncGenerator<StreamResponse> {\n try {\n // Add timeout to the fetch request\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout\n\n const response = await fetch(`${this.apiUrl}/v1/chat`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify(this.createMCPRequest(message, stream)),\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n if (!stream) {\n // Handle non-streaming response\n const data = await response.json() as MCPResponse;\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n if (data.result?.output?.messages?.[0]?.content) {\n yield {\n text: data.result.output.messages[0].content,\n done: true\n };\n }\n return;\n }\n \n const reader = response.body?.getReader();\n if (!reader) throw new Error('No reader available');\n\n const decoder = new TextDecoder();\n let buffer = '';\n let hasReceivedContent = false;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n \n // Process complete lines immediately as they arrive\n let lineStartIndex = 0;\n let newlineIndex;\n \n while ((newlineIndex = buffer.indexOf('\\n', lineStartIndex)) !== -1) {\n const line = buffer.slice(lineStartIndex, newlineIndex).trim();\n lineStartIndex = newlineIndex + 1;\n \n if (line && line.startsWith('data: ')) {\n const jsonText = line.slice(6).trim();\n \n // Check for [DONE] message or empty data lines\n if (!jsonText || jsonText === '[DONE]') {\n yield { text: '', done: true };\n return;\n }\n\n try {\n const data = JSON.parse(jsonText) as MCPResponse;\n \n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n \n // Handle MCP protocol format - server sends chunks of new text directly\n if (data.result?.type === 'chunk' && data.result.chunk?.content) {\n hasReceivedContent = true;\n yield { text: data.result.chunk.content, done: false };\n } else if (data.result?.type === 'complete') {\n // Final piece of content or just the done signal\n const finalText = data.result.output?.messages?.[0]?.content ?? '';\n // Only yield final text if we haven't received incremental chunks\n if (!hasReceivedContent && finalText) {\n yield { text: finalText, done: true };\n } else {\n yield { text: '', done: true };\n }\n return;\n } else if ('response' in data && typeof data.response === 'string') {\n // Handle direct server response format (legacy compatibility)\n const isDone = 'done' in data && data.done === true;\n \n if (isDone) {\n // For final response, only yield if we haven't received incremental chunks\n if (!hasReceivedContent && data.response) {\n yield { text: data.response, done: true };\n } else {\n yield { text: '', done: true };\n }\n return;\n } else {\n // For incremental chunks, always yield\n hasReceivedContent = true;\n yield { text: data.response, done: false };\n }\n }\n \n } catch (parseError) {\n console.warn('Error parsing JSON chunk:', parseError);\n }\n }\n }\n \n // Keep remaining incomplete line in buffer\n buffer = buffer.slice(lineStartIndex);\n \n // Prevent buffer from growing too large\n if (buffer.length > 1000000) { // 1MB limit\n console.warn('Buffer too large, truncating...');\n buffer = buffer.slice(-500000); // Keep last 500KB\n }\n }\n \n // If we exit the while loop naturally, ensure we send a done signal\n if (hasReceivedContent) {\n yield { text: '', done: true };\n }\n \n } finally {\n reader.releaseLock();\n }\n \n } catch (error: any) {\n if (error.name === 'AbortError') {\n throw new Error('Connection timed out. Please check if the server is running.');\n } else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n // Re-throw the error to be caught by the caller\n throw error;\n }\n }\n }\n\n // New function to send tools request\n public async sendToolsRequest(tools: Array<{ name: string; parameters: Record<string, any> }>): Promise<MCPResponse> {\n const response = await fetch(`${this.apiUrl}/v1/chat`, this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(this.createMCPToolsRequest(tools)),\n }));\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n const data = await response.json() as MCPResponse;\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n\n return data;\n }\n}\n\n// Legacy compatibility functions - these create a default client instance\n// These are kept for backward compatibility but should be deprecated in favor of the class-based approach\n\nlet defaultClient: ApiClient | null = null;\n\n// Configure the API with a custom URL, API key (optional), and session ID (optional)\nexport const configureApi = (apiUrl: string, apiKey: string | null = null, sessionId: string | null = null): void => {\n defaultClient = new ApiClient({ apiUrl, apiKey, sessionId });\n}\n\n// Legacy streamChat function that uses the default client\nexport async function* streamChat(\n message: string,\n stream: boolean = true\n): AsyncGenerator<StreamResponse> {\n if (!defaultClient) {\n throw new Error('API not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n \n yield* defaultClient.streamChat(message, stream);\n}\n\n// Legacy sendToolsRequest function that uses the default client\nexport async function sendToolsRequest(tools: Array<{ name: string; parameters: Record<string, any> }>): Promise<MCPResponse> {\n if (!defaultClient) {\n throw new Error('API not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n \n return defaultClient.sendToolsRequest(tools);\n}"],"names":["httpAgent","httpsAgent","http","https","_a","_b","err","ApiClient","config","__publicField","sessionId","options","baseOptions","agent","headers","message","stream","tools","controller","timeoutId","response","errorText","data","_d","_c","reader","_e","decoder","buffer","hasReceivedContent","done","value","chunk","lineStartIndex","newlineIndex","line","jsonText","_f","_g","_h","finalText","_k","_j","_i","parseError","error","defaultClient","configureApi","apiUrl","apiKey","streamChat","sendToolsRequest"],"mappings":"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"}
|
|
@@ -25,8 +25,27 @@ interface MCPResponse {
|
|
|
25
25
|
message: string;
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
+
export declare class ApiClient {
|
|
29
|
+
private readonly apiUrl;
|
|
30
|
+
private readonly apiKey;
|
|
31
|
+
private sessionId;
|
|
32
|
+
constructor(config: {
|
|
33
|
+
apiUrl: string;
|
|
34
|
+
apiKey?: string | null;
|
|
35
|
+
sessionId?: string | null;
|
|
36
|
+
});
|
|
37
|
+
setSessionId(sessionId: string | null): void;
|
|
38
|
+
getSessionId(): string | null;
|
|
39
|
+
private getFetchOptions;
|
|
40
|
+
private createMCPRequest;
|
|
41
|
+
private createMCPToolsRequest;
|
|
42
|
+
streamChat(message: string, stream?: boolean): AsyncGenerator<StreamResponse>;
|
|
43
|
+
sendToolsRequest(tools: Array<{
|
|
44
|
+
name: string;
|
|
45
|
+
parameters: Record<string, any>;
|
|
46
|
+
}>): Promise<MCPResponse>;
|
|
47
|
+
}
|
|
28
48
|
export declare const configureApi: (apiUrl: string, apiKey?: string | null, sessionId?: string | null) => void;
|
|
29
|
-
export declare const initializeChatbot: (permanentApiKey: string, customOrigin?: string) => Promise<void>;
|
|
30
49
|
export declare function streamChat(message: string, stream?: boolean): AsyncGenerator<StreamResponse>;
|
|
31
50
|
export declare function sendToolsRequest(tools: Array<{
|
|
32
51
|
name: string;
|
package/dist/api.mjs
CHANGED
|
@@ -1,305 +1,203 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
let
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!r.ok) {
|
|
28
|
-
const i = await r.text();
|
|
29
|
-
throw new Error(`Failed to exchange API key: ${r.status} ${i}`);
|
|
30
|
-
}
|
|
31
|
-
const o = await r.json();
|
|
32
|
-
$ = o.sessionId, S = o.temporaryKey, q = o.sessionId, t = "";
|
|
33
|
-
const c = o.expiresIn * 0.9 * 1e3;
|
|
34
|
-
f && clearTimeout(f), f = setTimeout(() => U(), c);
|
|
35
|
-
} catch (n) {
|
|
36
|
-
throw n;
|
|
1
|
+
var j = Object.defineProperty;
|
|
2
|
+
var M = (r, e, t) => e in r ? j(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
|
|
3
|
+
var w = (r, e, t) => M(r, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
let I = null, v = null;
|
|
5
|
+
typeof window > "u" && Promise.all([
|
|
6
|
+
import("http").catch(() => null),
|
|
7
|
+
import("https").catch(() => null)
|
|
8
|
+
]).then(([r, e]) => {
|
|
9
|
+
var t, s;
|
|
10
|
+
(t = r == null ? void 0 : r.default) != null && t.Agent ? I = new r.default.Agent({ keepAlive: !0 }) : r != null && r.Agent && (I = new r.Agent({ keepAlive: !0 })), (s = e == null ? void 0 : e.default) != null && s.Agent ? v = new e.default.Agent({ keepAlive: !0 }) : e != null && e.Agent && (v = new e.Agent({ keepAlive: !0 }));
|
|
11
|
+
}).catch((r) => {
|
|
12
|
+
console.warn("Failed to initialize HTTP agents:", r.message);
|
|
13
|
+
});
|
|
14
|
+
class K {
|
|
15
|
+
// Session ID can be mutable
|
|
16
|
+
constructor(e) {
|
|
17
|
+
w(this, "apiUrl");
|
|
18
|
+
w(this, "apiKey");
|
|
19
|
+
w(this, "sessionId");
|
|
20
|
+
if (!e.apiUrl || typeof e.apiUrl != "string")
|
|
21
|
+
throw new Error("API URL must be a valid string");
|
|
22
|
+
if (e.apiKey !== void 0 && e.apiKey !== null && typeof e.apiKey != "string")
|
|
23
|
+
throw new Error("API key must be a valid string or null");
|
|
24
|
+
if (e.sessionId !== void 0 && e.sessionId !== null && typeof e.sessionId != "string")
|
|
25
|
+
throw new Error("Session ID must be a valid string or null");
|
|
26
|
+
this.apiUrl = e.apiUrl, this.apiKey = e.apiKey ?? null, this.sessionId = e.sessionId ?? null;
|
|
37
27
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
method: "POST",
|
|
43
|
-
headers: {
|
|
44
|
-
"Content-Type": "application/json",
|
|
45
|
-
"X-Session-ID": $,
|
|
46
|
-
Origin: typeof window < "u" ? window.location.origin : "node-client"
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
if (!e.ok) {
|
|
50
|
-
const o = await e.text();
|
|
51
|
-
throw new Error(`Failed to refresh key: ${e.status} ${o}`);
|
|
52
|
-
}
|
|
53
|
-
const n = await e.json();
|
|
54
|
-
S = n.temporaryKey, f && clearTimeout(f);
|
|
55
|
-
const r = n.expiresIn * 0.9 * 1e3;
|
|
56
|
-
f = setTimeout(() => U(), r);
|
|
57
|
-
} catch (t) {
|
|
58
|
-
typeof window < "u" && window.dispatchEvent(new CustomEvent("chatbot:auth:failed", { detail: t }));
|
|
59
|
-
}
|
|
60
|
-
}, X = (t, e = {}) => {
|
|
61
|
-
t.startsWith("https:");
|
|
62
|
-
const n = Date.now().toString(36) + Math.random().toString(36).substring(2), r = Date.now(), o = {
|
|
63
|
-
Connection: "keep-alive",
|
|
64
|
-
"X-Request-ID": n,
|
|
65
|
-
"X-Timestamp": r.toString()
|
|
66
|
-
};
|
|
67
|
-
typeof window < "u" && (o["X-Origin"] = window.location.origin, o["X-Referrer"] = document.referrer || "direct");
|
|
68
|
-
const c = _();
|
|
69
|
-
c && (o["X-API-Key"] = c, typeof window < "u" && (o["X-Signature"] = z(
|
|
70
|
-
n,
|
|
71
|
-
r,
|
|
72
|
-
e.body
|
|
73
|
-
)));
|
|
74
|
-
const i = M();
|
|
75
|
-
return i && (o["X-Session-ID"] = i), {
|
|
76
|
-
...e,
|
|
77
|
-
headers: {
|
|
78
|
-
...e.headers,
|
|
79
|
-
...o
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
}, z = (t, e, n) => {
|
|
83
|
-
const r = `${t}:${e}:${n ? JSON.stringify(n) : ""}`;
|
|
84
|
-
return btoa(r).substring(0, 16);
|
|
85
|
-
};
|
|
86
|
-
class H {
|
|
87
|
-
constructor() {
|
|
88
|
-
N(this, "requests", /* @__PURE__ */ new Map());
|
|
28
|
+
setSessionId(e) {
|
|
29
|
+
if (e !== null && typeof e != "string")
|
|
30
|
+
throw new Error("Session ID must be a valid string or null");
|
|
31
|
+
this.sessionId = e;
|
|
89
32
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return i.length >= n ? !1 : (i.push(o), this.requests.set(e, i), this.requests.size > 1e3 && this.cleanup(r), !0);
|
|
33
|
+
getSessionId() {
|
|
34
|
+
return this.sessionId;
|
|
93
35
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
36
|
+
// Helper to get fetch options with connection pooling if available
|
|
37
|
+
getFetchOptions(e = {}) {
|
|
38
|
+
const t = {};
|
|
39
|
+
if (typeof window > "u") {
|
|
40
|
+
const u = this.apiUrl.startsWith("https:") ? v : I;
|
|
41
|
+
u && (t.agent = u);
|
|
42
|
+
} else
|
|
43
|
+
t.headers = { Connection: "keep-alive" };
|
|
44
|
+
const s = {
|
|
45
|
+
"X-Request-ID": Date.now().toString(36) + Math.random().toString(36).substring(2)
|
|
46
|
+
};
|
|
47
|
+
return t.headers && Object.assign(s, t.headers), e.headers && Object.assign(s, e.headers), this.apiKey && (s["X-API-Key"] = this.apiKey), this.sessionId && (s["X-Session-ID"] = this.sessionId), {
|
|
48
|
+
...e,
|
|
49
|
+
...t,
|
|
50
|
+
headers: s
|
|
51
|
+
};
|
|
100
52
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
53
|
+
// Create MCP request
|
|
54
|
+
createMCPRequest(e, t = !0) {
|
|
55
|
+
return {
|
|
56
|
+
jsonrpc: "2.0",
|
|
57
|
+
method: "tools/call",
|
|
58
|
+
params: {
|
|
59
|
+
name: "chat",
|
|
60
|
+
arguments: {
|
|
61
|
+
messages: [
|
|
62
|
+
{ role: "user", content: e }
|
|
63
|
+
],
|
|
64
|
+
stream: t
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
id: Date.now().toString(36) + Math.random().toString(36).substring(2)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Create MCP tools request
|
|
71
|
+
createMCPToolsRequest(e) {
|
|
72
|
+
return {
|
|
73
|
+
jsonrpc: "2.0",
|
|
74
|
+
method: "tools/call",
|
|
75
|
+
params: {
|
|
76
|
+
name: "tools",
|
|
77
|
+
arguments: {
|
|
78
|
+
tools: e
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
id: Date.now().toString(36) + Math.random().toString(36).substring(2)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async *streamChat(e, t = !0) {
|
|
85
|
+
var s, l, u, E, P, b, k, x, C, T, S;
|
|
109
86
|
try {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
87
|
+
const a = new AbortController(), O = setTimeout(() => a.abort(), 6e4), c = await fetch(`${this.apiUrl}/v1/chat`, {
|
|
88
|
+
...this.getFetchOptions({
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: {
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
Accept: t ? "text/event-stream" : "application/json"
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify(this.createMCPRequest(e, t))
|
|
95
|
+
}),
|
|
96
|
+
signal: a.signal
|
|
97
|
+
});
|
|
98
|
+
if (clearTimeout(O), !c.ok) {
|
|
99
|
+
const i = await c.text();
|
|
100
|
+
throw new Error(`Network response was not ok: ${c.status} ${i}`);
|
|
101
|
+
}
|
|
102
|
+
if (!t) {
|
|
103
|
+
const i = await c.json();
|
|
104
|
+
if (i.error)
|
|
105
|
+
throw new Error(`MCP Error: ${i.error.message}`);
|
|
106
|
+
(E = (u = (l = (s = i.result) == null ? void 0 : s.output) == null ? void 0 : l.messages) == null ? void 0 : u[0]) != null && E.content && (yield {
|
|
107
|
+
text: i.result.output.messages[0].content,
|
|
108
|
+
done: !0
|
|
116
109
|
});
|
|
110
|
+
return;
|
|
117
111
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
name: "chat",
|
|
137
|
-
arguments: {
|
|
138
|
-
messages: [
|
|
139
|
-
{ role: "user", content: t }
|
|
140
|
-
],
|
|
141
|
-
stream: e
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
id: Date.now().toString(36) + Math.random().toString(36).substring(2)
|
|
145
|
-
}), V = (t) => ({
|
|
146
|
-
jsonrpc: "2.0",
|
|
147
|
-
method: "tools/call",
|
|
148
|
-
params: {
|
|
149
|
-
name: "tools",
|
|
150
|
-
arguments: {
|
|
151
|
-
tools: t
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
id: Date.now().toString(36) + Math.random().toString(36).substring(2)
|
|
155
|
-
});
|
|
156
|
-
async function* te(t, e = !0) {
|
|
157
|
-
var n, r, o, c, i, g, A, C, O;
|
|
158
|
-
try {
|
|
159
|
-
const l = M() || "anonymous";
|
|
160
|
-
if (!B.canMakeRequest(l, 20, 6e4)) {
|
|
161
|
-
yield {
|
|
162
|
-
text: "Rate limit exceeded. Please wait a moment before sending more messages.",
|
|
163
|
-
done: !0
|
|
164
|
-
};
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
const D = w(), j = new AbortController(), J = setTimeout(() => j.abort(), 6e4), h = await fetch(`${D}/v1/chat`, {
|
|
168
|
-
...X(D, {
|
|
169
|
-
method: "POST",
|
|
170
|
-
headers: {
|
|
171
|
-
"Content-Type": "application/json",
|
|
172
|
-
Accept: e ? "text/event-stream" : "application/json"
|
|
173
|
-
},
|
|
174
|
-
body: JSON.stringify(Q(t, e))
|
|
175
|
-
}),
|
|
176
|
-
signal: j.signal
|
|
177
|
-
});
|
|
178
|
-
if (clearTimeout(J), !h.ok) {
|
|
179
|
-
const a = await h.text();
|
|
180
|
-
throw new Error(`Network response was not ok: ${h.status} ${a}`);
|
|
181
|
-
}
|
|
182
|
-
if (!e) {
|
|
183
|
-
const a = await h.json();
|
|
184
|
-
if (a.error)
|
|
185
|
-
throw new Error(`MCP Error: ${a.error.message}`);
|
|
186
|
-
(c = (o = (r = (n = a.result) == null ? void 0 : n.output) == null ? void 0 : r.messages) == null ? void 0 : o[0]) != null && c.content && (yield {
|
|
187
|
-
text: a.result.output.messages[0].content,
|
|
188
|
-
done: !0
|
|
189
|
-
});
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const T = (i = h.body) == null ? void 0 : i.getReader();
|
|
193
|
-
if (!T) throw new Error("No reader available");
|
|
194
|
-
const W = new TextDecoder();
|
|
195
|
-
let u = "", P = "", p = !1;
|
|
196
|
-
try {
|
|
197
|
-
for (; ; ) {
|
|
198
|
-
const { done: a, value: y } = await T.read();
|
|
199
|
-
if (a)
|
|
200
|
-
break;
|
|
201
|
-
const m = W.decode(y, { stream: !0 });
|
|
202
|
-
u += m;
|
|
203
|
-
let k = 0, R;
|
|
204
|
-
for (; (R = u.indexOf(`
|
|
205
|
-
`, k)) !== -1; ) {
|
|
206
|
-
const b = u.slice(k, R).trim();
|
|
207
|
-
if (k = R + 1, b && b.startsWith("data: "))
|
|
208
|
-
try {
|
|
209
|
-
const x = b.slice(6).trim();
|
|
210
|
-
if (x === "[DONE]") {
|
|
112
|
+
const g = (P = c.body) == null ? void 0 : P.getReader();
|
|
113
|
+
if (!g) throw new Error("No reader available");
|
|
114
|
+
const R = new TextDecoder();
|
|
115
|
+
let o = "", d = !1;
|
|
116
|
+
try {
|
|
117
|
+
for (; ; ) {
|
|
118
|
+
const { done: i, value: D } = await g.read();
|
|
119
|
+
if (i)
|
|
120
|
+
break;
|
|
121
|
+
const U = R.decode(D, { stream: !0 });
|
|
122
|
+
o += U;
|
|
123
|
+
let h = 0, p;
|
|
124
|
+
for (; (p = o.indexOf(`
|
|
125
|
+
`, h)) !== -1; ) {
|
|
126
|
+
const y = o.slice(h, p).trim();
|
|
127
|
+
if (h = p + 1, y && y.startsWith("data: ")) {
|
|
128
|
+
const m = y.slice(6).trim();
|
|
129
|
+
if (!m || m === "[DONE]") {
|
|
211
130
|
yield { text: "", done: !0 };
|
|
212
131
|
return;
|
|
213
132
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
else
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const E = L(d, P);
|
|
233
|
-
E && (P += E, p = !0, yield {
|
|
234
|
-
text: E,
|
|
235
|
-
done: I
|
|
236
|
-
});
|
|
133
|
+
try {
|
|
134
|
+
const n = JSON.parse(m);
|
|
135
|
+
if (n.error)
|
|
136
|
+
throw new Error(`MCP Error: ${n.error.message}`);
|
|
137
|
+
if (((b = n.result) == null ? void 0 : b.type) === "chunk" && ((k = n.result.chunk) != null && k.content))
|
|
138
|
+
d = !0, yield { text: n.result.chunk.content, done: !1 };
|
|
139
|
+
else if (((x = n.result) == null ? void 0 : x.type) === "complete") {
|
|
140
|
+
const A = ((S = (T = (C = n.result.output) == null ? void 0 : C.messages) == null ? void 0 : T[0]) == null ? void 0 : S.content) ?? "";
|
|
141
|
+
!d && A ? yield { text: A, done: !0 } : yield { text: "", done: !0 };
|
|
142
|
+
return;
|
|
143
|
+
} else if ("response" in n && typeof n.response == "string")
|
|
144
|
+
if ("done" in n && n.done === !0) {
|
|
145
|
+
!d && n.response ? yield { text: n.response, done: !0 } : yield { text: "", done: !0 };
|
|
146
|
+
return;
|
|
147
|
+
} else
|
|
148
|
+
d = !0, yield { text: n.response, done: !1 };
|
|
149
|
+
} catch (n) {
|
|
150
|
+
console.warn("Error parsing JSON chunk:", n);
|
|
237
151
|
}
|
|
238
|
-
if (I) {
|
|
239
|
-
p || (yield { text: "", done: !0 });
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
} catch {
|
|
243
152
|
}
|
|
244
|
-
}
|
|
245
|
-
u = u.slice(k), u.length > 1e6 && (u = u.slice(-5e5));
|
|
246
|
-
}
|
|
247
|
-
p && (yield { text: "", done: !0 });
|
|
248
|
-
} finally {
|
|
249
|
-
T.releaseLock();
|
|
250
|
-
}
|
|
251
|
-
if (u && u.startsWith("data: "))
|
|
252
|
-
try {
|
|
253
|
-
const a = u.slice(6).trim();
|
|
254
|
-
if (a && a !== "[DONE]") {
|
|
255
|
-
const y = JSON.parse(a);
|
|
256
|
-
if ((O = (C = y.result) == null ? void 0 : C.chunk) != null && O.content) {
|
|
257
|
-
const m = L(y.result.chunk.content, P);
|
|
258
|
-
m && (yield {
|
|
259
|
-
text: m,
|
|
260
|
-
done: !0
|
|
261
|
-
});
|
|
262
153
|
}
|
|
154
|
+
o = o.slice(h), o.length > 1e6 && (console.warn("Buffer too large, truncating..."), o = o.slice(-5e5));
|
|
263
155
|
}
|
|
264
|
-
|
|
156
|
+
d && (yield { text: "", done: !0 });
|
|
157
|
+
} finally {
|
|
158
|
+
g.releaseLock();
|
|
265
159
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
160
|
+
} catch (a) {
|
|
161
|
+
throw a.name === "AbortError" ? new Error("Connection timed out. Please check if the server is running.") : a.name === "TypeError" && a.message.includes("Failed to fetch") ? new Error("Could not connect to the server. Please check if the server is running.") : a;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// New function to send tools request
|
|
165
|
+
async sendToolsRequest(e) {
|
|
166
|
+
const t = await fetch(`${this.apiUrl}/v1/chat`, this.getFetchOptions({
|
|
167
|
+
method: "POST",
|
|
168
|
+
headers: {
|
|
169
|
+
"Content-Type": "application/json"
|
|
170
|
+
},
|
|
171
|
+
body: JSON.stringify(this.createMCPToolsRequest(e))
|
|
172
|
+
}));
|
|
173
|
+
if (!t.ok) {
|
|
174
|
+
const l = await t.text();
|
|
175
|
+
throw new Error(`Network response was not ok: ${t.status} ${l}`);
|
|
176
|
+
}
|
|
177
|
+
const s = await t.json();
|
|
178
|
+
if (s.error)
|
|
179
|
+
throw new Error(`MCP Error: ${s.error.message}`);
|
|
180
|
+
return s;
|
|
277
181
|
}
|
|
278
182
|
}
|
|
279
|
-
|
|
280
|
-
|
|
183
|
+
let f = null;
|
|
184
|
+
const q = (r, e = null, t = null) => {
|
|
185
|
+
f = new K({ apiUrl: r, apiKey: e, sessionId: t });
|
|
186
|
+
};
|
|
187
|
+
async function* N(r, e = !0) {
|
|
188
|
+
if (!f)
|
|
189
|
+
throw new Error("API not configured. Please call configureApi() with your server URL before using any API functions.");
|
|
190
|
+
yield* f.streamChat(r, e);
|
|
281
191
|
}
|
|
282
|
-
async function
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
"Content-Type": "application/json"
|
|
287
|
-
},
|
|
288
|
-
body: JSON.stringify(V(t))
|
|
289
|
-
}));
|
|
290
|
-
if (!n.ok) {
|
|
291
|
-
const o = await n.text();
|
|
292
|
-
throw new Error(`Network response was not ok: ${n.status} ${o}`);
|
|
293
|
-
}
|
|
294
|
-
const r = await n.json();
|
|
295
|
-
if (r.error)
|
|
296
|
-
throw new Error(`MCP Error: ${r.error.message}`);
|
|
297
|
-
return r;
|
|
192
|
+
async function F(r) {
|
|
193
|
+
if (!f)
|
|
194
|
+
throw new Error("API not configured. Please call configureApi() with your server URL before using any API functions.");
|
|
195
|
+
return f.sendToolsRequest(r);
|
|
298
196
|
}
|
|
299
197
|
export {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
198
|
+
K as ApiClient,
|
|
199
|
+
q as configureApi,
|
|
200
|
+
F as sendToolsRequest,
|
|
201
|
+
N as streamChat
|
|
304
202
|
};
|
|
305
203
|
//# sourceMappingURL=api.mjs.map
|
package/dist/api.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.mjs","sources":["../api.ts"],"sourcesContent":["// For Node.js environments, we can use http.Agent for connection pooling\nlet httpAgent: any = null;\nlet httpsAgent: any = null;\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n}\n\nexport interface ChatResponse {\n response: string;\n}\n\n// MCP Protocol interfaces\ninterface MCPRequest {\n jsonrpc: \"2.0\";\n method: string;\n params: {\n name: string;\n arguments: {\n messages?: Array<{\n role: string;\n content: string;\n }>;\n stream?: boolean;\n tools?: Array<{\n name: string;\n parameters: Record<string, any>;\n }>;\n };\n };\n id: string;\n}\n\ninterface MCPResponse {\n jsonrpc: \"2.0\";\n id: string;\n result?: {\n type?: \"start\" | \"chunk\" | \"complete\";\n chunk?: {\n content: string;\n };\n output?: {\n messages: Array<{\n role: string;\n content: string;\n }>;\n };\n };\n error?: {\n code: number;\n message: string;\n };\n}\n\n// Store the configured API URL, key, and session ID\nlet configuredApiUrl: string | null = null;\nlet configuredApiKey: string | null = null;\nlet configuredSessionId: string | null = null;\n\n// Temporary key management\nlet sessionId: string | null = null;\nlet keyRefreshTimer: number | null = null;\n\n// Key Exchange Response interface\ninterface KeyExchangeResponse {\n temporaryKey: string;\n sessionId: string;\n expiresIn: number; // seconds\n expiresAt: number; // timestamp\n}\n\n// Configure the API with a custom URL, API key (optional), and session ID (optional)\nexport const configureApi = (apiUrl: string, apiKey: string | null = null, sessionId: string | null = null): void => {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (apiKey !== null && typeof apiKey !== 'string') {\n throw new Error('API key must be a valid string or null');\n }\n if (sessionId !== null && typeof sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n configuredApiUrl = apiUrl;\n configuredApiKey = apiKey;\n configuredSessionId = sessionId;\n}\n\n// Get the configured API URL or throw an error if not configured\nconst getApiUrl = (): string => {\n if (!configuredApiUrl) {\n throw new Error('API URL not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n return configuredApiUrl;\n};\n\n// Get the configured API key or return null if not configured\nconst getApiKey = (): string | null => {\n return configuredApiKey;\n};\n\n// Get the configured session ID or return null if not configured\nconst getSessionId = (): string | null => {\n return configuredSessionId;\n};\n\n// Initialize chatbot with temporary key exchange\nexport const initializeChatbot = async (permanentApiKey: string, customOrigin?: string): Promise<void> => {\n try {\n const API_URL = getApiUrl();\n \n // Exchange permanent key for temporary session key\n const response = await fetch(`${API_URL}/api/v1/exchange-key`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ \n apiKey: permanentApiKey,\n origin: customOrigin || (typeof window !== 'undefined' ? window.location.origin : 'node-client')\n })\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to exchange API key: ${response.status} ${errorText}`);\n }\n \n const data: KeyExchangeResponse = await response.json();\n \n // Store only the temporary key and session\n sessionId = data.sessionId;\n configuredApiKey = data.temporaryKey; // Use temporary key as the main API key\n configuredSessionId = data.sessionId;\n \n // Clear permanent key from memory (security measure)\n permanentApiKey = '';\n \n // Set up auto-refresh (90% of expiry time)\n const refreshInterval = data.expiresIn * 0.9 * 1000;\n if (keyRefreshTimer) {\n clearTimeout(keyRefreshTimer);\n }\n keyRefreshTimer = setTimeout(() => refreshTemporaryKey(), refreshInterval) as any; \n } catch (error) {\n throw error;\n }\n};\n\n// Refresh temporary key before it expires\nconst refreshTemporaryKey = async (): Promise<void> => {\n if (!sessionId) return;\n \n try {\n const API_URL = getApiUrl();\n \n const response = await fetch(`${API_URL}/api/v1/refresh-key`, {\n method: 'POST',\n headers: { \n 'Content-Type': 'application/json',\n 'X-Session-ID': sessionId,\n 'Origin': typeof window !== 'undefined' ? window.location.origin : 'node-client'\n }\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to refresh key: ${response.status} ${errorText}`);\n }\n \n const data: KeyExchangeResponse = await response.json();\n \n // Update keys\n configuredApiKey = data.temporaryKey;\n \n // Reset timer\n if (keyRefreshTimer) {\n clearTimeout(keyRefreshTimer);\n }\n const refreshInterval = data.expiresIn * 0.9 * 1000;\n keyRefreshTimer = setTimeout(() => refreshTemporaryKey(), refreshInterval) as any;\n } catch (error) {\n // Emit event for handling by consumer\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent('chatbot:auth:failed', { detail: error }));\n }\n }\n};\n\n// Helper to get fetch options with connection pooling and enhanced security\nconst getFetchOptions = (apiUrl: string, options: RequestInit = {}): RequestInit | any => {\n const isHttps = apiUrl.startsWith('https:');\n \n // Only use agents in Node.js environment\n if (typeof window === 'undefined') {\n if (isHttps && httpsAgent) {\n return { ...options, agent: httpsAgent } as any;\n } else if (httpAgent) {\n return { ...options, agent: httpAgent } as any;\n }\n }\n \n // Browser environment with enhanced security headers\n const requestId = Date.now().toString(36) + Math.random().toString(36).substring(2);\n const timestamp = Date.now();\n \n // Enhanced security headers\n const headers: Record<string, string> = {\n 'Connection': 'keep-alive',\n 'X-Request-ID': requestId,\n 'X-Timestamp': timestamp.toString()\n };\n \n // Add origin and referrer for security validation\n if (typeof window !== 'undefined') {\n headers['X-Origin'] = window.location.origin;\n headers['X-Referrer'] = document.referrer || 'direct';\n }\n \n // Add API key to headers only if it exists\n const apiKey = getApiKey();\n if (apiKey) {\n headers['X-API-Key'] = apiKey;\n \n // Add request signature for enhanced security\n if (typeof window !== 'undefined') {\n headers['X-Signature'] = generateRequestSignature(\n requestId,\n timestamp,\n options.body\n );\n }\n }\n \n // Add session ID to headers only if it exists\n const sessionId = getSessionId();\n if (sessionId) {\n headers['X-Session-ID'] = sessionId;\n }\n \n return {\n ...options,\n headers: {\n ...options.headers,\n ...headers\n }\n };\n};\n\n// Generate request signature for enhanced security\nconst generateRequestSignature = (\n requestId: string, \n timestamp: number, \n body?: any\n): string => {\n const payload = `${requestId}:${timestamp}:${body ? JSON.stringify(body) : ''}`;\n // Simple base64 encoding - in production, you should use HMAC\n return btoa(payload).substring(0, 16);\n};\n\n// Rate limiter implementation\nclass RateLimiter {\n private requests: Map<string, number[]> = new Map();\n \n canMakeRequest(\n identifier: string, \n maxRequests: number = 20, \n windowMs: number = 60000\n ): boolean {\n const now = Date.now();\n const userRequests = this.requests.get(identifier) || [];\n const recentRequests = userRequests.filter(time => now - time < windowMs);\n \n if (recentRequests.length >= maxRequests) {\n return false;\n }\n \n recentRequests.push(now);\n this.requests.set(identifier, recentRequests);\n \n // Cleanup old entries\n if (this.requests.size > 1000) {\n this.cleanup(windowMs);\n }\n \n return true;\n }\n \n private cleanup(windowMs: number): void {\n const now = Date.now();\n for (const [key, requests] of this.requests.entries()) {\n const recent = requests.filter(time => now - time < windowMs);\n if (recent.length === 0) {\n this.requests.delete(key);\n } else {\n this.requests.set(key, recent);\n }\n }\n }\n}\n\n// Global rate limiter instance\nconst rateLimiter = new RateLimiter();\n\n// Security monitoring (client-side)\nconst securityMonitor = {\n config: {\n monitoring: {\n enabled: true\n }\n },\n\n async loadConfig(): Promise<void> {\n try {\n const API_URL = getApiUrl();\n const response = await fetch(`${API_URL}/api/v1/security/config`);\n if (response.ok) {\n const config = await response.json();\n if (config.monitoring) {\n this.config.monitoring = {\n ...this.config.monitoring,\n ...config.monitoring\n };\n }\n }\n } catch (e) {\n // Fail silently, use default config\n }\n },\n\n detectDevTools(): boolean {\n if (typeof window === 'undefined') return false;\n const threshold = 160;\n return (window.outerHeight - window.innerHeight > threshold) || \n (window.outerWidth - window.innerWidth > threshold);\n },\n \n async init(): Promise<void> {\n if (typeof window === 'undefined') return;\n \n // Load configuration first\n await this.loadConfig();\n }\n};\n\n// Initialize security monitoring if in browser\nif (typeof window !== 'undefined') {\n securityMonitor.init().catch(() => {\n // Fail silently, security monitoring will use default config\n });\n}\n\n// Create MCP request\nconst createMCPRequest = (message: string, stream: boolean = true): MCPRequest => {\n return {\n jsonrpc: \"2.0\",\n method: \"tools/call\",\n params: {\n name: \"chat\",\n arguments: {\n messages: [\n { role: \"user\", content: message }\n ],\n stream\n }\n },\n id: Date.now().toString(36) + Math.random().toString(36).substring(2)\n };\n};\n\n// Create MCP tools request\nconst createMCPToolsRequest = (tools: Array<{ name: string; parameters: Record<string, any> }>): MCPRequest => {\n return {\n jsonrpc: \"2.0\",\n method: \"tools/call\",\n params: {\n name: \"tools\",\n arguments: {\n tools\n }\n },\n id: Date.now().toString(36) + Math.random().toString(36).substring(2)\n };\n};\n\nexport async function* streamChat(\n message: string,\n stream: boolean = true\n): AsyncGenerator<StreamResponse> {\n try {\n // Check rate limiting\n const sessionIdentifier = getSessionId() || 'anonymous';\n \n if (!rateLimiter.canMakeRequest(sessionIdentifier, 20, 60000)) {\n yield { \n text: 'Rate limit exceeded. Please wait a moment before sending more messages.', \n done: true \n };\n return;\n }\n \n const API_URL = getApiUrl();\n \n // Add timeout to the fetch request\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout\n\n const response = await fetch(`${API_URL}/v1/chat`, {\n ...getFetchOptions(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify(createMCPRequest(message, stream)),\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n if (!stream) {\n // Handle non-streaming response\n const data = await response.json() as MCPResponse;\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n if (data.result?.output?.messages?.[0]?.content) {\n yield {\n text: data.result.output.messages[0].content,\n done: true\n };\n }\n return;\n }\n \n const reader = response.body?.getReader();\n if (!reader) throw new Error('No reader available');\n\n const decoder = new TextDecoder();\n let buffer = '';\n let currentFullText = '';\n let hasReceivedContent = false;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n \n // Process complete lines immediately as they arrive\n let lineStartIndex = 0;\n let newlineIndex;\n \n while ((newlineIndex = buffer.indexOf('\\n', lineStartIndex)) !== -1) {\n const line = buffer.slice(lineStartIndex, newlineIndex).trim();\n lineStartIndex = newlineIndex + 1;\n \n if (line && line.startsWith('data: ')) {\n try {\n const jsonText = line.slice(6).trim();\n \n // Check for [DONE] message (legacy format)\n if (jsonText === '[DONE]') {\n yield { text: '', done: true };\n return;\n }\n\n // Skip empty data lines\n if (!jsonText) {\n continue;\n }\n\n const data = JSON.parse(jsonText) as MCPResponse;\n \n // Handle errors\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n \n let content = '';\n let isDone = false;\n \n // Handle MCP protocol format\n if (data.result) {\n if (data.result.type === 'start') {\n continue;\n } else if (data.result.type === 'chunk' && data.result.chunk) {\n content = data.result.chunk.content;\n } else if (data.result.type === 'complete' && data.result.output?.messages?.[0]) {\n // For complete messages, only yield if we haven't received any content yet\n if (!hasReceivedContent) {\n content = data.result.output.messages[0].content;\n isDone = true;\n } else {\n // Skip the complete message if we've already received chunks\n continue;\n }\n }\n }\n \n // Handle direct server response format (from LLM clients)\n // This is what the server actually sends: { \"response\": \"...\", \"done\": false/true }\n if (!content && 'response' in data && typeof data.response === 'string') {\n content = data.response;\n }\n \n // Check for done signal in the data\n if ('done' in data && data.done === true) {\n isDone = true;\n }\n\n if (content) {\n const newText = extractNewText(content, currentFullText);\n if (newText) {\n currentFullText += newText;\n hasReceivedContent = true;\n yield {\n text: newText,\n done: isDone\n };\n }\n }\n \n // If we received a done signal, exit\n if (isDone) {\n if (!hasReceivedContent) {\n // Yield empty response to indicate completion\n yield { text: '', done: true };\n }\n return;\n }\n } catch (parseError) {\n // Don't throw, just continue processing\n }\n }\n }\n \n // Keep remaining incomplete line in buffer\n buffer = buffer.slice(lineStartIndex);\n \n // Prevent buffer from growing too large\n if (buffer.length > 1000000) { // 1MB limit\n buffer = buffer.slice(-500000); // Keep last 500KB\n }\n }\n \n // If we exit the while loop naturally, ensure we send a done signal\n if (hasReceivedContent) {\n yield { text: '', done: true };\n }\n \n } finally {\n reader.releaseLock();\n }\n\n // Handle any remaining buffer (fallback)\n if (buffer && buffer.startsWith('data: ')) {\n try {\n const jsonText = buffer.slice(6).trim();\n if (jsonText && jsonText !== '[DONE]') {\n const data = JSON.parse(jsonText) as MCPResponse;\n if (data.result?.chunk?.content) {\n const newText = extractNewText(data.result.chunk.content, currentFullText);\n if (newText) {\n yield {\n text: newText,\n done: true\n };\n }\n }\n }\n } catch (error) {\n // Fail silently for final buffer parsing\n }\n }\n \n } catch (error: any) {\n if (error.name === 'AbortError') {\n yield { \n text: 'Connection timed out. Please check if the server is running.', \n done: true \n };\n } else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n yield { \n text: 'Could not connect to the server. Please check if the server is running.', \n done: true \n };\n } else {\n yield { \n text: `Error: ${error.message}`, \n done: true \n };\n }\n }\n}\n\n// Helper function to extract only new text from incoming chunks\nfunction extractNewText(incomingText: string, currentText: string): string {\n // Simplified version - just check if we have new content at the end\n if (!currentText) return incomingText;\n \n // If incoming text is longer and starts with current text, return the new part\n if (incomingText.length > currentText.length && incomingText.startsWith(currentText)) {\n return incomingText.slice(currentText.length);\n }\n \n // Otherwise return the full incoming text (fallback)\n return incomingText;\n}\n\n// New function to send tools request\nexport async function sendToolsRequest(tools: Array<{ name: string; parameters: Record<string, any> }>): Promise<MCPResponse> {\n const API_URL = getApiUrl();\n \n const response = await fetch(`${API_URL}/v1/chat`, getFetchOptions(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(createMCPToolsRequest(tools)),\n }));\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n const data = await response.json() as MCPResponse;\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n\n return data;\n}"],"names":["configuredApiUrl","configuredApiKey","configuredSessionId","sessionId","keyRefreshTimer","configureApi","apiUrl","apiKey","getApiUrl","getApiKey","getSessionId","initializeChatbot","permanentApiKey","customOrigin","API_URL","response","errorText","data","refreshInterval","refreshTemporaryKey","error","getFetchOptions","options","requestId","timestamp","headers","generateRequestSignature","body","payload","RateLimiter","__publicField","identifier","maxRequests","windowMs","now","recentRequests","time","key","requests","recent","rateLimiter","securityMonitor","config","threshold","createMCPRequest","message","stream","createMCPToolsRequest","tools","streamChat","_a","_b","_c","_d","_e","_f","_g","_h","_i","sessionIdentifier","controller","timeoutId","reader","decoder","buffer","currentFullText","hasReceivedContent","done","value","chunk","lineStartIndex","newlineIndex","line","jsonText","content","isDone","newText","extractNewText","incomingText","currentText","sendToolsRequest"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api.mjs","sources":["../api.ts"],"sourcesContent":["// For Node.js environments, we can use http.Agent for connection pooling\nlet httpAgent: any = null;\nlet httpsAgent: any = null;\n\n// Initialize agents for connection pooling in Node.js environments\nif (typeof window === 'undefined') {\n // Lazy load to avoid including 'http' in browser bundles\n Promise.all([\n import('http').catch(() => null),\n import('https').catch(() => null)\n ]).then(([http, https]) => {\n if (http?.default?.Agent) {\n httpAgent = new http.default.Agent({ keepAlive: true });\n } else if (http?.Agent) {\n httpAgent = new http.Agent({ keepAlive: true });\n }\n \n if (https?.default?.Agent) {\n httpsAgent = new https.default.Agent({ keepAlive: true });\n } else if (https?.Agent) {\n httpsAgent = new https.Agent({ keepAlive: true });\n }\n }).catch(err => {\n // Silently fail - connection pooling is optional\n console.warn('Failed to initialize HTTP agents:', err.message);\n });\n}\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n}\n\nexport interface ChatResponse {\n response: string;\n}\n\n// MCP Protocol interfaces\ninterface MCPRequest {\n jsonrpc: \"2.0\";\n method: string;\n params: {\n name: string;\n arguments: {\n messages?: Array<{\n role: string;\n content: string;\n }>;\n stream?: boolean;\n tools?: Array<{\n name: string;\n parameters: Record<string, any>;\n }>;\n };\n };\n id: string;\n}\n\ninterface MCPResponse {\n jsonrpc: \"2.0\";\n id: string;\n result?: {\n type?: \"start\" | \"chunk\" | \"complete\";\n chunk?: {\n content: string;\n };\n output?: {\n messages: Array<{\n role: string;\n content: string;\n }>;\n };\n };\n error?: {\n code: number;\n message: string;\n };\n}\n\nexport class ApiClient {\n private readonly apiUrl: string;\n private readonly apiKey: string | null;\n private sessionId: string | null; // Session ID can be mutable\n\n constructor(config: { apiUrl: string; apiKey?: string | null; sessionId?: string | null }) {\n if (!config.apiUrl || typeof config.apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (config.apiKey !== undefined && config.apiKey !== null && typeof config.apiKey !== 'string') {\n throw new Error('API key must be a valid string or null');\n }\n if (config.sessionId !== undefined && config.sessionId !== null && typeof config.sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n \n this.apiUrl = config.apiUrl;\n this.apiKey = config.apiKey ?? null;\n this.sessionId = config.sessionId ?? null;\n }\n\n public setSessionId(sessionId: string | null): void {\n if (sessionId !== null && typeof sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n this.sessionId = sessionId;\n }\n\n public getSessionId(): string | null {\n return this.sessionId;\n }\n\n // Helper to get fetch options with connection pooling if available\n private getFetchOptions(options: RequestInit = {}): RequestInit {\n const baseOptions: RequestInit = {};\n \n // Environment-specific options\n if (typeof window === 'undefined') {\n // Node.js: Use connection pooling agent\n const isHttps = this.apiUrl.startsWith('https:');\n const agent = isHttps ? httpsAgent : httpAgent;\n if (agent) {\n (baseOptions as any).agent = agent;\n }\n } else {\n // Browser: Use keep-alive header\n baseOptions.headers = { 'Connection': 'keep-alive' };\n }\n\n // Common headers\n const headers: Record<string, string> = {\n 'X-Request-ID': Date.now().toString(36) + Math.random().toString(36).substring(2),\n };\n\n // Merge base options headers (for browser keep-alive)\n if (baseOptions.headers) {\n Object.assign(headers, baseOptions.headers);\n }\n\n // Merge original request headers\n if (options.headers) {\n Object.assign(headers, options.headers);\n }\n\n if (this.apiKey) {\n headers['X-API-Key'] = this.apiKey;\n }\n\n if (this.sessionId) {\n headers['X-Session-ID'] = this.sessionId;\n }\n\n return {\n ...options,\n ...baseOptions,\n headers,\n };\n }\n\n // Create MCP request\n private createMCPRequest(message: string, stream: boolean = true): MCPRequest {\n return {\n jsonrpc: \"2.0\",\n method: \"tools/call\",\n params: {\n name: \"chat\",\n arguments: {\n messages: [\n { role: \"user\", content: message }\n ],\n stream\n }\n },\n id: Date.now().toString(36) + Math.random().toString(36).substring(2)\n };\n }\n\n // Create MCP tools request\n private createMCPToolsRequest(tools: Array<{ name: string; parameters: Record<string, any> }>): MCPRequest {\n return {\n jsonrpc: \"2.0\",\n method: \"tools/call\",\n params: {\n name: \"tools\",\n arguments: {\n tools\n }\n },\n id: Date.now().toString(36) + Math.random().toString(36).substring(2)\n };\n }\n\n public async *streamChat(\n message: string,\n stream: boolean = true\n ): AsyncGenerator<StreamResponse> {\n try {\n // Add timeout to the fetch request\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout\n\n const response = await fetch(`${this.apiUrl}/v1/chat`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify(this.createMCPRequest(message, stream)),\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n if (!stream) {\n // Handle non-streaming response\n const data = await response.json() as MCPResponse;\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n if (data.result?.output?.messages?.[0]?.content) {\n yield {\n text: data.result.output.messages[0].content,\n done: true\n };\n }\n return;\n }\n \n const reader = response.body?.getReader();\n if (!reader) throw new Error('No reader available');\n\n const decoder = new TextDecoder();\n let buffer = '';\n let hasReceivedContent = false;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n \n // Process complete lines immediately as they arrive\n let lineStartIndex = 0;\n let newlineIndex;\n \n while ((newlineIndex = buffer.indexOf('\\n', lineStartIndex)) !== -1) {\n const line = buffer.slice(lineStartIndex, newlineIndex).trim();\n lineStartIndex = newlineIndex + 1;\n \n if (line && line.startsWith('data: ')) {\n const jsonText = line.slice(6).trim();\n \n // Check for [DONE] message or empty data lines\n if (!jsonText || jsonText === '[DONE]') {\n yield { text: '', done: true };\n return;\n }\n\n try {\n const data = JSON.parse(jsonText) as MCPResponse;\n \n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n \n // Handle MCP protocol format - server sends chunks of new text directly\n if (data.result?.type === 'chunk' && data.result.chunk?.content) {\n hasReceivedContent = true;\n yield { text: data.result.chunk.content, done: false };\n } else if (data.result?.type === 'complete') {\n // Final piece of content or just the done signal\n const finalText = data.result.output?.messages?.[0]?.content ?? '';\n // Only yield final text if we haven't received incremental chunks\n if (!hasReceivedContent && finalText) {\n yield { text: finalText, done: true };\n } else {\n yield { text: '', done: true };\n }\n return;\n } else if ('response' in data && typeof data.response === 'string') {\n // Handle direct server response format (legacy compatibility)\n const isDone = 'done' in data && data.done === true;\n \n if (isDone) {\n // For final response, only yield if we haven't received incremental chunks\n if (!hasReceivedContent && data.response) {\n yield { text: data.response, done: true };\n } else {\n yield { text: '', done: true };\n }\n return;\n } else {\n // For incremental chunks, always yield\n hasReceivedContent = true;\n yield { text: data.response, done: false };\n }\n }\n \n } catch (parseError) {\n console.warn('Error parsing JSON chunk:', parseError);\n }\n }\n }\n \n // Keep remaining incomplete line in buffer\n buffer = buffer.slice(lineStartIndex);\n \n // Prevent buffer from growing too large\n if (buffer.length > 1000000) { // 1MB limit\n console.warn('Buffer too large, truncating...');\n buffer = buffer.slice(-500000); // Keep last 500KB\n }\n }\n \n // If we exit the while loop naturally, ensure we send a done signal\n if (hasReceivedContent) {\n yield { text: '', done: true };\n }\n \n } finally {\n reader.releaseLock();\n }\n \n } catch (error: any) {\n if (error.name === 'AbortError') {\n throw new Error('Connection timed out. Please check if the server is running.');\n } else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n // Re-throw the error to be caught by the caller\n throw error;\n }\n }\n }\n\n // New function to send tools request\n public async sendToolsRequest(tools: Array<{ name: string; parameters: Record<string, any> }>): Promise<MCPResponse> {\n const response = await fetch(`${this.apiUrl}/v1/chat`, this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(this.createMCPToolsRequest(tools)),\n }));\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n const data = await response.json() as MCPResponse;\n if (data.error) {\n throw new Error(`MCP Error: ${data.error.message}`);\n }\n\n return data;\n }\n}\n\n// Legacy compatibility functions - these create a default client instance\n// These are kept for backward compatibility but should be deprecated in favor of the class-based approach\n\nlet defaultClient: ApiClient | null = null;\n\n// Configure the API with a custom URL, API key (optional), and session ID (optional)\nexport const configureApi = (apiUrl: string, apiKey: string | null = null, sessionId: string | null = null): void => {\n defaultClient = new ApiClient({ apiUrl, apiKey, sessionId });\n}\n\n// Legacy streamChat function that uses the default client\nexport async function* streamChat(\n message: string,\n stream: boolean = true\n): AsyncGenerator<StreamResponse> {\n if (!defaultClient) {\n throw new Error('API not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n \n yield* defaultClient.streamChat(message, stream);\n}\n\n// Legacy sendToolsRequest function that uses the default client\nexport async function sendToolsRequest(tools: Array<{ name: string; parameters: Record<string, any> }>): Promise<MCPResponse> {\n if (!defaultClient) {\n throw new Error('API not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n \n return defaultClient.sendToolsRequest(tools);\n}"],"names":["httpAgent","httpsAgent","http","https","_a","_b","err","ApiClient","config","__publicField","sessionId","options","baseOptions","agent","headers","message","stream","tools","_c","_d","_e","_f","_g","_h","_i","_j","_k","controller","timeoutId","response","errorText","data","reader","decoder","buffer","hasReceivedContent","done","value","chunk","lineStartIndex","newlineIndex","line","jsonText","finalText","parseError","error","defaultClient","configureApi","apiUrl","apiKey","streamChat","sendToolsRequest"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schmitech/chatbot-api",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.1",
|
|
5
5
|
"description": "API client for the ORBIT MCP server",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/api.cjs",
|
|
8
8
|
"module": "./dist/api.mjs",
|
|
9
|
-
"types": "./api.d.ts",
|
|
9
|
+
"types": "./dist/api.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
|
-
"import":
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/api.d.ts",
|
|
14
|
+
"default": "./dist/api.mjs"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/api.d.ts",
|
|
18
|
+
"default": "./dist/api.cjs"
|
|
19
|
+
}
|
|
15
20
|
}
|
|
16
21
|
},
|
|
17
22
|
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
"api.d.ts"
|
|
23
|
+
"dist"
|
|
20
24
|
],
|
|
21
25
|
"scripts": {
|
|
22
26
|
"dev": "vite",
|
|
23
|
-
"build": "
|
|
27
|
+
"build": "vite build",
|
|
24
28
|
"lint": "eslint .",
|
|
25
29
|
"preview": "vite preview",
|
|
26
30
|
"test": "vitest",
|
|
@@ -28,15 +32,9 @@
|
|
|
28
32
|
"test-query": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' ./test/run-query.js",
|
|
29
33
|
"test-query-from-pairs": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' ./test/run-query-from-pairs.js"
|
|
30
34
|
},
|
|
31
|
-
"dependencies": {
|
|
32
|
-
"cors": "^2.8.5",
|
|
33
|
-
"express": "^4.18.3",
|
|
34
|
-
"node-fetch": "^3.3.2"
|
|
35
|
-
},
|
|
35
|
+
"dependencies": {},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@eslint/js": "^9.9.1",
|
|
38
|
-
"@types/cors": "^2.8.17",
|
|
39
|
-
"@types/express": "^4.17.21",
|
|
40
38
|
"@types/node": "^20.11.24",
|
|
41
39
|
"eslint": "^9.9.1",
|
|
42
40
|
"globals": "^15.9.0",
|
|
@@ -45,6 +43,7 @@
|
|
|
45
43
|
"typescript": "^5.5.3",
|
|
46
44
|
"typescript-eslint": "^8.3.0",
|
|
47
45
|
"vite": "^5.4.2",
|
|
46
|
+
"vite-plugin-dts": "^4.3.0",
|
|
48
47
|
"vitest": "^0.34.6"
|
|
49
48
|
},
|
|
50
49
|
"peerDependencies": {
|
package/dist/api.mjs.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "../api.d.ts";
|