orbitchat 2.1.7 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/bin/dev-server.js +62 -0
- package/bin/orbitchat.js +223 -71
- package/dist/assets/{api-DZkCbfw-.js → api-iWGWe9XZ.js} +1 -1
- package/dist/assets/arc-m8RC5oDD-DxtXpPlf.js +1 -0
- package/dist/assets/{blockDiagram-c4efeb88-NvaZhZFT-2jTGmfzw.js → blockDiagram-c4efeb88-CZ2mpei3-B_3saIkQ.js} +7 -7
- package/dist/assets/{c4Diagram-c83219d4-gPr1eF38-DMPWXy6y.js → c4Diagram-c83219d4-DL2EmCLj-Dv5sdkpm.js} +2 -2
- package/dist/assets/channel-BKp1WYnX-Cc6fwi-B.js +1 -0
- package/dist/assets/classDiagram-beda092f-vKbJ2bcD-CpzCwdb3.js +2 -0
- package/dist/assets/classDiagram-v2-2358418a-BHXpn1Du-ClzQ61Pm.js +2 -0
- package/dist/assets/clone-B1V2003j-Cr3Uw-Zs.js +1 -0
- package/dist/assets/{createText-1719965b-D9Y3vUMn-DDdadCK5.js → createText-1719965b-C2-AT0ab-CWg8fDWo.js} +2 -2
- package/dist/assets/edges-96097737-Zk-qG_8u-HnJhTil4.js +4 -0
- package/dist/assets/{erDiagram-0228fc6a-BLw8FsIp-Cs8VClxW.js → erDiagram-0228fc6a-B94anMjz-ocfxI7NV.js} +3 -3
- package/dist/assets/{flowDb-c6c81e3f-DXVkMlSE-lHya7k5v.js → flowDb-c6c81e3f-CRRW-bQO-G1WHNmUV.js} +4 -4
- package/dist/assets/{flowDiagram-50d868cf-mkkB0hFd-S2mNfjcZ.js → flowDiagram-50d868cf-DPzQMhJv-DgwkjDqI.js} +4 -4
- package/dist/assets/flowDiagram-v2-4f6560a1-Si9enBmB-DYUBnRO1.js +1 -0
- package/dist/assets/{flowchart-elk-definition-6af322e1-D9IbjnSJ-RN3qt1rV.js → flowchart-elk-definition-6af322e1-CULhtvD--GZ9ilB9C.js} +1 -1
- package/dist/assets/{ganttDiagram-a2739b55-Cqs5_Zht-C5tqANPy.js → ganttDiagram-a2739b55-Bp8yOLEz-CqM7mRjt.js} +3 -3
- package/dist/assets/{gitGraphDiagram-82fe8481-txBzFn40-CsYRcfiT.js → gitGraphDiagram-82fe8481-BYtOQJl0-C2naxeTp.js} +1 -1
- package/dist/assets/{graph-BZahQKIN-CenQ9kLM.js → graph-BV6jB5rk-CELreG_k.js} +1 -1
- package/dist/assets/index-42QQgFVq.css +1 -0
- package/dist/assets/{index-5325376f-DsNDppci-BmtId_7h.js → index-5325376f-DrY72DrM-CA0ZD5ej.js} +1 -1
- package/dist/assets/index-BaIhc-dj.js +1008 -0
- package/dist/assets/index-DlFdZiHl-BkXTu13U.js +134 -0
- package/dist/assets/{infoDiagram-8eee0895-SbmhgTjv-DWbp8qIg.js → infoDiagram-8eee0895-C6GFnrZ7-BhM7Tkvh.js} +4 -4
- package/dist/assets/{journeyDiagram-c64418c1-C7AiwbKr-CR4ESCM3.js → journeyDiagram-c64418c1-daS1W8qe-DipmPDM8.js} +4 -4
- package/dist/assets/{layout-j-hMNqAM-d_O0ZYu6.js → layout-LXMv3mSS-fzT-WsR4.js} +1 -1
- package/dist/assets/{mindmap-definition-8da855dc-B1WjnfxE-C_ekkGNm.js → mindmap-definition-8da855dc-DmNQYVT1-BupnMO6G.js} +1 -1
- package/dist/assets/{pieDiagram-a8764435-zrkaw4r--nNryZ7xt.js → pieDiagram-a8764435-381urBY5-CJU6X8kv.js} +5 -5
- package/dist/assets/{quadrantDiagram-1e28029f-C2Wa5n16-DJsmIh09.js → quadrantDiagram-1e28029f-hgRmK2bX-fzcWnYUM.js} +4 -4
- package/dist/assets/{requirementDiagram-08caed73-BtHxx079-DyPYX9i4.js → requirementDiagram-08caed73-BB0Q-cI9-CPFdXDhT.js} +1 -1
- package/dist/assets/{sankeyDiagram-a04cb91d-DXlmqTKB-DF_MhieU.js → sankeyDiagram-a04cb91d-BpsipZNt-B3YPu4J8.js} +5 -5
- package/dist/assets/{sequenceDiagram-c5b8d532-BVQF3vcA-OQvBng6Z.js → sequenceDiagram-c5b8d532-xoAvPcQJ-B9rDXSoA.js} +4 -4
- package/dist/assets/{stateDiagram-1ecb1508-Dybc3i2m-DpShq5Fv.js → stateDiagram-1ecb1508-b721R49K-CWcqUIUo.js} +1 -1
- package/dist/assets/stateDiagram-v2-c2b004d7-BmSbrbqZ-DlbnZV5y.js +1 -0
- package/dist/assets/{styles-b4e223ce-BSiU4oYL-D8WR6bxO.js → styles-b4e223ce-TYmJC5IN-BHQXJFVt.js} +3 -3
- package/dist/assets/{styles-ca3715f6-DU7x0doE-BqM8h3vG.js → styles-ca3715f6-CfJZu0Yn-DtD2x1No.js} +2 -2
- package/dist/assets/{styles-d45a18b0-B_CU21kM-BP9RAefH.js → styles-d45a18b0-DZ8oSYcV-CvugbGS9.js} +3 -3
- package/dist/assets/{svgDrawCommon-b86b1483-sbEe2n2v-CFwc1_jc.js → svgDrawCommon-b86b1483-CXPkN8HW-DH97F0YX.js} +1 -1
- package/dist/assets/{timeline-definition-faaaa080-b82QCnJ9-D_9su1EE.js → timeline-definition-faaaa080-LDFf1jfb-DxM6VoSn.js} +6 -6
- package/dist/assets/{xychartDiagram-f5964ef8-BtRcO0uy-C_Ru9X19.js → xychartDiagram-f5964ef8-DdhO5-Bp-CevMp6HN.js} +3 -3
- package/dist/index.html +2 -2
- package/package.json +7 -3
- package/dist/assets/arc-CvECY7sY-CCUjLJq2.js +0 -1
- package/dist/assets/channel-6PI3wNcc-BxckuQvX.js +0 -1
- package/dist/assets/classDiagram-beda092f-B8axaKGu-Cu_ZIJ6u.js +0 -2
- package/dist/assets/classDiagram-v2-2358418a-DPdf2DxR-BZsTNggP.js +0 -2
- package/dist/assets/clone-D01vUIhD-DmY5mrln.js +0 -1
- package/dist/assets/edges-96097737-Zc6U9nQz-BzB5ko4h.js +0 -4
- package/dist/assets/flowDiagram-v2-4f6560a1-BRIhxQ3y-DOhW7LIb.js +0 -1
- package/dist/assets/index-CITIGg-2.css +0 -1
- package/dist/assets/index-CVRNWcf-.js +0 -1006
- package/dist/assets/stateDiagram-v2-c2b004d7-Bp8rRkv2-9nDDCxFS.js +0 -1
package/README.md
CHANGED
|
@@ -99,6 +99,27 @@ Configuration is loaded in the following priority order:
|
|
|
99
99
|
|
|
100
100
|
**Note:** GitHub stats and GitHub owner/repo are always shown and default to "schmitech/orbit". These are only configurable via build-time environment variables (`VITE_SHOW_GITHUB_STATS`, `VITE_GITHUB_OWNER`, `VITE_GITHUB_REPO`) for developers who fork the repository and build their own version.
|
|
101
101
|
|
|
102
|
+
### Protect API Keys with the Middleware Proxy
|
|
103
|
+
|
|
104
|
+
You can prevent API keys from ever reaching the browser by enabling the built-in middleware layer:
|
|
105
|
+
|
|
106
|
+
1. Create an `adapters.yaml` file (next to `bin/orbitchat.js`, in your working directory, or in `~/.orbit-chat-app/`). Example:
|
|
107
|
+
```yaml
|
|
108
|
+
adapters:
|
|
109
|
+
local-dev:
|
|
110
|
+
apiKey: orbit_dev_key
|
|
111
|
+
apiUrl: http://localhost:3000
|
|
112
|
+
production:
|
|
113
|
+
apiKey: orbit_prod_key
|
|
114
|
+
apiUrl: https://api.example.com
|
|
115
|
+
```
|
|
116
|
+
2. Start the CLI with `--enable-api-middleware` (or export `VITE_ENABLE_API_MIDDLEWARE=true`). The Express server now:
|
|
117
|
+
- Serves `GET /api/adapters` so the React app can list safe adapter names.
|
|
118
|
+
- Proxies all chat/file/thread/admin calls through `/api/proxy/...`, injecting the adapter’s real `X-API-Key`.
|
|
119
|
+
3. The UI automatically swaps the API-key modal for an Adapter Selector and stores adapter names per conversation.
|
|
120
|
+
|
|
121
|
+
Keep `adapters.yaml` out of source control and run the CLI behind HTTPS (or another reverse proxy) when deploying.
|
|
122
|
+
|
|
102
123
|
### Environment Variables
|
|
103
124
|
|
|
104
125
|
You can also set configuration via environment variables (for development):
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Development server that runs both Express (for middleware) and Vite
|
|
4
|
+
* This allows testing the API middleware feature during development
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname } from 'path';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
const expressPort = 5174;
|
|
16
|
+
const vitePort = 5173;
|
|
17
|
+
|
|
18
|
+
// Start Express server for middleware endpoints
|
|
19
|
+
console.log('Starting Express middleware server on port', expressPort);
|
|
20
|
+
const expressServer = spawn('node', [
|
|
21
|
+
path.join(__dirname, 'orbitchat.js'),
|
|
22
|
+
'--port', expressPort.toString(),
|
|
23
|
+
'--enable-api-middleware'
|
|
24
|
+
], {
|
|
25
|
+
stdio: 'inherit',
|
|
26
|
+
env: {
|
|
27
|
+
...process.env,
|
|
28
|
+
VITE_ENABLE_API_MIDDLEWARE: 'true',
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Start Vite dev server
|
|
33
|
+
console.log('Starting Vite dev server on port', vitePort);
|
|
34
|
+
const viteServer = spawn('npm', ['run', 'dev'], {
|
|
35
|
+
stdio: 'inherit',
|
|
36
|
+
env: {
|
|
37
|
+
...process.env,
|
|
38
|
+
VITE_ENABLE_API_MIDDLEWARE: 'true',
|
|
39
|
+
VITE_MIDDLEWARE_SERVER_URL: `http://localhost:${expressPort}`,
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Handle cleanup
|
|
44
|
+
process.on('SIGINT', () => {
|
|
45
|
+
console.log('\nShutting down servers...');
|
|
46
|
+
expressServer.kill();
|
|
47
|
+
viteServer.kill();
|
|
48
|
+
process.exit(0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expressServer.on('exit', (code) => {
|
|
52
|
+
console.log(`Express server exited with code ${code}`);
|
|
53
|
+
viteServer.kill();
|
|
54
|
+
process.exit(code);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
viteServer.on('exit', (code) => {
|
|
58
|
+
console.log(`Vite server exited with code ${code}`);
|
|
59
|
+
expressServer.kill();
|
|
60
|
+
process.exit(code);
|
|
61
|
+
});
|
|
62
|
+
|
package/bin/orbitchat.js
CHANGED
|
@@ -4,15 +4,20 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Serves the chat-app as a standalone application with runtime configuration.
|
|
6
6
|
* Configuration can be provided via CLI arguments, config file, or environment variables.
|
|
7
|
+
*
|
|
8
|
+
* When VITE_ENABLE_API_MIDDLEWARE is enabled, the server acts as a proxy to hide
|
|
9
|
+
* API keys from the client by mapping adapter names to actual API keys.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
|
-
import
|
|
12
|
+
import express from 'express';
|
|
10
13
|
import fs from 'fs';
|
|
11
14
|
import path from 'path';
|
|
12
15
|
import { homedir } from 'os';
|
|
13
16
|
import { execSync } from 'child_process';
|
|
14
17
|
import { fileURLToPath } from 'url';
|
|
15
18
|
import { dirname } from 'path';
|
|
19
|
+
import yaml from 'js-yaml';
|
|
20
|
+
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
16
21
|
|
|
17
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
23
|
const __dirname = dirname(__filename);
|
|
@@ -28,6 +33,7 @@ const DEFAULT_CONFIG = {
|
|
|
28
33
|
enableUploadButton: false,
|
|
29
34
|
enableAudioOutput: false,
|
|
30
35
|
enableFeedbackButtons: false,
|
|
36
|
+
enableApiMiddleware: false,
|
|
31
37
|
maxFilesPerConversation: 5,
|
|
32
38
|
maxFileSizeMB: 50,
|
|
33
39
|
maxTotalFiles: 100,
|
|
@@ -37,6 +43,33 @@ const DEFAULT_CONFIG = {
|
|
|
37
43
|
maxMessageLength: 1000,
|
|
38
44
|
};
|
|
39
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Load adapter mappings from YAML file
|
|
48
|
+
*/
|
|
49
|
+
function loadAdaptersConfig() {
|
|
50
|
+
const configPaths = [
|
|
51
|
+
path.join(__dirname, '..', 'adapters.yaml'),
|
|
52
|
+
path.join(process.cwd(), 'adapters.yaml'),
|
|
53
|
+
path.join(homedir(), '.orbit-chat-app', 'adapters.yaml'),
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
for (const configPath of configPaths) {
|
|
57
|
+
try {
|
|
58
|
+
if (fs.existsSync(configPath)) {
|
|
59
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
60
|
+
const config = yaml.load(content);
|
|
61
|
+
if (config && config.adapters) {
|
|
62
|
+
return config.adapters;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.warn(`Warning: Could not load adapters config from ${configPath}:`, error.message);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
40
73
|
/**
|
|
41
74
|
* Parse command-line arguments
|
|
42
75
|
*/
|
|
@@ -62,22 +95,25 @@ function parseArgs() {
|
|
|
62
95
|
config.defaultKey = args[++i];
|
|
63
96
|
break;
|
|
64
97
|
case '--use-local-api':
|
|
65
|
-
config.useLocalApi =
|
|
98
|
+
config.useLocalApi = true;
|
|
66
99
|
break;
|
|
67
100
|
case '--local-api-path':
|
|
68
101
|
config.localApiPath = args[++i];
|
|
69
102
|
break;
|
|
70
103
|
case '--console-debug':
|
|
71
|
-
config.consoleDebug =
|
|
104
|
+
config.consoleDebug = true;
|
|
72
105
|
break;
|
|
73
106
|
case '--enable-upload':
|
|
74
|
-
config.enableUploadButton =
|
|
107
|
+
config.enableUploadButton = true;
|
|
75
108
|
break;
|
|
76
109
|
case '--enable-audio':
|
|
77
|
-
config.enableAudioOutput =
|
|
110
|
+
config.enableAudioOutput = true;
|
|
78
111
|
break;
|
|
79
112
|
case '--enable-feedback':
|
|
80
|
-
config.enableFeedbackButtons =
|
|
113
|
+
config.enableFeedbackButtons = true;
|
|
114
|
+
break;
|
|
115
|
+
case '--enable-api-middleware':
|
|
116
|
+
config.enableApiMiddleware = true;
|
|
81
117
|
break;
|
|
82
118
|
case '--max-files-per-conversation':
|
|
83
119
|
config.maxFilesPerConversation = parseInt(args[++i], 10);
|
|
@@ -163,6 +199,7 @@ function loadConfigFromEnv() {
|
|
|
163
199
|
VITE_ENABLE_UPLOAD: 'enableUploadButton',
|
|
164
200
|
VITE_ENABLE_AUDIO_OUTPUT: 'enableAudioOutput',
|
|
165
201
|
VITE_ENABLE_FEEDBACK: 'enableFeedbackButtons',
|
|
202
|
+
VITE_ENABLE_API_MIDDLEWARE: 'enableApiMiddleware',
|
|
166
203
|
VITE_MAX_FILES_PER_CONVERSATION: 'maxFilesPerConversation',
|
|
167
204
|
VITE_MAX_FILE_SIZE_MB: 'maxFileSizeMB',
|
|
168
205
|
VITE_MAX_TOTAL_FILES: 'maxTotalFiles',
|
|
@@ -177,7 +214,7 @@ function loadConfigFromEnv() {
|
|
|
177
214
|
if (value !== undefined) {
|
|
178
215
|
if (configKey === 'useLocalApi' || configKey === 'consoleDebug' ||
|
|
179
216
|
configKey === 'enableUploadButton' || configKey === 'enableAudioOutput' ||
|
|
180
|
-
configKey === 'enableFeedbackButtons') {
|
|
217
|
+
configKey === 'enableFeedbackButtons' || configKey === 'enableApiMiddleware') {
|
|
181
218
|
envConfig[configKey] = value === 'true';
|
|
182
219
|
} else if (configKey.includes('max') && configKey !== 'maxFileSizeMB') {
|
|
183
220
|
const parsed = parseInt(value, 10);
|
|
@@ -242,6 +279,158 @@ function injectConfig(html, config) {
|
|
|
242
279
|
);
|
|
243
280
|
}
|
|
244
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Create Express server to serve the built app
|
|
284
|
+
*/
|
|
285
|
+
function createServer(distPath, config) {
|
|
286
|
+
const app = express();
|
|
287
|
+
const adapters = config.enableApiMiddleware ? loadAdaptersConfig() : null;
|
|
288
|
+
|
|
289
|
+
// API endpoints for middleware mode - MUST be before body parsers
|
|
290
|
+
if (config.enableApiMiddleware && adapters) {
|
|
291
|
+
// Endpoint to list available adapters
|
|
292
|
+
app.get('/api/adapters', (req, res) => {
|
|
293
|
+
const adapterList = Object.keys(adapters).map(name => ({
|
|
294
|
+
name,
|
|
295
|
+
apiUrl: adapters[name].apiUrl,
|
|
296
|
+
// Don't expose API keys
|
|
297
|
+
}));
|
|
298
|
+
res.json({ adapters: adapterList });
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Proxy middleware for API requests - must be before body parsers to preserve request stream
|
|
302
|
+
app.use('/api/proxy', (req, res, next) => {
|
|
303
|
+
const adapterName = req.headers['x-adapter-name'];
|
|
304
|
+
|
|
305
|
+
if (!adapterName) {
|
|
306
|
+
return res.status(400).json({ error: 'X-Adapter-Name header is required' });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const adapter = adapters[adapterName];
|
|
310
|
+
if (!adapter) {
|
|
311
|
+
return res.status(404).json({ error: `Adapter '${adapterName}' not found` });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Create proxy middleware for this request
|
|
315
|
+
const proxy = createProxyMiddleware({
|
|
316
|
+
target: adapter.apiUrl,
|
|
317
|
+
changeOrigin: true,
|
|
318
|
+
pathRewrite: {
|
|
319
|
+
'^/api/proxy': '', // Remove /api/proxy prefix
|
|
320
|
+
},
|
|
321
|
+
onProxyReq: (proxyReq, req) => {
|
|
322
|
+
// Remove adapter name header
|
|
323
|
+
proxyReq.removeHeader('x-adapter-name');
|
|
324
|
+
// Add actual API key (use X-API-Key to match backend expectation)
|
|
325
|
+
proxyReq.setHeader('X-API-Key', adapter.apiKey);
|
|
326
|
+
// Preserve important headers
|
|
327
|
+
const headersToPreserve = ['content-type', 'x-session-id', 'x-thread-id', 'accept', 'content-length'];
|
|
328
|
+
headersToPreserve.forEach(header => {
|
|
329
|
+
const value = req.headers[header];
|
|
330
|
+
if (value) {
|
|
331
|
+
proxyReq.setHeader(header, value);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
// Copy all other headers
|
|
335
|
+
Object.keys(req.headers).forEach(key => {
|
|
336
|
+
const lowerKey = key.toLowerCase();
|
|
337
|
+
if (!['x-adapter-name', 'host', 'connection', 'transfer-encoding'].includes(lowerKey)) {
|
|
338
|
+
const value = req.headers[key];
|
|
339
|
+
if (value && !headersToPreserve.includes(lowerKey)) {
|
|
340
|
+
proxyReq.setHeader(key, value);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
},
|
|
345
|
+
onProxyRes: (proxyRes, req, res) => {
|
|
346
|
+
// Handle CORS if needed
|
|
347
|
+
proxyRes.headers['access-control-allow-origin'] = '*';
|
|
348
|
+
proxyRes.headers['access-control-allow-methods'] = 'GET, POST, PUT, DELETE, OPTIONS';
|
|
349
|
+
proxyRes.headers['access-control-allow-headers'] = 'Content-Type, X-API-Key, X-Session-ID, X-Thread-ID, X-Adapter-Name';
|
|
350
|
+
},
|
|
351
|
+
onError: (err, req, res) => {
|
|
352
|
+
console.error('Proxy error:', err);
|
|
353
|
+
if (!res.headersSent) {
|
|
354
|
+
res.status(500).json({ error: 'Proxy error', message: err.message });
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
ws: false, // Disable WebSocket proxying
|
|
358
|
+
logLevel: 'silent', // Reduce logging
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
proxy(req, res, next);
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Middleware for parsing JSON - after proxy routes to preserve request body stream
|
|
366
|
+
app.use(express.json());
|
|
367
|
+
app.use(express.urlencoded({ extended: true }));
|
|
368
|
+
|
|
369
|
+
// Serve static files
|
|
370
|
+
app.use(express.static(distPath, {
|
|
371
|
+
setHeaders: (res, filePath) => {
|
|
372
|
+
// Inject config into HTML files
|
|
373
|
+
if (path.extname(filePath) === '.html') {
|
|
374
|
+
// This will be handled in the route handler
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
}));
|
|
378
|
+
|
|
379
|
+
// SPA fallback - serve index.html for all non-file requests
|
|
380
|
+
app.get('*', (req, res, next) => {
|
|
381
|
+
// Skip API routes
|
|
382
|
+
if (req.path.startsWith('/api/')) {
|
|
383
|
+
return next();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
let filePath = path.join(distPath, req.path === '/' ? 'index.html' : req.path);
|
|
387
|
+
|
|
388
|
+
// Security: prevent directory traversal
|
|
389
|
+
filePath = path.normalize(filePath);
|
|
390
|
+
if (!filePath.startsWith(path.normalize(distPath))) {
|
|
391
|
+
return res.status(403).send('Forbidden');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Check if file exists
|
|
395
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
396
|
+
// Read and serve file
|
|
397
|
+
try {
|
|
398
|
+
let content = fs.readFileSync(filePath);
|
|
399
|
+
|
|
400
|
+
// Inject configuration into HTML files
|
|
401
|
+
if (path.extname(filePath) === '.html') {
|
|
402
|
+
content = Buffer.from(injectConfig(content.toString(), config));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const mimeType = getMimeType(filePath);
|
|
406
|
+
res.setHeader('Content-Type', mimeType);
|
|
407
|
+
res.send(content);
|
|
408
|
+
} catch (error) {
|
|
409
|
+
console.error('Error serving file:', error);
|
|
410
|
+
res.status(500).send('Internal Server Error');
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
// For SPA routing, serve index.html for non-file requests
|
|
414
|
+
if (!path.extname(filePath)) {
|
|
415
|
+
filePath = path.join(distPath, 'index.html');
|
|
416
|
+
try {
|
|
417
|
+
let content = fs.readFileSync(filePath);
|
|
418
|
+
content = Buffer.from(injectConfig(content.toString(), config));
|
|
419
|
+
res.setHeader('Content-Type', 'text/html');
|
|
420
|
+
res.send(content);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error('Error serving index.html:', error);
|
|
423
|
+
res.status(500).send('Internal Server Error');
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
res.status(404).send('Not Found');
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
return app;
|
|
432
|
+
}
|
|
433
|
+
|
|
245
434
|
/**
|
|
246
435
|
* Get MIME type for file extension
|
|
247
436
|
*/
|
|
@@ -267,52 +456,6 @@ function getMimeType(filePath) {
|
|
|
267
456
|
return mimeTypes[ext] || 'application/octet-stream';
|
|
268
457
|
}
|
|
269
458
|
|
|
270
|
-
/**
|
|
271
|
-
* Create HTTP server to serve the built app
|
|
272
|
-
*/
|
|
273
|
-
function createServer(distPath, config) {
|
|
274
|
-
return http.createServer((req, res) => {
|
|
275
|
-
let filePath = path.join(distPath, req.url === '/' ? 'index.html' : req.url);
|
|
276
|
-
|
|
277
|
-
// Security: prevent directory traversal
|
|
278
|
-
filePath = path.normalize(filePath);
|
|
279
|
-
if (!filePath.startsWith(distPath)) {
|
|
280
|
-
res.writeHead(403);
|
|
281
|
-
res.end('Forbidden');
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Check if file exists
|
|
286
|
-
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
287
|
-
// For SPA routing, serve index.html for non-file requests
|
|
288
|
-
if (!path.extname(filePath)) {
|
|
289
|
-
filePath = path.join(distPath, 'index.html');
|
|
290
|
-
} else {
|
|
291
|
-
res.writeHead(404);
|
|
292
|
-
res.end('Not Found');
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Read and serve file
|
|
298
|
-
try {
|
|
299
|
-
let content = fs.readFileSync(filePath);
|
|
300
|
-
|
|
301
|
-
// Inject configuration into HTML files
|
|
302
|
-
if (path.extname(filePath) === '.html') {
|
|
303
|
-
content = Buffer.from(injectConfig(content.toString(), config));
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const mimeType = getMimeType(filePath);
|
|
307
|
-
res.writeHead(200, { 'Content-Type': mimeType });
|
|
308
|
-
res.end(content);
|
|
309
|
-
} catch (error) {
|
|
310
|
-
res.writeHead(500);
|
|
311
|
-
res.end('Internal Server Error');
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
459
|
/**
|
|
317
460
|
* Open browser
|
|
318
461
|
*/
|
|
@@ -370,12 +513,13 @@ Usage: orbitchat [options]
|
|
|
370
513
|
Options:
|
|
371
514
|
--api-url URL API URL (default: http://localhost:3000)
|
|
372
515
|
--api-key KEY Default API key (default: default-key)
|
|
373
|
-
--use-local-api
|
|
516
|
+
--use-local-api Use local API build (default: false)
|
|
374
517
|
--local-api-path PATH Path to local API
|
|
375
|
-
--console-debug
|
|
376
|
-
--enable-upload
|
|
377
|
-
--enable-audio
|
|
378
|
-
--enable-feedback
|
|
518
|
+
--console-debug Enable console debug (default: false)
|
|
519
|
+
--enable-upload Enable upload button (default: false)
|
|
520
|
+
--enable-audio Enable audio button (default: false)
|
|
521
|
+
--enable-feedback Enable feedback buttons (default: false)
|
|
522
|
+
--enable-api-middleware Enable API middleware mode (default: false)
|
|
379
523
|
--max-files-per-conversation N Max files per conversation (default: 5)
|
|
380
524
|
--max-file-size-mb N Max file size in MB (default: 50)
|
|
381
525
|
--max-total-files N Max total files (default: 100, 0 = unlimited)
|
|
@@ -399,6 +543,7 @@ Configuration Priority:
|
|
|
399
543
|
Examples:
|
|
400
544
|
orbitchat --api-url http://localhost:3000 --port 8080
|
|
401
545
|
orbitchat --api-key my-key --open
|
|
546
|
+
orbitchat --enable-audio --enable-upload --console-debug
|
|
402
547
|
orbitchat --config /path/to/config.json
|
|
403
548
|
`);
|
|
404
549
|
}
|
|
@@ -432,16 +577,26 @@ function main() {
|
|
|
432
577
|
}
|
|
433
578
|
|
|
434
579
|
// Create and start server
|
|
435
|
-
const
|
|
580
|
+
const app = createServer(distPath, config);
|
|
436
581
|
|
|
437
|
-
|
|
582
|
+
app.listen(serverConfig.port, serverConfig.host, () => {
|
|
438
583
|
const url = `http://${serverConfig.host}:${serverConfig.port}`;
|
|
439
|
-
console.
|
|
440
|
-
console.
|
|
441
|
-
console.
|
|
442
|
-
console.
|
|
443
|
-
console.
|
|
444
|
-
console.
|
|
584
|
+
console.debug(`\n🚀 ORBIT Chat App is running at ${url}\n`);
|
|
585
|
+
console.debug('Configuration:');
|
|
586
|
+
console.debug(` API URL: ${config.apiUrl}`);
|
|
587
|
+
console.debug(` Default Key: ${config.defaultKey}`);
|
|
588
|
+
console.debug(` Port: ${serverConfig.port}`);
|
|
589
|
+
console.debug(` Host: ${serverConfig.host}`);
|
|
590
|
+
if (config.enableApiMiddleware) {
|
|
591
|
+
console.debug(` API Middleware: Enabled`);
|
|
592
|
+
const adapters = loadAdaptersConfig();
|
|
593
|
+
if (adapters) {
|
|
594
|
+
console.debug(` Available Adapters: ${Object.keys(adapters).join(', ')}`);
|
|
595
|
+
} else {
|
|
596
|
+
console.debug(` Warning: No adapters.yaml found`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
console.debug('');
|
|
445
600
|
|
|
446
601
|
if (serverConfig.open) {
|
|
447
602
|
openBrowser(url);
|
|
@@ -450,11 +605,8 @@ function main() {
|
|
|
450
605
|
|
|
451
606
|
// Handle graceful shutdown
|
|
452
607
|
process.on('SIGINT', () => {
|
|
453
|
-
console.
|
|
454
|
-
|
|
455
|
-
console.log('Server closed.');
|
|
456
|
-
process.exit(0);
|
|
457
|
-
});
|
|
608
|
+
console.debug('\n\nShutting down server...');
|
|
609
|
+
process.exit(0);
|
|
458
610
|
});
|
|
459
611
|
}
|
|
460
612
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{_ as K}from"./index-
|
|
1
|
+
import{_ as K}from"./index-BaIhc-dj.js";var j=Object.defineProperty,q=(l,i,e)=>i in l?j(l,i,{enumerable:!0,configurable:!0,writable:!0,value:e}):l[i]=e,I=(l,i,e)=>q(l,typeof i!="symbol"?i+"":i,e);let F=null,$=null;typeof window>"u"&&Promise.all([K(()=>import("./__vite-browser-external-BIHI7g3E.js"),[]).catch(()=>null),K(()=>import("./__vite-browser-external-BIHI7g3E.js"),[]).catch(()=>null)]).then(([l,i])=>{var e,t;(e=l==null?void 0:l.default)!=null&&e.Agent?F=new l.default.Agent({keepAlive:!0}):l!=null&&l.Agent&&(F=new l.Agent({keepAlive:!0})),(t=i==null?void 0:i.default)!=null&&t.Agent?$=new i.default.Agent({keepAlive:!0}):i!=null&&i.Agent&&($=new i.Agent({keepAlive:!0}))}).catch(l=>{console.warn("Failed to initialize HTTP agents:",l.message)});class D{constructor(i){if(I(this,"apiUrl"),I(this,"apiKey"),I(this,"sessionId"),!i.apiUrl||typeof i.apiUrl!="string")throw new Error("API URL must be a valid string");if(i.apiKey!==void 0&&i.apiKey!==null&&typeof i.apiKey!="string")throw new Error("API key must be a valid string or null");if(i.sessionId!==void 0&&i.sessionId!==null&&typeof i.sessionId!="string")throw new Error("Session ID must be a valid string or null");this.apiUrl=i.apiUrl,this.apiKey=i.apiKey??null,this.sessionId=i.sessionId??null}setSessionId(i){if(i!==null&&typeof i!="string")throw new Error("Session ID must be a valid string or null");this.sessionId=i}getSessionId(){return this.sessionId}async validateApiKey(){var i;if(!this.apiKey)throw new Error("API key is required for validation");try{const e=await fetch(`${this.apiUrl}/admin/api-keys/${this.apiKey}/status`,{...this.getFetchOptions({method:"GET"})}).catch(r=>{throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r});if(!e.ok){let r="";try{r=await e.text()}catch{r=`HTTP ${e.status}`}let s,o;try{const a=JSON.parse(r);s=a.detail||a.message||r}catch{s=r||`HTTP ${e.status}`}switch(e.status){case 401:o="API key is invalid or expired";break;case 403:o="Access denied: API key does not have required permissions";break;case 404:o="API key not found";break;case 503:o="API key management is not available in inference-only mode";break;default:o=`Failed to validate API key: ${s}`;break}throw new Error(o)}const t=await e.json();if(!t.exists){const r="API key does not exist";throw new Error(r)}if(!t.active){const r="API key is inactive";throw new Error(r)}return t}catch(e){let t;throw e instanceof Error&&e.message?e.message.includes("API key")||e.message.includes("Access denied")||e.message.includes("invalid")||e.message.includes("expired")||e.message.includes("inactive")||e.message.includes("not found")||e.message.includes("Could not connect")?t=e.message:t=`API key validation failed: ${e.message}`:e.name==="TypeError"&&(i=e.message)!=null&&i.includes("Failed to fetch")?t="Could not connect to the server. Please check if the server is running.":t="API key validation failed. Please check your API key and try again.",console.warn(`[ApiClient] ${t}`),new Error(t)}}async getAdapterInfo(){var i;if(!this.apiKey)throw new Error("API key is required to get adapter information");try{const e=await fetch(`${this.apiUrl}/admin/api-keys/info`,{...this.getFetchOptions({method:"GET"})}).catch(t=>{throw t.name==="TypeError"&&t.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):t});if(!e.ok){let t="";try{t=await e.text()}catch{t=`HTTP ${e.status}`}let r,s;try{const o=JSON.parse(t);r=o.detail||o.message||t}catch{r=t||`HTTP ${e.status}`}switch(e.status){case 401:s="API key is invalid, disabled, or has no associated adapter";break;case 404:s="Adapter configuration not found";break;case 503:s="Service is not available";break;default:s=`Failed to get adapter info: ${r}`;break}throw new Error(s)}return await e.json()}catch(e){let t;throw e instanceof Error&&e.message?e.message.includes("API key")||e.message.includes("Adapter")||e.message.includes("invalid")||e.message.includes("disabled")||e.message.includes("not found")||e.message.includes("Could not connect")?t=e.message:t=`Failed to get adapter info: ${e.message}`:e.name==="TypeError"&&(i=e.message)!=null&&i.includes("Failed to fetch")?t="Could not connect to the server. Please check if the server is running.":t="Failed to get adapter information. Please try again.",console.warn(`[ApiClient] ${t}`),new Error(t)}}getFetchOptions(i={}){const e={};if(typeof window>"u"){const r=this.apiUrl.startsWith("https:")?$:F;r&&(e.agent=r)}else e.headers={Connection:"keep-alive"};const t={"X-Request-ID":Date.now().toString(36)+Math.random().toString(36).substring(2)};if(e.headers&&Object.assign(t,e.headers),i.headers){const r=i.headers;for(const[s,o]of Object.entries(r))(s.toLowerCase()!=="x-api-key"||!this.apiKey)&&(t[s]=o)}return this.apiKey&&(t["X-API-Key"]=this.apiKey),this.sessionId&&(t["X-Session-ID"]=this.sessionId),{...i,...e,headers:t}}createChatRequest(i,e=!0,t,r,s,o,a,h,p,y,v){const c={messages:[{role:"user",content:i}],stream:e};return t&&t.length>0&&(c.file_ids=t),r&&(c.thread_id=r),s&&(c.audio_input=s),o&&(c.audio_format=o),a&&(c.language=a),h!==void 0&&(c.return_audio=h),p&&(c.tts_voice=p),y&&(c.source_language=y),v&&(c.target_language=v),c}async*streamChat(i,e=!0,t,r,s,o,a,h,p,y,v){var c,C;try{const w=new AbortController,O=setTimeout(()=>w.abort(),6e4),g=await fetch(`${this.apiUrl}/v1/chat`,{...this.getFetchOptions({method:"POST",headers:{"Content-Type":"application/json",Accept:e?"text/event-stream":"application/json"},body:JSON.stringify(this.createChatRequest(i,e,t,r,s,o,a,h,p,y,v))}),signal:w.signal});if(clearTimeout(O),!g.ok){const f=await g.text();throw new Error(`Network response was not ok: ${g.status} ${f}`)}if(!e){const f=await g.json();f.response&&(yield{text:f.response,done:!0,audio:f.audio,audioFormat:f.audio_format});return}const A=(c=g.body)==null?void 0:c.getReader();if(!A)throw new Error("No reader available");const _=new TextDecoder;let u="",E=!1;try{for(;;){const{done:f,value:S}=await A.read();if(f)break;const U=_.decode(S,{stream:!0});u+=U;let k=0,P;for(;(P=u.indexOf(`
|
|
2
2
|
`,k))!==-1;){const m=u.slice(k,P).trim();if(k=P+1,m&&m.startsWith("data: ")){const d=m.slice(6).trim();if(!d||d==="[DONE]"){yield{text:"",done:!0};return}try{const n=JSON.parse(d);if(n.error){const x=`Server error: ${((C=n.error)==null?void 0:C.message)||n.error||"Unknown server error"}`;throw console.warn(`[ApiClient] ${x}`),new Error(x)}if(n.done===!0){E=!0,yield{text:"",done:!0,audio:n.audio,audioFormat:n.audio_format||n.audioFormat,threading:n.threading};return}const b=n.response||"";n.audio_chunk!==void 0&&(yield{text:"",done:!1,audio_chunk:n.audio_chunk,audioFormat:n.audioFormat||n.audio_format||"opus",chunk_index:n.chunk_index??0}),(b||n.audio)&&(E=!0,yield{text:b,done:n.done||!1,audio:n.audio,audioFormat:n.audio_format||n.audioFormat,threading:n.threading})}catch(n){console.warn("[ApiClient] Unable to parse server response. This may be a temporary issue."),console.warn("[ApiClient] Parse error details:",n==null?void 0:n.message),console.warn("[ApiClient] JSON text length:",d==null?void 0:d.length),console.warn("[ApiClient] JSON text preview (first 200 chars):",d==null?void 0:d.substring(0,200)),console.warn("[ApiClient] JSON text preview (last 200 chars):",d==null?void 0:d.substring(d.length-200))}}else m&&(E=!0,yield{text:m,done:!1})}u=u.slice(k),u.length>1e6&&(console.warn("[ApiClient] Buffer too large, truncating..."),u=u.slice(-5e5))}E&&(yield{text:"",done:!0})}finally{A.releaseLock()}}catch(w){throw w.name==="AbortError"?new Error("Connection timed out. Please check if the server is running."):w.name==="TypeError"&&w.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):w}}async clearConversationHistory(i){const e=i||this.sessionId;if(!e)throw new Error("No session ID provided and no current session available");if(!this.apiKey)throw new Error("API key is required for clearing conversation history");const t={"Content-Type":"application/json","X-Session-ID":e,"X-API-Key":this.apiKey};try{const r=await fetch(`${this.apiUrl}/admin/chat-history/${e}`,{...this.getFetchOptions({method:"DELETE",headers:t})});if(!r.ok){const s=await r.text();throw new Error(`Failed to clear conversation history: ${r.status} ${s}`)}return await r.json()}catch(r){throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r}}async deleteConversationWithFiles(i,e){const t=i||this.sessionId;if(!t)throw new Error("No session ID provided and no current session available");if(!this.apiKey)throw new Error("API key is required for deleting conversation");const r={"Content-Type":"application/json","X-Session-ID":t,"X-API-Key":this.apiKey},s=e&&e.length>0?`?file_ids=${e.join(",")}`:"",o=`${this.apiUrl}/admin/conversations/${t}${s}`;try{const a=await fetch(o,{...this.getFetchOptions({method:"DELETE",headers:r})});if(!a.ok){const h=await a.text();throw new Error(`Failed to delete conversation: ${a.status} ${h}`)}return await a.json()}catch(a){throw 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}}async createThread(i,e){if(!this.apiKey)throw new Error("API key is required for creating threads");const t={"Content-Type":"application/json","X-API-Key":this.apiKey};try{const r=await fetch(`${this.apiUrl}/api/threads`,{...this.getFetchOptions({method:"POST",headers:t,body:JSON.stringify({message_id:i,session_id:e})})});if(!r.ok){const s=await r.text();throw new Error(`Failed to create thread: ${r.status} ${s}`)}return await r.json()}catch(r){throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r}}async getThreadInfo(i){if(!this.apiKey)throw new Error("API key is required for getting thread info");const e={"X-API-Key":this.apiKey};try{const t=await fetch(`${this.apiUrl}/api/threads/${i}`,{...this.getFetchOptions({method:"GET",headers:e})});if(!t.ok){const r=await t.text();throw new Error(`Failed to get thread info: ${t.status} ${r}`)}return await t.json()}catch(t){throw t.name==="TypeError"&&t.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):t}}async deleteThread(i){if(!this.apiKey)throw new Error("API key is required for deleting threads");const e={"X-API-Key":this.apiKey};try{const t=await fetch(`${this.apiUrl}/api/threads/${i}`,{...this.getFetchOptions({method:"DELETE",headers:e})});if(!t.ok){const r=await t.text();throw new Error(`Failed to delete thread: ${t.status} ${r}`)}return await t.json()}catch(t){throw t.name==="TypeError"&&t.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):t}}async uploadFile(i){if(!this.apiKey)throw new Error("API key is required for file upload");const e=new FormData;e.append("file",i);try{const t=await fetch(`${this.apiUrl}/api/files/upload`,{...this.getFetchOptions({method:"POST",body:e})});if(!t.ok){const r=await t.text();throw new Error(`Failed to upload file: ${t.status} ${r}`)}return await t.json()}catch(t){throw t.name==="TypeError"&&t.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):t}}async listFiles(){if(!this.apiKey)throw new Error("API key is required for listing files");try{const i=await fetch(`${this.apiUrl}/api/files`,{...this.getFetchOptions({method:"GET"})});if(!i.ok){const e=await i.text();throw new Error(`Failed to list files: ${i.status} ${e}`)}return await i.json()}catch(i){throw i.name==="TypeError"&&i.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):i}}async getFileInfo(i){if(!this.apiKey)throw new Error("API key is required for getting file info");try{const e=await fetch(`${this.apiUrl}/api/files/${i}`,{...this.getFetchOptions({method:"GET"})});if(!e.ok){const t=await e.text();throw new Error(`Failed to get file info: ${e.status} ${t}`)}return await e.json()}catch(e){throw e.name==="TypeError"&&e.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):e}}async queryFile(i,e,t=10){if(!this.apiKey)throw new Error("API key is required for querying files");try{const r=await fetch(`${this.apiUrl}/api/files/${i}/query`,{...this.getFetchOptions({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,max_results:t})})});if(!r.ok){const s=await r.text();throw new Error(`Failed to query file: ${r.status} ${s}`)}return await r.json()}catch(r){throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r}}async deleteFile(i){if(!this.apiKey)throw new Error("API key is required for deleting files");const e=`${this.apiUrl}/api/files/${i}`,t=this.getFetchOptions({method:"DELETE"});try{const r=await fetch(e,t);if(!r.ok){const s=await r.text();let o;try{const a=JSON.parse(s);o=a.detail||a.message||`Failed to delete file (HTTP ${r.status})`}catch{o=`Failed to delete file (HTTP ${r.status})`}throw console.warn(`[ApiClient] ${o}`),new Error(o)}return await r.json()}catch(r){let s;throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?s="Could not connect to the server. Please check if the server is running.":r.message&&!r.message.includes("Failed to delete file")?s=r.message:s="Failed to delete file. Please try again.",console.warn(`[ApiClient] ${s}`),new Error(s)}}}let T=null;const J=(l,i=null,e=null)=>{T=new D({apiUrl:l,apiKey:i,sessionId:e})};async function*L(l,i=!0,e,t,r,s,o,a,h,p,y){if(!T)throw new Error("API not configured. Please call configureApi() with your server URL before using any API functions.");yield*T.streamChat(l,i,e,t,r,s,o,a,h,p,y)}export{D as ApiClient,J as configureApi,L as streamChat};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{aP as sn,aQ as an,aR as Q,aS as W,ag as Z,aT as un,aU as l,ai as on,aV as L,aW as q,aX as cn,aY as e,aZ as en,a_ as rn,a$ as yn}from"./index-BaIhc-dj.js";function fn(s){return s.innerRadius}function ln(s){return s.outerRadius}function gn(s){return s.startAngle}function mn(s){return s.endAngle}function pn(s){return s&&s.padAngle}function xn(s,v,V,K,R,d,_,a){var B=V-s,r=K-v,n=_-R,m=a-d,u=m*B-n*r;if(!(u*u<l))return u=(n*(v-d)-m*(s-R))/u,[s+u*B,v+u*r]}function N(s,v,V,K,R,d,_){var a=s-V,B=v-K,r=(_?d:-d)/L(a*a+B*B),n=r*B,m=-r*a,u=s+n,i=v+m,y=V+n,f=K+m,D=(u+y)/2,o=(i+f)/2,p=y-u,g=f-i,T=p*p+g*g,X=R-d,A=u*f-y*i,P=(g<0?-1:1)*L(en(0,X*X*T-A*A)),S=(A*g-p*P)/T,U=(-A*p-g*P)/T,E=(A*g+p*P)/T,x=(-A*p+g*P)/T,h=S-D,t=U-o,c=E-D,M=x-o;return h*h+t*t>c*c+M*M&&(S=E,U=x),{cx:S,cy:U,x01:-n,y01:-m,x11:S*(R/X-1),y11:U*(R/X-1)}}function vn(){var s=fn,v=ln,V=Z(0),K=null,R=gn,d=mn,_=pn,a=null,B=sn(r);function r(){var n,m,u=+s.apply(this,arguments),i=+v.apply(this,arguments),y=R.apply(this,arguments)-un,f=d.apply(this,arguments)-un,D=cn(f-y),o=f>y;if(a||(a=n=B()),i<u&&(m=i,i=u,u=m),!(i>l))a.moveTo(0,0);else if(D>on-l)a.moveTo(i*Q(y),i*W(y)),a.arc(0,0,i,y,f,!o),u>l&&(a.moveTo(u*Q(f),u*W(f)),a.arc(0,0,u,f,y,o));else{var p=y,g=f,T=y,X=f,A=D,P=D,S=_.apply(this,arguments)/2,U=S>l&&(K?+K.apply(this,arguments):L(u*u+i*i)),E=q(cn(i-u)/2,+V.apply(this,arguments)),x=E,h=E,t,c;if(U>l){var M=rn(U/u*W(S)),$=rn(U/i*W(S));(A-=M*2)>l?(M*=o?1:-1,T+=M,X-=M):(A=0,T=X=(y+f)/2),(P-=$*2)>l?($*=o?1:-1,p+=$,g-=$):(P=0,p=g=(y+f)/2)}var Y=i*Q(p),I=i*W(p),b=u*Q(X),k=u*W(X);if(E>l){var F=i*Q(g),G=i*W(g),C=u*Q(T),H=u*W(T),O;if(D<an)if(O=xn(Y,I,C,H,F,G,b,k)){var J=Y-O[0],j=I-O[1],w=F-O[0],z=G-O[1],nn=1/W(yn((J*w+j*z)/(L(J*J+j*j)*L(w*w+z*z)))/2),tn=L(O[0]*O[0]+O[1]*O[1]);x=q(E,(u-tn)/(nn-1)),h=q(E,(i-tn)/(nn+1))}else x=h=0}P>l?h>l?(t=N(C,H,Y,I,i,h,o),c=N(F,G,b,k,i,h,o),a.moveTo(t.cx+t.x01,t.cy+t.y01),h<E?a.arc(t.cx,t.cy,h,e(t.y01,t.x01),e(c.y01,c.x01),!o):(a.arc(t.cx,t.cy,h,e(t.y01,t.x01),e(t.y11,t.x11),!o),a.arc(0,0,i,e(t.cy+t.y11,t.cx+t.x11),e(c.cy+c.y11,c.cx+c.x11),!o),a.arc(c.cx,c.cy,h,e(c.y11,c.x11),e(c.y01,c.x01),!o))):(a.moveTo(Y,I),a.arc(0,0,i,p,g,!o)):a.moveTo(Y,I),!(u>l)||!(A>l)?a.lineTo(b,k):x>l?(t=N(b,k,F,G,u,-x,o),c=N(Y,I,C,H,u,-x,o),a.lineTo(t.cx+t.x01,t.cy+t.y01),x<E?a.arc(t.cx,t.cy,x,e(t.y01,t.x01),e(c.y01,c.x01),!o):(a.arc(t.cx,t.cy,x,e(t.y01,t.x01),e(t.y11,t.x11),!o),a.arc(0,0,u,e(t.cy+t.y11,t.cx+t.x11),e(c.cy+c.y11,c.cx+c.x11),o),a.arc(c.cx,c.cy,x,e(c.y11,c.x11),e(c.y01,c.x01),!o))):a.arc(0,0,u,X,T,o)}if(a.closePath(),n)return a=null,n+""||null}return r.centroid=function(){var n=(+s.apply(this,arguments)+ +v.apply(this,arguments))/2,m=(+R.apply(this,arguments)+ +d.apply(this,arguments))/2-an/2;return[Q(m)*n,W(m)*n]},r.innerRadius=function(n){return arguments.length?(s=typeof n=="function"?n:Z(+n),r):s},r.outerRadius=function(n){return arguments.length?(v=typeof n=="function"?n:Z(+n),r):v},r.cornerRadius=function(n){return arguments.length?(V=typeof n=="function"?n:Z(+n),r):V},r.padRadius=function(n){return arguments.length?(K=n==null?null:typeof n=="function"?n:Z(+n),r):K},r.startAngle=function(n){return arguments.length?(R=typeof n=="function"?n:Z(+n),r):R},r.endAngle=function(n){return arguments.length?(d=typeof n=="function"?n:Z(+n),r):d},r.padAngle=function(n){return arguments.length?(_=typeof n=="function"?n:Z(+n),r):_},r.context=function(n){return arguments.length?(a=n??null,r):a},r}export{vn as h};
|