agent-consultation-mcp 1.0.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/LICENSE +21 -0
- package/README.md +140 -0
- package/dist/api/index.d.ts +27 -0
- package/dist/api/index.js +213 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/middleware/security.d.ts +6 -0
- package/dist/api/middleware/security.js +28 -0
- package/dist/api/middleware/security.js.map +1 -0
- package/dist/api/routes/chat.d.ts +2 -0
- package/dist/api/routes/chat.js +61 -0
- package/dist/api/routes/chat.js.map +1 -0
- package/dist/api/routes/config.d.ts +2 -0
- package/dist/api/routes/config.js +81 -0
- package/dist/api/routes/config.js.map +1 -0
- package/dist/api/routes/providers.d.ts +2 -0
- package/dist/api/routes/providers.js +225 -0
- package/dist/api/routes/providers.js.map +1 -0
- package/dist/api/standalone-server.d.ts +12 -0
- package/dist/api/standalone-server.js +91 -0
- package/dist/api/standalone-server.js.map +1 -0
- package/dist/config/defaults.d.ts +17 -0
- package/dist/config/defaults.js +30 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/encryption.d.ts +12 -0
- package/dist/config/encryption.js +85 -0
- package/dist/config/encryption.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manager.d.ts +62 -0
- package/dist/config/manager.js +186 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/prompts.d.ts +10 -0
- package/dist/config/prompts.js +140 -0
- package/dist/config/prompts.js.map +1 -0
- package/dist/config/schema.d.ts +141 -0
- package/dist/config/schema.js +54 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +224 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/base.d.ts +44 -0
- package/dist/providers/base.js +84 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/deepseek.d.ts +30 -0
- package/dist/providers/deepseek.js +148 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +30 -0
- package/dist/providers/openai.js +123 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/registry.d.ts +37 -0
- package/dist/providers/registry.js +65 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +71 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/server/conversation.d.ts +101 -0
- package/dist/server/conversation.js +275 -0
- package/dist/server/conversation.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/tools/consult.d.ts +15 -0
- package/dist/server/tools/consult.js +164 -0
- package/dist/server/tools/consult.js.map +1 -0
- package/dist/server/tools/index.d.ts +1 -0
- package/dist/server/tools/index.js +2 -0
- package/dist/server/tools/index.js.map +1 -0
- package/dist/types/config.d.ts +20 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/messages.d.ts +48 -0
- package/dist/types/messages.js +2 -0
- package/dist/types/messages.js.map +1 -0
- package/dist/types/models.d.ts +39 -0
- package/dist/types/models.js +66 -0
- package/dist/types/models.js.map +1 -0
- package/dist/ui/index.html +1044 -0
- package/dist/utils/errors.d.ts +32 -0
- package/dist/utils/errors.js +53 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.js +73 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Menes Ekinci
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Agent Consultation MCP
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that allows AI agents like Claude Code to consult other AI models (DeepSeek Reasoner, OpenAI GPT-5.2) for alternative perspectives during coding sessions.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-Provider Support**: DeepSeek (Reasoner, Chat) and OpenAI (GPT-5.2, GPT-5.2 Pro)
|
|
8
|
+
- **Specialized Consultation Modes**: Debug, Code Analysis, Architecture Review, Plan Validation, Concept Explanation
|
|
9
|
+
- **Conversation Management**: Continue multi-turn conversations with context
|
|
10
|
+
- **Web UI**: Configure providers and API keys through a browser interface
|
|
11
|
+
- **Encrypted Storage**: API keys are encrypted at rest
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### Via npx (Recommended)
|
|
16
|
+
|
|
17
|
+
No installation required - just add to your Claude Code MCP settings:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"agent-consultation": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "agent-consultation-mcp"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Global Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g agent-consultation-mcp
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then add to your MCP settings:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"agent-consultation": {
|
|
42
|
+
"command": "agent-consultation-mcp"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
### Setting Up API Keys
|
|
51
|
+
|
|
52
|
+
1. **Via Web UI** (Recommended):
|
|
53
|
+
```bash
|
|
54
|
+
npx agent-consultation-mcp --config
|
|
55
|
+
```
|
|
56
|
+
This opens a browser where you can configure providers and API keys.
|
|
57
|
+
|
|
58
|
+
2. **Via Config File**:
|
|
59
|
+
Config is stored at `~/.agent-consultation-mcp/config.json`
|
|
60
|
+
|
|
61
|
+
### Supported Providers
|
|
62
|
+
|
|
63
|
+
| Provider | Models | API Key |
|
|
64
|
+
|----------|--------|---------|
|
|
65
|
+
| DeepSeek | `deepseek-reasoner` (default), `deepseek-chat` | [Get API Key](https://platform.deepseek.com/) |
|
|
66
|
+
| OpenAI | `gpt-5.2`, `gpt-5.2-pro` | [Get API Key](https://platform.openai.com/) |
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
Once configured, Claude Code can use the following tools:
|
|
71
|
+
|
|
72
|
+
### consult_agent
|
|
73
|
+
|
|
74
|
+
Get a second opinion from another AI model.
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Parameters:
|
|
78
|
+
- question (required): The question or problem to get advice on
|
|
79
|
+
- mode (optional): Consultation mode - debug, analyzeCode, reviewArchitecture, validatePlan, explainConcept, general
|
|
80
|
+
- context (optional): Additional context like code snippets or error messages
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### continue_conversation
|
|
84
|
+
|
|
85
|
+
Continue an existing consultation conversation.
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Parameters:
|
|
89
|
+
- conversationId (required): The conversation ID from a previous consultation
|
|
90
|
+
- message (required): Your follow-up message
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### end_conversation
|
|
94
|
+
|
|
95
|
+
End an active consultation conversation.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Parameters:
|
|
99
|
+
- conversationId (required): The conversation ID to end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Example
|
|
103
|
+
|
|
104
|
+
In Claude Code, you might use it like:
|
|
105
|
+
|
|
106
|
+
> "Can you consult DeepSeek about this architecture decision? I'm not sure if using a monorepo is the right choice for this microservices setup."
|
|
107
|
+
|
|
108
|
+
Claude will then call the `consult_agent` tool with your question and provide the response.
|
|
109
|
+
|
|
110
|
+
## Consultation Modes
|
|
111
|
+
|
|
112
|
+
| Mode | Description |
|
|
113
|
+
|------|-------------|
|
|
114
|
+
| `debug` | Focus on finding bugs, analyzing errors, and suggesting fixes |
|
|
115
|
+
| `analyzeCode` | Code review focusing on quality, patterns, and improvements |
|
|
116
|
+
| `reviewArchitecture` | Evaluate architectural decisions and suggest alternatives |
|
|
117
|
+
| `validatePlan` | Review implementation plans for completeness and risks |
|
|
118
|
+
| `explainConcept` | Explain technical concepts clearly |
|
|
119
|
+
| `general` | General-purpose consultation |
|
|
120
|
+
|
|
121
|
+
## Development
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Clone the repo
|
|
125
|
+
git clone https://github.com/menesekinci/agent-consultation-mcp.git
|
|
126
|
+
cd agent-consultation-mcp
|
|
127
|
+
|
|
128
|
+
# Install dependencies
|
|
129
|
+
npm install
|
|
130
|
+
|
|
131
|
+
# Run in development mode
|
|
132
|
+
npm run dev
|
|
133
|
+
|
|
134
|
+
# Build for production
|
|
135
|
+
npm run build
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
MIT
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ConfigUIOptions {
|
|
2
|
+
port?: number;
|
|
3
|
+
openBrowser?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Start the configuration UI HTTP server
|
|
7
|
+
*/
|
|
8
|
+
export declare function startConfigUI(options?: ConfigUIOptions): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Start the UI server in background mode (for MCP mode)
|
|
11
|
+
* Does not block and does not open browser automatically
|
|
12
|
+
*/
|
|
13
|
+
export declare function startBackgroundUIServer(port?: number): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* Open the Web UI in browser (only opens once per session)
|
|
16
|
+
* Spawns a completely separate standalone server process that handles
|
|
17
|
+
* both server startup and browser opening.
|
|
18
|
+
*/
|
|
19
|
+
export declare function openWebUI(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if browser has been opened
|
|
22
|
+
*/
|
|
23
|
+
export declare function isBrowserOpened(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Check if background server is running
|
|
26
|
+
*/
|
|
27
|
+
export declare function isBackgroundServerRunning(): boolean;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import open from 'open';
|
|
6
|
+
import { getConfigManager } from '../config/index.js';
|
|
7
|
+
import { logger } from '../utils/index.js';
|
|
8
|
+
import { configRoutes } from './routes/config.js';
|
|
9
|
+
import { providerRoutes } from './routes/providers.js';
|
|
10
|
+
import chatRoutes from './routes/chat.js';
|
|
11
|
+
import { securityMiddleware } from './middleware/security.js';
|
|
12
|
+
// Get __dirname equivalent in ES modules
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
const DEFAULT_PORT = 3456;
|
|
16
|
+
// Track background UI server state
|
|
17
|
+
let backgroundServerRunning = false;
|
|
18
|
+
let browserOpened = false;
|
|
19
|
+
let backgroundServerPort = DEFAULT_PORT;
|
|
20
|
+
/**
|
|
21
|
+
* Start the configuration UI HTTP server
|
|
22
|
+
*/
|
|
23
|
+
export async function startConfigUI(options = {}) {
|
|
24
|
+
const port = options.port ?? DEFAULT_PORT;
|
|
25
|
+
const shouldOpenBrowser = options.openBrowser ?? true;
|
|
26
|
+
// Ensure config is loaded
|
|
27
|
+
const configManager = getConfigManager();
|
|
28
|
+
await configManager.init();
|
|
29
|
+
const app = express();
|
|
30
|
+
// Middleware
|
|
31
|
+
app.use(express.json());
|
|
32
|
+
app.use(securityMiddleware);
|
|
33
|
+
// API routes
|
|
34
|
+
app.use('/api/config', configRoutes);
|
|
35
|
+
app.use('/api/providers', providerRoutes);
|
|
36
|
+
app.use('/api/chat', chatRoutes);
|
|
37
|
+
// Serve static UI files
|
|
38
|
+
const uiPath = path.join(__dirname, '../ui');
|
|
39
|
+
app.use(express.static(uiPath));
|
|
40
|
+
// Fallback to index.html for SPA-like behavior (Express 5 compatible)
|
|
41
|
+
app.use((_req, res, next) => {
|
|
42
|
+
// Only serve index.html for non-API requests that don't have a file extension
|
|
43
|
+
if (!_req.path.startsWith('/api') && !path.extname(_req.path)) {
|
|
44
|
+
res.sendFile(path.join(uiPath, 'index.html'));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
next();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// Error handling middleware
|
|
51
|
+
app.use((err, _req, res, _next) => {
|
|
52
|
+
logger.error('HTTP server error', { error: err.message });
|
|
53
|
+
res.status(500).json({
|
|
54
|
+
error: 'Internal server error',
|
|
55
|
+
message: err.message,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
// CRITICAL: Bind to localhost only for security
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const server = app.listen(port, '127.0.0.1', () => {
|
|
61
|
+
const url = `http://127.0.0.1:${port}`;
|
|
62
|
+
logger.info(`Config UI running at ${url}`);
|
|
63
|
+
console.log(`
|
|
64
|
+
┌─────────────────────────────────────────────────┐
|
|
65
|
+
│ │
|
|
66
|
+
│ Agent Consultation MCP - Configuration UI │
|
|
67
|
+
│ │
|
|
68
|
+
│ URL: ${url.padEnd(40)}│
|
|
69
|
+
│ │
|
|
70
|
+
│ Press Ctrl+C to stop │
|
|
71
|
+
│ │
|
|
72
|
+
└─────────────────────────────────────────────────┘
|
|
73
|
+
`);
|
|
74
|
+
// Open browser
|
|
75
|
+
if (shouldOpenBrowser) {
|
|
76
|
+
open(url).catch((err) => {
|
|
77
|
+
logger.warn('Failed to open browser', { error: err.message });
|
|
78
|
+
console.log(`Please open ${url} in your browser`);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
resolve();
|
|
82
|
+
});
|
|
83
|
+
server.on('error', (err) => {
|
|
84
|
+
if (err.code === 'EADDRINUSE') {
|
|
85
|
+
logger.error(`Port ${port} is already in use`);
|
|
86
|
+
console.error(`\nError: Port ${port} is already in use.`);
|
|
87
|
+
console.error(`Try a different port: npx agent-consultation-mcp --config --port ${port + 1}\n`);
|
|
88
|
+
}
|
|
89
|
+
reject(err);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Start the UI server in background mode (for MCP mode)
|
|
95
|
+
* Does not block and does not open browser automatically
|
|
96
|
+
*/
|
|
97
|
+
export async function startBackgroundUIServer(port = DEFAULT_PORT) {
|
|
98
|
+
if (backgroundServerRunning) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
// Ensure config is loaded
|
|
102
|
+
const configManager = getConfigManager();
|
|
103
|
+
await configManager.init();
|
|
104
|
+
const app = express();
|
|
105
|
+
// Middleware
|
|
106
|
+
app.use(express.json());
|
|
107
|
+
app.use(securityMiddleware);
|
|
108
|
+
// API routes
|
|
109
|
+
app.use('/api/config', configRoutes);
|
|
110
|
+
app.use('/api/providers', providerRoutes);
|
|
111
|
+
app.use('/api/chat', chatRoutes);
|
|
112
|
+
// Serve static UI files
|
|
113
|
+
const uiPath = path.join(__dirname, '../ui');
|
|
114
|
+
app.use(express.static(uiPath));
|
|
115
|
+
// Fallback to index.html for SPA-like behavior
|
|
116
|
+
app.use((_req, res, next) => {
|
|
117
|
+
if (!_req.path.startsWith('/api') && !path.extname(_req.path)) {
|
|
118
|
+
res.sendFile(path.join(uiPath, 'index.html'));
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
next();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Error handling middleware
|
|
125
|
+
app.use((err, _req, res, _next) => {
|
|
126
|
+
logger.error('Background HTTP server error', { error: err.message });
|
|
127
|
+
res.status(500).json({
|
|
128
|
+
error: 'Internal server error',
|
|
129
|
+
message: err.message,
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
const server = app.listen(port, '127.0.0.1', () => {
|
|
134
|
+
backgroundServerRunning = true;
|
|
135
|
+
backgroundServerPort = port;
|
|
136
|
+
logger.info(`Background UI server started at http://127.0.0.1:${port}`);
|
|
137
|
+
resolve(true);
|
|
138
|
+
});
|
|
139
|
+
server.on('error', (err) => {
|
|
140
|
+
if (err.code === 'EADDRINUSE') {
|
|
141
|
+
// Port is in use, maybe server is already running from another process
|
|
142
|
+
logger.debug(`Port ${port} already in use, assuming UI server is running`);
|
|
143
|
+
backgroundServerRunning = true;
|
|
144
|
+
backgroundServerPort = port;
|
|
145
|
+
resolve(true);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
logger.error('Failed to start background UI server', { error: err.message });
|
|
149
|
+
resolve(false);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Open the Web UI in browser (only opens once per session)
|
|
156
|
+
* Spawns a completely separate standalone server process that handles
|
|
157
|
+
* both server startup and browser opening.
|
|
158
|
+
*/
|
|
159
|
+
export async function openWebUI() {
|
|
160
|
+
logger.info('openWebUI called', { browserOpened });
|
|
161
|
+
if (browserOpened) {
|
|
162
|
+
logger.info('Browser already opened, skipping');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
// Path to the standalone server script
|
|
166
|
+
const standaloneServerPath = path.join(__dirname, 'standalone-server.js');
|
|
167
|
+
logger.info('Spawning standalone server process', { standaloneServerPath });
|
|
168
|
+
try {
|
|
169
|
+
// Spawn a completely detached process
|
|
170
|
+
// This process will start the server AND open the browser
|
|
171
|
+
const child = spawn(process.execPath, [standaloneServerPath, String(backgroundServerPort)], {
|
|
172
|
+
detached: true,
|
|
173
|
+
stdio: 'ignore',
|
|
174
|
+
windowsHide: true,
|
|
175
|
+
env: { ...process.env }
|
|
176
|
+
});
|
|
177
|
+
// Unref to allow parent (MCP) process to exit independently
|
|
178
|
+
child.unref();
|
|
179
|
+
browserOpened = true;
|
|
180
|
+
backgroundServerRunning = true;
|
|
181
|
+
logger.info('Standalone server process spawned successfully');
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
logger.error('Failed to spawn standalone server process', {
|
|
185
|
+
error: err instanceof Error ? err.message : 'Unknown error'
|
|
186
|
+
});
|
|
187
|
+
// Fallback: try to open browser directly (server might already be running)
|
|
188
|
+
const url = `http://127.0.0.1:${backgroundServerPort}`;
|
|
189
|
+
try {
|
|
190
|
+
await open(url, { wait: false });
|
|
191
|
+
browserOpened = true;
|
|
192
|
+
logger.info('Browser opened via fallback');
|
|
193
|
+
}
|
|
194
|
+
catch (openErr) {
|
|
195
|
+
logger.error('Fallback browser open also failed', {
|
|
196
|
+
error: openErr instanceof Error ? openErr.message : 'Unknown error'
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Check if browser has been opened
|
|
203
|
+
*/
|
|
204
|
+
export function isBrowserOpened() {
|
|
205
|
+
return browserOpened;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if background server is running
|
|
209
|
+
*/
|
|
210
|
+
export function isBackgroundServerRunning() {
|
|
211
|
+
return backgroundServerRunning;
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Security middleware for the config UI HTTP server
|
|
4
|
+
* Adds security headers to all responses
|
|
5
|
+
*/
|
|
6
|
+
export declare function securityMiddleware(_req: Request, res: Response, next: NextFunction): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security middleware for the config UI HTTP server
|
|
3
|
+
* Adds security headers to all responses
|
|
4
|
+
*/
|
|
5
|
+
export function securityMiddleware(_req, res, next) {
|
|
6
|
+
// Prevent MIME type sniffing
|
|
7
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
8
|
+
// Prevent clickjacking
|
|
9
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
10
|
+
// Enable XSS filter in browsers
|
|
11
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
12
|
+
// Content Security Policy - allow CDN resources for Alpine.js and Tailwind
|
|
13
|
+
// Note: 'unsafe-eval' is required for Alpine.js to evaluate expressions
|
|
14
|
+
// This is acceptable since the UI runs on localhost only
|
|
15
|
+
res.setHeader('Content-Security-Policy', [
|
|
16
|
+
"default-src 'self'",
|
|
17
|
+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.tailwindcss.com https://unpkg.com",
|
|
18
|
+
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
|
19
|
+
"img-src 'self' data:",
|
|
20
|
+
"font-src 'self' https://fonts.gstatic.com",
|
|
21
|
+
"connect-src 'self'",
|
|
22
|
+
].join('; '));
|
|
23
|
+
// Prevent caching of sensitive data
|
|
24
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
25
|
+
res.setHeader('Pragma', 'no-cache');
|
|
26
|
+
next();
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/api/middleware/security.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAa,EACb,GAAa,EACb,IAAkB;IAElB,6BAA6B;IAC7B,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;IAEnD,uBAAuB;IACvB,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAEzC,gCAAgC;IAChC,GAAG,CAAC,SAAS,CAAC,kBAAkB,EAAE,eAAe,CAAC,CAAC;IAEnD,2EAA2E;IAC3E,wEAAwE;IACxE,yDAAyD;IACzD,GAAG,CAAC,SAAS,CACX,yBAAyB,EACzB;QACE,oBAAoB;QACpB,+FAA+F;QAC/F,+DAA+D;QAC/D,sBAAsB;QACtB,2CAA2C;QAC3C,oBAAoB;KACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;IAEF,oCAAoC;IACpC,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,qCAAqC,CAAC,CAAC;IACtE,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEpC,IAAI,EAAE,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { ConversationManager } from '../../server/conversation.js';
|
|
3
|
+
const router = Router();
|
|
4
|
+
/**
|
|
5
|
+
* GET /api/chat/history
|
|
6
|
+
* Returns all conversations (active + archived) from files
|
|
7
|
+
*/
|
|
8
|
+
router.get('/history', (_req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
// Load active conversations
|
|
11
|
+
const activeConversations = ConversationManager.loadFromFile();
|
|
12
|
+
// Load archived conversations
|
|
13
|
+
const archivedConversations = ConversationManager.loadHistoryFromFile();
|
|
14
|
+
// Format active conversations for UI display
|
|
15
|
+
const active = activeConversations.map((conv) => ({
|
|
16
|
+
id: conv.id,
|
|
17
|
+
model: conv.model,
|
|
18
|
+
messageCount: conv.messages.length,
|
|
19
|
+
messages: conv.messages.map((msg, idx) => ({
|
|
20
|
+
id: `${conv.id}-${idx}`,
|
|
21
|
+
role: msg.role,
|
|
22
|
+
content: msg.content,
|
|
23
|
+
})),
|
|
24
|
+
createdAt: conv.createdAt.toISOString(),
|
|
25
|
+
lastActivityAt: conv.lastActivityAt.toISOString(),
|
|
26
|
+
status: 'active',
|
|
27
|
+
}));
|
|
28
|
+
// Format archived conversations for UI display
|
|
29
|
+
const archived = archivedConversations.map((conv) => ({
|
|
30
|
+
id: conv.id,
|
|
31
|
+
model: conv.model,
|
|
32
|
+
messageCount: conv.messages.length,
|
|
33
|
+
messages: conv.messages.map((msg, idx) => ({
|
|
34
|
+
id: `${conv.id}-${idx}`,
|
|
35
|
+
role: msg.role,
|
|
36
|
+
content: msg.content,
|
|
37
|
+
})),
|
|
38
|
+
createdAt: conv.createdAt,
|
|
39
|
+
lastActivityAt: conv.lastActivityAt,
|
|
40
|
+
endedAt: conv.endedAt,
|
|
41
|
+
endReason: conv.endReason,
|
|
42
|
+
status: 'archived',
|
|
43
|
+
}));
|
|
44
|
+
// Combine: active first, then archived (already sorted by newest first)
|
|
45
|
+
const allConversations = [...active, ...archived];
|
|
46
|
+
res.json({
|
|
47
|
+
count: allConversations.length,
|
|
48
|
+
activeCount: active.length,
|
|
49
|
+
archivedCount: archived.length,
|
|
50
|
+
conversations: allConversations,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
res.status(500).json({
|
|
55
|
+
error: 'Failed to fetch conversation history',
|
|
56
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
export default router;
|
|
61
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../src/api/routes/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA+B,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAgD,MAAM,8BAA8B,CAAC;AAGjH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;AAExB;;;GAGG;AACH,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;IACtD,IAAI,CAAC;QACH,4BAA4B;QAC5B,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,YAAY,EAAE,CAAC;QAE/D,8BAA8B;QAC9B,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,mBAAmB,EAAE,CAAC;QAExE,6CAA6C;QAC7C,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAkB,EAAE,EAAE,CAAC,CAAC;YAC9D,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;YAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAY,EAAE,GAAW,EAAE,EAAE,CAAC,CAAC;gBAC1D,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE;gBACvB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB,CAAC,CAAC;YACH,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE;YACvC,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE;YACjD,MAAM,EAAE,QAAiB;SAC1B,CAAC,CAAC,CAAC;QAEJ,+CAA+C;QAC/C,MAAM,QAAQ,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC,IAA0B,EAAE,EAAE,CAAC,CAAC;YAC1E,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;YAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAY,EAAE,GAAW,EAAE,EAAE,CAAC,CAAC;gBAC1D,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE;gBACvB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB,CAAC,CAAC;YACH,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,MAAM,EAAE,UAAmB;SAC5B,CAAC,CAAC,CAAC;QAEJ,wEAAwE;QACxE,MAAM,gBAAgB,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC;QAElD,GAAG,CAAC,IAAI,CAAC;YACP,KAAK,EAAE,gBAAgB,CAAC,MAAM;YAC9B,WAAW,EAAE,MAAM,CAAC,MAAM;YAC1B,aAAa,EAAE,QAAQ,CAAC,MAAM;YAC9B,aAAa,EAAE,gBAAgB;SAChC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,sCAAsC;YAC7C,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;SAClE,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { getConfigManager } from '../../config/index.js';
|
|
3
|
+
import { MODEL_TYPES } from '../../types/index.js';
|
|
4
|
+
const router = Router();
|
|
5
|
+
/**
|
|
6
|
+
* GET /api/config - Get current configuration (with masked keys)
|
|
7
|
+
*/
|
|
8
|
+
router.get('/', (_req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
const config = getConfigManager().getConfig();
|
|
11
|
+
// Build safe response with masked keys
|
|
12
|
+
const safeConfig = {
|
|
13
|
+
defaultModel: config.defaultModel,
|
|
14
|
+
maxMessages: config.maxMessages,
|
|
15
|
+
availableModels: MODEL_TYPES,
|
|
16
|
+
providers: Object.fromEntries(Object.entries(config.providers).map(([id, cfg]) => [
|
|
17
|
+
id,
|
|
18
|
+
{
|
|
19
|
+
enabled: cfg.enabled,
|
|
20
|
+
hasKey: !!cfg.apiKey,
|
|
21
|
+
},
|
|
22
|
+
])),
|
|
23
|
+
};
|
|
24
|
+
res.json(safeConfig);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
res.status(500).json({
|
|
28
|
+
error: 'Failed to get configuration',
|
|
29
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* PATCH /api/config - Update configuration (defaultModel, maxMessages)
|
|
35
|
+
*/
|
|
36
|
+
router.patch('/', async (req, res) => {
|
|
37
|
+
try {
|
|
38
|
+
const { defaultModel, maxMessages } = req.body;
|
|
39
|
+
const updates = {};
|
|
40
|
+
// Validate defaultModel
|
|
41
|
+
if (defaultModel !== undefined) {
|
|
42
|
+
if (!MODEL_TYPES.includes(defaultModel)) {
|
|
43
|
+
res.status(400).json({
|
|
44
|
+
error: 'Invalid model',
|
|
45
|
+
message: `Model must be one of: ${MODEL_TYPES.join(', ')}`,
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
updates.defaultModel = defaultModel;
|
|
50
|
+
}
|
|
51
|
+
// Validate maxMessages
|
|
52
|
+
if (maxMessages !== undefined) {
|
|
53
|
+
const num = parseInt(maxMessages, 10);
|
|
54
|
+
if (isNaN(num) || num < 1 || num > 50) {
|
|
55
|
+
res.status(400).json({
|
|
56
|
+
error: 'Invalid maxMessages',
|
|
57
|
+
message: 'maxMessages must be between 1 and 50',
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
updates.maxMessages = num;
|
|
62
|
+
}
|
|
63
|
+
if (Object.keys(updates).length === 0) {
|
|
64
|
+
res.status(400).json({
|
|
65
|
+
error: 'No updates provided',
|
|
66
|
+
message: 'Provide at least one of: defaultModel, maxMessages',
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
await getConfigManager().update(updates);
|
|
71
|
+
res.json({ success: true, updated: updates });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
res.status(500).json({
|
|
75
|
+
error: 'Failed to update configuration',
|
|
76
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
export { router as configRoutes };
|
|
81
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/api/routes/config.ts"],"names":[],"mappings":"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"}
|