@fentz26/envcp 1.0.2 → 1.0.3
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 +3 -3
- package/dist/cli/index.js +382 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/config/manager.d.ts +6 -0
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +77 -0
- package/dist/config/manager.js.map +1 -1
- package/dist/storage/index.d.ts +10 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +89 -6
- package/dist/storage/index.js.map +1 -1
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/crypto.d.ts +3 -0
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +12 -0
- package/dist/utils/crypto.js.map +1 -1
- package/package.json +6 -1
- package/.github/workflows/publish.yml +0 -48
- package/__tests__/config.test.ts +0 -65
- package/__tests__/crypto.test.ts +0 -76
- package/__tests__/http.test.ts +0 -49
- package/__tests__/storage.test.ts +0 -94
- package/jest.config.js +0 -11
- package/src/adapters/base.ts +0 -542
- package/src/adapters/gemini.ts +0 -228
- package/src/adapters/index.ts +0 -4
- package/src/adapters/openai.ts +0 -238
- package/src/adapters/rest.ts +0 -298
- package/src/cli/index.ts +0 -516
- package/src/cli.ts +0 -2
- package/src/config/manager.ts +0 -137
- package/src/index.ts +0 -4
- package/src/mcp/index.ts +0 -1
- package/src/mcp/server.ts +0 -67
- package/src/server/index.ts +0 -1
- package/src/server/unified.ts +0 -474
- package/src/storage/index.ts +0 -128
- package/src/types.ts +0 -183
- package/src/utils/crypto.ts +0 -100
- package/src/utils/http.ts +0 -119
- package/src/utils/session.ts +0 -146
- package/tsconfig.json +0 -20
package/src/adapters/gemini.ts
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { EnvCPConfig, GeminiFunctionDeclaration, GeminiFunctionCall, GeminiFunctionResponse } from '../types.js';
|
|
3
|
-
import { setCorsHeaders, sendJson, parseBody, validateApiKey, RateLimiter, rateLimitMiddleware } from '../utils/http.js';
|
|
4
|
-
import * as http from 'http';
|
|
5
|
-
|
|
6
|
-
export class GeminiAdapter extends BaseAdapter {
|
|
7
|
-
private server: http.Server | null = null;
|
|
8
|
-
private rateLimiter = new RateLimiter(60, 60000);
|
|
9
|
-
|
|
10
|
-
constructor(config: EnvCPConfig, projectPath: string, password?: string) {
|
|
11
|
-
super(config, projectPath, password);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
protected registerTools(): void {
|
|
15
|
-
this.registerDefaultTools();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Convert tools to Gemini function declaration format
|
|
19
|
-
getGeminiFunctionDeclarations(): GeminiFunctionDeclaration[] {
|
|
20
|
-
return this.getToolDefinitions().map(tool => ({
|
|
21
|
-
name: tool.name,
|
|
22
|
-
description: tool.description,
|
|
23
|
-
parameters: {
|
|
24
|
-
type: 'object' as const,
|
|
25
|
-
properties: (tool.parameters as Record<string, unknown>).properties as Record<string, unknown> || {},
|
|
26
|
-
required: (tool.parameters as Record<string, unknown>).required as string[] | undefined,
|
|
27
|
-
},
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Process Gemini function calls
|
|
32
|
-
async processFunctionCalls(calls: GeminiFunctionCall[]): Promise<GeminiFunctionResponse[]> {
|
|
33
|
-
const results: GeminiFunctionResponse[] = [];
|
|
34
|
-
|
|
35
|
-
for (const call of calls) {
|
|
36
|
-
try {
|
|
37
|
-
const result = await this.callTool(call.name, call.args);
|
|
38
|
-
results.push({
|
|
39
|
-
name: call.name,
|
|
40
|
-
response: { result },
|
|
41
|
-
});
|
|
42
|
-
} catch (error: unknown) {
|
|
43
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
-
results.push({
|
|
45
|
-
name: call.name,
|
|
46
|
-
response: { error: message },
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return results;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
async startServer(port: number, host: string, apiKey?: string): Promise<void> {
|
|
56
|
-
await this.init();
|
|
57
|
-
|
|
58
|
-
this.server = http.createServer(async (req, res) => {
|
|
59
|
-
setCorsHeaders(res, undefined, req.headers.origin);
|
|
60
|
-
|
|
61
|
-
if (req.method === 'OPTIONS') {
|
|
62
|
-
res.writeHead(204);
|
|
63
|
-
res.end();
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!rateLimitMiddleware(this.rateLimiter, req, res)) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// API key validation
|
|
72
|
-
if (apiKey) {
|
|
73
|
-
const providedKey = (req.headers['x-goog-api-key'] || req.headers['authorization']?.replace('Bearer ', '')) as string | undefined;
|
|
74
|
-
if (!validateApiKey(providedKey, apiKey)) {
|
|
75
|
-
sendJson(res, 401, { error: { code: 401, message: 'Invalid API key', status: 'UNAUTHENTICATED' } });
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const parsedUrl = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
81
|
-
const pathname = parsedUrl.pathname;
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
// Gemini-compatible endpoints
|
|
85
|
-
|
|
86
|
-
// GET /v1/models - List models
|
|
87
|
-
if (pathname === '/v1/models' && req.method === 'GET') {
|
|
88
|
-
sendJson(res, 200, {
|
|
89
|
-
models: [{
|
|
90
|
-
name: 'models/envcp-1.0',
|
|
91
|
-
displayName: 'EnvCP Tool Server',
|
|
92
|
-
description: 'Environment variable management tools',
|
|
93
|
-
supportedGenerationMethods: ['generateContent'],
|
|
94
|
-
}],
|
|
95
|
-
});
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// GET /v1/tools - List available tools/functions
|
|
100
|
-
if (pathname === '/v1/tools' && req.method === 'GET') {
|
|
101
|
-
sendJson(res, 200, {
|
|
102
|
-
tools: [{
|
|
103
|
-
functionDeclarations: this.getGeminiFunctionDeclarations(),
|
|
104
|
-
}],
|
|
105
|
-
});
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// POST /v1/functions/call - Call a function directly
|
|
110
|
-
if (pathname === '/v1/functions/call' && req.method === 'POST') {
|
|
111
|
-
const body = await parseBody(req);
|
|
112
|
-
const { name, args } = body as { name: string; args: Record<string, unknown> };
|
|
113
|
-
|
|
114
|
-
if (!name) {
|
|
115
|
-
sendJson(res, 400, { error: { code: 400, message: 'Function name required', status: 'INVALID_ARGUMENT' } });
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const result = await this.callTool(name, args || {});
|
|
120
|
-
sendJson(res, 200, {
|
|
121
|
-
name,
|
|
122
|
-
response: { result },
|
|
123
|
-
});
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// POST /v1/function_calls - Process function calls (batch)
|
|
128
|
-
if (pathname === '/v1/function_calls' && req.method === 'POST') {
|
|
129
|
-
const body = await parseBody(req);
|
|
130
|
-
const { functionCalls } = body as { functionCalls: GeminiFunctionCall[] };
|
|
131
|
-
|
|
132
|
-
if (!functionCalls || !Array.isArray(functionCalls)) {
|
|
133
|
-
sendJson(res, 400, { error: { code: 400, message: 'functionCalls array required', status: 'INVALID_ARGUMENT' } });
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const results = await this.processFunctionCalls(functionCalls);
|
|
138
|
-
sendJson(res, 200, {
|
|
139
|
-
functionResponses: results,
|
|
140
|
-
});
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// POST /v1/models/envcp:generateContent - Gemini-style content generation
|
|
145
|
-
if ((pathname === '/v1/models/envcp:generateContent' || pathname === '/v1beta/models/envcp:generateContent') && req.method === 'POST') {
|
|
146
|
-
const body = await parseBody(req);
|
|
147
|
-
const contents = body.contents as Array<{ parts: Array<{ functionCall?: GeminiFunctionCall }> }> | undefined;
|
|
148
|
-
|
|
149
|
-
// Look for function calls in the content
|
|
150
|
-
const functionCalls: GeminiFunctionCall[] = [];
|
|
151
|
-
if (contents) {
|
|
152
|
-
for (const content of contents) {
|
|
153
|
-
for (const part of content.parts || []) {
|
|
154
|
-
if (part.functionCall) {
|
|
155
|
-
functionCalls.push(part.functionCall);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (functionCalls.length > 0) {
|
|
162
|
-
const results = await this.processFunctionCalls(functionCalls);
|
|
163
|
-
sendJson(res, 200, {
|
|
164
|
-
candidates: [{
|
|
165
|
-
content: {
|
|
166
|
-
parts: results.map(r => ({
|
|
167
|
-
functionResponse: r,
|
|
168
|
-
})),
|
|
169
|
-
role: 'model',
|
|
170
|
-
},
|
|
171
|
-
finishReason: 'STOP',
|
|
172
|
-
}],
|
|
173
|
-
});
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Return available tools if no function calls
|
|
178
|
-
sendJson(res, 200, {
|
|
179
|
-
candidates: [{
|
|
180
|
-
content: {
|
|
181
|
-
parts: [{
|
|
182
|
-
text: 'EnvCP tools available. Use function calling to interact with environment variables.',
|
|
183
|
-
}],
|
|
184
|
-
role: 'model',
|
|
185
|
-
},
|
|
186
|
-
finishReason: 'STOP',
|
|
187
|
-
}],
|
|
188
|
-
availableTools: [{
|
|
189
|
-
functionDeclarations: this.getGeminiFunctionDeclarations(),
|
|
190
|
-
}],
|
|
191
|
-
});
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Health check
|
|
196
|
-
if (pathname === '/v1/health' || pathname === '/') {
|
|
197
|
-
sendJson(res, 200, {
|
|
198
|
-
status: 'ok',
|
|
199
|
-
version: '1.0.0',
|
|
200
|
-
mode: 'gemini',
|
|
201
|
-
endpoints: ['/v1/models', '/v1/tools', '/v1/functions/call', '/v1/function_calls', '/v1/models/envcp:generateContent'],
|
|
202
|
-
});
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// 404
|
|
207
|
-
sendJson(res, 404, { error: { code: 404, message: 'Not found', status: 'NOT_FOUND' } });
|
|
208
|
-
|
|
209
|
-
} catch (error: unknown) {
|
|
210
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
211
|
-
sendJson(res, 500, { error: { code: 500, message, status: 'INTERNAL' } });
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
return new Promise((resolve) => {
|
|
216
|
-
this.server!.listen(port, host, () => {
|
|
217
|
-
resolve();
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
stopServer(): void {
|
|
223
|
-
if (this.server) {
|
|
224
|
-
this.server.close();
|
|
225
|
-
this.server = null;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
package/src/adapters/index.ts
DELETED
package/src/adapters/openai.ts
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import { BaseAdapter } from './base.js';
|
|
2
|
-
import { EnvCPConfig, OpenAIFunction, OpenAIToolCall, OpenAIMessage } from '../types.js';
|
|
3
|
-
import { setCorsHeaders, sendJson, parseBody, validateApiKey, RateLimiter, rateLimitMiddleware } from '../utils/http.js';
|
|
4
|
-
import * as http from 'http';
|
|
5
|
-
|
|
6
|
-
export class OpenAIAdapter extends BaseAdapter {
|
|
7
|
-
private server: http.Server | null = null;
|
|
8
|
-
private rateLimiter = new RateLimiter(60, 60000);
|
|
9
|
-
|
|
10
|
-
constructor(config: EnvCPConfig, projectPath: string, password?: string) {
|
|
11
|
-
super(config, projectPath, password);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
protected registerTools(): void {
|
|
15
|
-
this.registerDefaultTools();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Convert tools to OpenAI function format
|
|
19
|
-
getOpenAIFunctions(): OpenAIFunction[] {
|
|
20
|
-
return this.getToolDefinitions().map(tool => ({
|
|
21
|
-
name: tool.name,
|
|
22
|
-
description: tool.description,
|
|
23
|
-
parameters: {
|
|
24
|
-
type: 'object' as const,
|
|
25
|
-
properties: (tool.parameters as Record<string, unknown>).properties as Record<string, unknown> || {},
|
|
26
|
-
required: (tool.parameters as Record<string, unknown>).required as string[] | undefined,
|
|
27
|
-
},
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Process OpenAI tool calls
|
|
32
|
-
async processToolCalls(toolCalls: OpenAIToolCall[]): Promise<OpenAIMessage[]> {
|
|
33
|
-
const results: OpenAIMessage[] = [];
|
|
34
|
-
|
|
35
|
-
for (const call of toolCalls) {
|
|
36
|
-
try {
|
|
37
|
-
const args = JSON.parse(call.function.arguments);
|
|
38
|
-
const result = await this.callTool(call.function.name, args);
|
|
39
|
-
results.push({
|
|
40
|
-
role: 'tool',
|
|
41
|
-
tool_call_id: call.id,
|
|
42
|
-
content: JSON.stringify(result),
|
|
43
|
-
});
|
|
44
|
-
} catch (error: unknown) {
|
|
45
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
46
|
-
results.push({
|
|
47
|
-
role: 'tool',
|
|
48
|
-
tool_call_id: call.id,
|
|
49
|
-
content: JSON.stringify({ error: message }),
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return results;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
async startServer(port: number, host: string, apiKey?: string): Promise<void> {
|
|
59
|
-
await this.init();
|
|
60
|
-
|
|
61
|
-
this.server = http.createServer(async (req, res) => {
|
|
62
|
-
setCorsHeaders(res, undefined, req.headers.origin);
|
|
63
|
-
|
|
64
|
-
if (req.method === 'OPTIONS') {
|
|
65
|
-
res.writeHead(204);
|
|
66
|
-
res.end();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!rateLimitMiddleware(this.rateLimiter, req, res)) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// API key validation
|
|
75
|
-
if (apiKey) {
|
|
76
|
-
const providedKey = req.headers['authorization']?.replace('Bearer ', '');
|
|
77
|
-
if (!validateApiKey(providedKey, apiKey)) {
|
|
78
|
-
sendJson(res, 401, { error: { message: 'Invalid API key', type: 'invalid_api_key' } });
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const parsedUrl = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
84
|
-
const pathname = parsedUrl.pathname;
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
// OpenAI-compatible endpoints
|
|
88
|
-
|
|
89
|
-
// GET /v1/models - List models (for compatibility)
|
|
90
|
-
if (pathname === '/v1/models' && req.method === 'GET') {
|
|
91
|
-
sendJson(res, 200, {
|
|
92
|
-
object: 'list',
|
|
93
|
-
data: [{
|
|
94
|
-
id: 'envcp-1.0',
|
|
95
|
-
object: 'model',
|
|
96
|
-
created: Date.now(),
|
|
97
|
-
owned_by: 'envcp',
|
|
98
|
-
}],
|
|
99
|
-
});
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// GET /v1/functions - List available functions
|
|
104
|
-
if (pathname === '/v1/functions' && req.method === 'GET') {
|
|
105
|
-
sendJson(res, 200, {
|
|
106
|
-
object: 'list',
|
|
107
|
-
data: this.getOpenAIFunctions(),
|
|
108
|
-
});
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// POST /v1/functions/call - Call a function directly
|
|
113
|
-
if (pathname === '/v1/functions/call' && req.method === 'POST') {
|
|
114
|
-
const body = await parseBody(req);
|
|
115
|
-
const { name, arguments: args } = body as { name: string; arguments: Record<string, unknown> };
|
|
116
|
-
|
|
117
|
-
if (!name) {
|
|
118
|
-
sendJson(res, 400, { error: { message: 'Function name required', type: 'invalid_request_error' } });
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const result = await this.callTool(name, args || {});
|
|
123
|
-
sendJson(res, 200, {
|
|
124
|
-
object: 'function_result',
|
|
125
|
-
name,
|
|
126
|
-
result,
|
|
127
|
-
});
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// POST /v1/tool_calls - Process tool calls (batch)
|
|
132
|
-
if (pathname === '/v1/tool_calls' && req.method === 'POST') {
|
|
133
|
-
const body = await parseBody(req);
|
|
134
|
-
const { tool_calls } = body as { tool_calls: OpenAIToolCall[] };
|
|
135
|
-
|
|
136
|
-
if (!tool_calls || !Array.isArray(tool_calls)) {
|
|
137
|
-
sendJson(res, 400, { error: { message: 'tool_calls array required', type: 'invalid_request_error' } });
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const results = await this.processToolCalls(tool_calls);
|
|
142
|
-
sendJson(res, 200, {
|
|
143
|
-
object: 'list',
|
|
144
|
-
data: results,
|
|
145
|
-
});
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// POST /v1/chat/completions - For integration with proxies
|
|
150
|
-
// This allows tools to be called through a chat completion-like interface
|
|
151
|
-
if (pathname === '/v1/chat/completions' && req.method === 'POST') {
|
|
152
|
-
const body = await parseBody(req);
|
|
153
|
-
const messages = body.messages as OpenAIMessage[] | undefined;
|
|
154
|
-
|
|
155
|
-
if (!messages || !Array.isArray(messages)) {
|
|
156
|
-
sendJson(res, 400, { error: { message: 'messages array required', type: 'invalid_request_error' } });
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Check if last message has tool_calls to process
|
|
161
|
-
const lastMessage = messages[messages.length - 1];
|
|
162
|
-
if (lastMessage?.tool_calls) {
|
|
163
|
-
const results = await this.processToolCalls(lastMessage.tool_calls);
|
|
164
|
-
sendJson(res, 200, {
|
|
165
|
-
id: `chatcmpl-${Date.now()}`,
|
|
166
|
-
object: 'chat.completion',
|
|
167
|
-
created: Math.floor(Date.now() / 1000),
|
|
168
|
-
model: 'envcp-1.0',
|
|
169
|
-
choices: [{
|
|
170
|
-
index: 0,
|
|
171
|
-
message: {
|
|
172
|
-
role: 'assistant',
|
|
173
|
-
content: null,
|
|
174
|
-
tool_calls: null,
|
|
175
|
-
},
|
|
176
|
-
finish_reason: 'tool_calls',
|
|
177
|
-
}],
|
|
178
|
-
tool_results: results,
|
|
179
|
-
});
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Return available tools if no tool_calls
|
|
184
|
-
sendJson(res, 200, {
|
|
185
|
-
id: `chatcmpl-${Date.now()}`,
|
|
186
|
-
object: 'chat.completion',
|
|
187
|
-
created: Math.floor(Date.now() / 1000),
|
|
188
|
-
model: 'envcp-1.0',
|
|
189
|
-
choices: [{
|
|
190
|
-
index: 0,
|
|
191
|
-
message: {
|
|
192
|
-
role: 'assistant',
|
|
193
|
-
content: 'EnvCP tools available. Use function calling to interact with environment variables.',
|
|
194
|
-
},
|
|
195
|
-
finish_reason: 'stop',
|
|
196
|
-
}],
|
|
197
|
-
available_tools: this.getOpenAIFunctions().map(f => ({
|
|
198
|
-
type: 'function',
|
|
199
|
-
function: f,
|
|
200
|
-
})),
|
|
201
|
-
});
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Health check
|
|
206
|
-
if (pathname === '/v1/health' || pathname === '/') {
|
|
207
|
-
sendJson(res, 200, {
|
|
208
|
-
status: 'ok',
|
|
209
|
-
version: '1.0.0',
|
|
210
|
-
mode: 'openai',
|
|
211
|
-
endpoints: ['/v1/models', '/v1/functions', '/v1/functions/call', '/v1/tool_calls', '/v1/chat/completions'],
|
|
212
|
-
});
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// 404
|
|
217
|
-
sendJson(res, 404, { error: { message: 'Not found', type: 'not_found' } });
|
|
218
|
-
|
|
219
|
-
} catch (error: unknown) {
|
|
220
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
221
|
-
sendJson(res, 500, { error: { message, type: 'internal_error' } });
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
return new Promise((resolve) => {
|
|
226
|
-
this.server!.listen(port, host, () => {
|
|
227
|
-
resolve();
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
stopServer(): void {
|
|
233
|
-
if (this.server) {
|
|
234
|
-
this.server.close();
|
|
235
|
-
this.server = null;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|