@mcpilotx/intentorch 0.5.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 +201 -0
- package/README.md +545 -0
- package/dist/ai/ai.d.ts +205 -0
- package/dist/ai/ai.js +1200 -0
- package/dist/ai/cloud-intent-engine.d.ts +270 -0
- package/dist/ai/cloud-intent-engine.js +956 -0
- package/dist/ai/command.d.ts +59 -0
- package/dist/ai/command.js +285 -0
- package/dist/ai/config.d.ts +66 -0
- package/dist/ai/config.js +211 -0
- package/dist/ai/enhanced-intent.d.ts +17 -0
- package/dist/ai/enhanced-intent.js +32 -0
- package/dist/ai/index.d.ts +29 -0
- package/dist/ai/index.js +44 -0
- package/dist/ai/intent.d.ts +16 -0
- package/dist/ai/intent.js +30 -0
- package/dist/core/ai-config.d.ts +25 -0
- package/dist/core/ai-config.js +326 -0
- package/dist/core/config-manager.d.ts +36 -0
- package/dist/core/config-manager.js +400 -0
- package/dist/core/config-validator.d.ts +9 -0
- package/dist/core/config-validator.js +184 -0
- package/dist/core/constants.d.ts +34 -0
- package/dist/core/constants.js +37 -0
- package/dist/core/error-ai.d.ts +23 -0
- package/dist/core/error-ai.js +217 -0
- package/dist/core/error-handler.d.ts +197 -0
- package/dist/core/error-handler.js +467 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +17 -0
- package/dist/core/logger.d.ts +27 -0
- package/dist/core/logger.js +108 -0
- package/dist/core/performance-monitor.d.ts +74 -0
- package/dist/core/performance-monitor.js +260 -0
- package/dist/core/providers.d.ts +36 -0
- package/dist/core/providers.js +304 -0
- package/dist/core/retry-manager.d.ts +41 -0
- package/dist/core/retry-manager.js +204 -0
- package/dist/core/types.d.ts +155 -0
- package/dist/core/types.js +2 -0
- package/dist/daemon/index.d.ts +10 -0
- package/dist/daemon/index.js +15 -0
- package/dist/daemon/intent-engine.d.ts +22 -0
- package/dist/daemon/intent-engine.js +50 -0
- package/dist/daemon/orchestrator.d.ts +24 -0
- package/dist/daemon/orchestrator.js +100 -0
- package/dist/daemon/pm.d.ts +33 -0
- package/dist/daemon/pm.js +127 -0
- package/dist/daemon/process.d.ts +11 -0
- package/dist/daemon/process.js +49 -0
- package/dist/daemon/server.d.ts +17 -0
- package/dist/daemon/server.js +435 -0
- package/dist/daemon/service.d.ts +36 -0
- package/dist/daemon/service.js +278 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +36 -0
- package/dist/mcp/client.d.ts +51 -0
- package/dist/mcp/client.js +276 -0
- package/dist/mcp/index.d.ts +162 -0
- package/dist/mcp/index.js +199 -0
- package/dist/mcp/tool-registry.d.ts +71 -0
- package/dist/mcp/tool-registry.js +308 -0
- package/dist/mcp/transport.d.ts +83 -0
- package/dist/mcp/transport.js +515 -0
- package/dist/mcp/types.d.ts +136 -0
- package/dist/mcp/types.js +31 -0
- package/dist/runtime/adapter-advanced.d.ts +184 -0
- package/dist/runtime/adapter-advanced.js +160 -0
- package/dist/runtime/adapter.d.ts +9 -0
- package/dist/runtime/adapter.js +2 -0
- package/dist/runtime/detector-advanced.d.ts +59 -0
- package/dist/runtime/detector-advanced.js +487 -0
- package/dist/runtime/detector.d.ts +5 -0
- package/dist/runtime/detector.js +56 -0
- package/dist/runtime/docker-adapter.d.ts +18 -0
- package/dist/runtime/docker-adapter.js +170 -0
- package/dist/runtime/docker.d.ts +17 -0
- package/dist/runtime/docker.js +71 -0
- package/dist/runtime/executable-analyzer.d.ts +56 -0
- package/dist/runtime/executable-analyzer.js +391 -0
- package/dist/runtime/go-adapter.d.ts +19 -0
- package/dist/runtime/go-adapter.js +190 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.js +10 -0
- package/dist/runtime/node-adapter.d.ts +10 -0
- package/dist/runtime/node-adapter.js +23 -0
- package/dist/runtime/node.d.ts +20 -0
- package/dist/runtime/node.js +86 -0
- package/dist/runtime/python-adapter.d.ts +11 -0
- package/dist/runtime/python-adapter.js +102 -0
- package/dist/runtime/python.d.ts +17 -0
- package/dist/runtime/python.js +72 -0
- package/dist/runtime/rust-adapter.d.ts +21 -0
- package/dist/runtime/rust-adapter.js +267 -0
- package/dist/sdk.d.ts +500 -0
- package/dist/sdk.js +904 -0
- package/docs/README.ZH_CN.md +545 -0
- package/docs/api.md +888 -0
- package/docs/architecture.md +731 -0
- package/docs/development.md +744 -0
- package/package.json +112 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { CONFIG_PATH } from '../core/constants.js';
|
|
4
|
+
import { NodeAdapter } from '../runtime/node.js';
|
|
5
|
+
import { PythonAdapter } from '../runtime/python.js';
|
|
6
|
+
import { DockerAdapter } from '../runtime/docker.js';
|
|
7
|
+
export class ProcessManager extends EventEmitter {
|
|
8
|
+
instances = new Map();
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.loadFromConfig();
|
|
12
|
+
}
|
|
13
|
+
loadFromConfig() {
|
|
14
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
18
|
+
const services = config.services?.instances || [];
|
|
19
|
+
services.forEach((s) => {
|
|
20
|
+
if (!this.instances.has(s.name)) {
|
|
21
|
+
this.instances.set(s.name, { ...s, adapter: null, status: 'stopped', tools: [] });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async startService(name) {
|
|
26
|
+
const instance = this.instances.get(name);
|
|
27
|
+
if (!instance) {
|
|
28
|
+
throw new Error(`Service ${name} not found`);
|
|
29
|
+
}
|
|
30
|
+
if (instance.status === 'running') {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
if (instance.runtime === 'node') {
|
|
35
|
+
const adapter = new NodeAdapter({ name: instance.name, cwd: instance.path });
|
|
36
|
+
await adapter.start();
|
|
37
|
+
instance.adapter = adapter;
|
|
38
|
+
}
|
|
39
|
+
else if (instance.runtime === 'python') {
|
|
40
|
+
const adapter = new PythonAdapter({ name: instance.name, cwd: instance.path });
|
|
41
|
+
await adapter.start();
|
|
42
|
+
instance.adapter = adapter;
|
|
43
|
+
}
|
|
44
|
+
else if (instance.runtime === 'docker') {
|
|
45
|
+
const adapter = new DockerAdapter({ name: instance.name, image: instance.image });
|
|
46
|
+
await adapter.start();
|
|
47
|
+
instance.adapter = adapter;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
throw new Error(`Runtime ${instance.runtime} not supported yet.`);
|
|
51
|
+
}
|
|
52
|
+
instance.status = 'running';
|
|
53
|
+
console.log(`[PM] Service ${name} (${instance.runtime}) is now running.`);
|
|
54
|
+
await this.discoverTools(name);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
instance.status = 'error';
|
|
58
|
+
instance.error = err.message;
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async discoverTools(name) {
|
|
63
|
+
const instance = this.instances.get(name);
|
|
64
|
+
if (!instance || !instance.adapter) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.log(`[PM] Discovering tools for ${name}...`);
|
|
68
|
+
try {
|
|
69
|
+
// Send tools/list request according to MCP protocol
|
|
70
|
+
const response = await instance.adapter.call('tools/list', {});
|
|
71
|
+
instance.tools = response.result?.tools || [];
|
|
72
|
+
console.log(`[PM] Discovered ${instance.tools?.length} tools from ${name}.`);
|
|
73
|
+
// Trigger event to notify Orchestrator for vector indexing
|
|
74
|
+
this.emit('tools_discovered', { service: name, tools: instance.tools });
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
console.error(`[PM] Failed to discover tools for ${name}: ${e.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async callService(name, method, params = {}) {
|
|
81
|
+
const instance = this.instances.get(name);
|
|
82
|
+
if (!instance) {
|
|
83
|
+
throw new Error(`Service ${name} not found`);
|
|
84
|
+
}
|
|
85
|
+
if (instance.status !== 'running') {
|
|
86
|
+
await this.startService(name);
|
|
87
|
+
}
|
|
88
|
+
if (!instance.adapter) {
|
|
89
|
+
throw new Error(`Adapter for ${name} not initialized.`);
|
|
90
|
+
}
|
|
91
|
+
// Use MCP protocol format for tool calls
|
|
92
|
+
return await instance.adapter.call('tools/call', {
|
|
93
|
+
name: method,
|
|
94
|
+
arguments: params,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
getStatuses() {
|
|
98
|
+
this.loadFromConfig();
|
|
99
|
+
return Array.from(this.instances.values()).map(i => ({
|
|
100
|
+
name: i.name,
|
|
101
|
+
runtime: i.runtime,
|
|
102
|
+
status: i.status,
|
|
103
|
+
error: i.error,
|
|
104
|
+
toolsCount: i.tools?.length || 0,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
getRunningServices() {
|
|
108
|
+
this.loadFromConfig();
|
|
109
|
+
return Array.from(this.instances.values())
|
|
110
|
+
.filter(i => i.status === 'running')
|
|
111
|
+
.map(i => i.name);
|
|
112
|
+
}
|
|
113
|
+
getServiceTools(serviceName) {
|
|
114
|
+
const instance = this.instances.get(serviceName);
|
|
115
|
+
return instance?.tools || [];
|
|
116
|
+
}
|
|
117
|
+
stopService(name) {
|
|
118
|
+
const instance = this.instances.get(name);
|
|
119
|
+
if (instance && instance.status === 'running') {
|
|
120
|
+
instance.adapter?.stop();
|
|
121
|
+
instance.adapter = null;
|
|
122
|
+
instance.status = 'stopped';
|
|
123
|
+
console.log(`[PM] Service ${name} stopped.`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=pm.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ServiceConfig } from '../core/types';
|
|
2
|
+
export declare class ProcessManager {
|
|
3
|
+
private processes;
|
|
4
|
+
startService(config: ServiceConfig): Promise<import("child_process").ChildProcessWithoutNullStreams>;
|
|
5
|
+
stopService(name: string): boolean;
|
|
6
|
+
getStatuses(): {
|
|
7
|
+
name: string;
|
|
8
|
+
status: string;
|
|
9
|
+
}[];
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
export class ProcessManager {
|
|
4
|
+
processes = new Map();
|
|
5
|
+
async startService(config) {
|
|
6
|
+
if (this.processes.has(config.name)) {
|
|
7
|
+
throw new Error(`Service ${config.name} already running`);
|
|
8
|
+
}
|
|
9
|
+
// Resolve entry path relative to project directory if it's a relative path
|
|
10
|
+
let entryPath = config.entry;
|
|
11
|
+
if (!entryPath.startsWith('/') && !entryPath.startsWith('.')) {
|
|
12
|
+
// If it's not an absolute path and doesn't start with ./, assume it's in PATH
|
|
13
|
+
// For test.js, we'll use the full path
|
|
14
|
+
if (entryPath === 'test.js') {
|
|
15
|
+
entryPath = join(process.cwd(), 'test.js');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const child = spawn(entryPath, config.args || [], {
|
|
19
|
+
env: { ...process.env, ...config.env },
|
|
20
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
21
|
+
});
|
|
22
|
+
this.processes.set(config.name, child);
|
|
23
|
+
child.on('exit', (code) => {
|
|
24
|
+
console.log(`Service ${config.name} exited with code ${code}`);
|
|
25
|
+
this.processes.delete(config.name);
|
|
26
|
+
});
|
|
27
|
+
child.on('error', (error) => {
|
|
28
|
+
console.error(`Service ${config.name} error:`, error.message);
|
|
29
|
+
this.processes.delete(config.name);
|
|
30
|
+
});
|
|
31
|
+
return child;
|
|
32
|
+
}
|
|
33
|
+
stopService(name) {
|
|
34
|
+
const child = this.processes.get(name);
|
|
35
|
+
if (child) {
|
|
36
|
+
child.kill();
|
|
37
|
+
this.processes.delete(name);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
getStatuses() {
|
|
43
|
+
return Array.from(this.processes.keys()).map(name => ({
|
|
44
|
+
name,
|
|
45
|
+
status: 'running',
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=process.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Orchestrator } from './orchestrator';
|
|
2
|
+
export declare class DaemonServer {
|
|
3
|
+
private server;
|
|
4
|
+
private pm;
|
|
5
|
+
private orchestrator;
|
|
6
|
+
private app;
|
|
7
|
+
constructor();
|
|
8
|
+
private setupRoutes;
|
|
9
|
+
start(): Promise<void>;
|
|
10
|
+
static testAIConnection(orchestrator: Orchestrator): Promise<any>;
|
|
11
|
+
private static testCloudProvider;
|
|
12
|
+
private static getProviderConfig;
|
|
13
|
+
private static createTestRequest;
|
|
14
|
+
private static getDefaultModel;
|
|
15
|
+
stop(): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import { logger } from '../core/logger.js';
|
|
2
|
+
import { ProcessManager } from './pm.js';
|
|
3
|
+
import { Orchestrator } from './orchestrator.js';
|
|
4
|
+
import { ConfigValidator } from '../core/config-validator.js';
|
|
5
|
+
import { Hono } from 'hono';
|
|
6
|
+
import { serve } from '@hono/node-server';
|
|
7
|
+
export class DaemonServer {
|
|
8
|
+
server;
|
|
9
|
+
pm;
|
|
10
|
+
orchestrator;
|
|
11
|
+
app;
|
|
12
|
+
constructor() {
|
|
13
|
+
this.pm = new ProcessManager();
|
|
14
|
+
this.orchestrator = new Orchestrator(this.pm);
|
|
15
|
+
this.app = new Hono();
|
|
16
|
+
this.setupRoutes();
|
|
17
|
+
}
|
|
18
|
+
setupRoutes() {
|
|
19
|
+
const pm = this.pm;
|
|
20
|
+
const orchestrator = this.orchestrator;
|
|
21
|
+
this.app.post('/', async (c) => {
|
|
22
|
+
try {
|
|
23
|
+
const body = await c.req.json();
|
|
24
|
+
const { command, data } = body;
|
|
25
|
+
logger.logRequest(command, data);
|
|
26
|
+
switch (command) {
|
|
27
|
+
case 'ping':
|
|
28
|
+
return c.json({ status: 'ok', message: 'pong' });
|
|
29
|
+
case 'status':
|
|
30
|
+
return c.json({
|
|
31
|
+
status: 'ok',
|
|
32
|
+
data: {
|
|
33
|
+
uptime: process.uptime(),
|
|
34
|
+
pid: process.pid,
|
|
35
|
+
services: pm.getStatuses(),
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
case 'start-service':
|
|
39
|
+
await pm.startService(data.name);
|
|
40
|
+
logger.logServiceEvent(data.name, 'started');
|
|
41
|
+
return c.json({ status: 'ok', message: `Service ${data.name} started.` });
|
|
42
|
+
case 'stop-service':
|
|
43
|
+
pm.stopService(data.name);
|
|
44
|
+
logger.logServiceEvent(data.name, 'stopped');
|
|
45
|
+
return c.json({ status: 'ok', message: `Service ${data.name} stopped.` });
|
|
46
|
+
case 'ai-query':
|
|
47
|
+
const result = await orchestrator.executeQuery(data.query);
|
|
48
|
+
logger.logAIQuery(data.query, result);
|
|
49
|
+
return c.json({ status: 'ok', data: result });
|
|
50
|
+
case 'get-config':
|
|
51
|
+
const config = orchestrator.getConfig();
|
|
52
|
+
return c.json({ status: 'ok', data: config });
|
|
53
|
+
case 'get-ai-config':
|
|
54
|
+
const aiConfig = orchestrator.getConfig().ai;
|
|
55
|
+
return c.json({ status: 'ok', data: aiConfig });
|
|
56
|
+
case 'update-ai-config':
|
|
57
|
+
const updateResult = orchestrator.updateAIConfig(data.config);
|
|
58
|
+
if (updateResult.success) {
|
|
59
|
+
logger.logConfigUpdate('ai', data.config);
|
|
60
|
+
return c.json({ status: 'ok', data: updateResult });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return c.json({ status: 'error', message: updateResult.error }, 400);
|
|
64
|
+
}
|
|
65
|
+
case 'test-ai-connection':
|
|
66
|
+
const testResult = await DaemonServer.testAIConnection(orchestrator);
|
|
67
|
+
return c.json(testResult);
|
|
68
|
+
case 'reset-ai-config':
|
|
69
|
+
const defaultConfig = ConfigValidator.getDefaultConfig();
|
|
70
|
+
const resetResult = orchestrator.updateAIConfig(defaultConfig.ai);
|
|
71
|
+
if (resetResult.success) {
|
|
72
|
+
logger.logConfigUpdate('ai', defaultConfig.ai);
|
|
73
|
+
return c.json({ status: 'ok', data: resetResult });
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
return c.json({ status: 'error', message: resetResult.error }, 400);
|
|
77
|
+
}
|
|
78
|
+
default:
|
|
79
|
+
logger.warn(`Unknown command received: ${command}`);
|
|
80
|
+
return c.json({ status: 'error', message: `Unknown command: ${command}` }, 404);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
logger.error(`Error processing request: ${err.message}`, { stack: err.stack });
|
|
85
|
+
return c.json({ status: 'error', message: err.message }, 500);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async start() {
|
|
90
|
+
const PORT = 8082;
|
|
91
|
+
logger.info(`Starting MCPilot Daemon on http://localhost:${PORT}...`);
|
|
92
|
+
this.server = serve({
|
|
93
|
+
fetch: this.app.fetch,
|
|
94
|
+
port: PORT,
|
|
95
|
+
});
|
|
96
|
+
logger.info('Daemon is now running.');
|
|
97
|
+
}
|
|
98
|
+
// Test AI connection
|
|
99
|
+
static async testAIConnection(orchestrator) {
|
|
100
|
+
try {
|
|
101
|
+
const config = orchestrator.getConfig();
|
|
102
|
+
const aiConfig = config.ai;
|
|
103
|
+
if (!aiConfig || !aiConfig.provider) {
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
error: 'AI configuration not set. Use "mcp ai use <provider>" to configure AI.',
|
|
107
|
+
data: null,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
logger.info(`[AI] Testing connection to provider: ${aiConfig.provider}`);
|
|
111
|
+
// Test based on different providers
|
|
112
|
+
switch (aiConfig.provider) {
|
|
113
|
+
case 'openai':
|
|
114
|
+
case 'deepseek':
|
|
115
|
+
case 'anthropic':
|
|
116
|
+
case 'cohere':
|
|
117
|
+
// These providers require API key
|
|
118
|
+
if (!aiConfig.apiKey || aiConfig.apiKey.trim() === '') {
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
error: `Missing API key for ${aiConfig.provider}. Get an API key from the provider's website and use "mcp cfg set ai.apiKey=your-api-key" to set it.`,
|
|
122
|
+
data: {
|
|
123
|
+
provider: aiConfig.provider,
|
|
124
|
+
suggestion: `Get API key from: ${aiConfig.provider === 'deepseek' ? 'https://platform.deepseek.com/api_keys' :
|
|
125
|
+
aiConfig.provider === 'openai' ? 'https://platform.openai.com/api-keys' :
|
|
126
|
+
aiConfig.provider === 'anthropic' ? 'https://console.anthropic.com/account/keys' :
|
|
127
|
+
'https://dashboard.cohere.com/api-keys'}`,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// Direct API test for cloud providers
|
|
132
|
+
try {
|
|
133
|
+
const testResult = await DaemonServer.testCloudProvider(aiConfig.provider, aiConfig.apiKey, aiConfig.model || this.getDefaultModel(aiConfig.provider));
|
|
134
|
+
if (testResult.success) {
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
data: {
|
|
138
|
+
provider: aiConfig.provider,
|
|
139
|
+
model: aiConfig.model,
|
|
140
|
+
status: 'connected',
|
|
141
|
+
testResult: 'API connection successful',
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
error: `API connection failed: ${testResult.error}`,
|
|
149
|
+
data: {
|
|
150
|
+
provider: aiConfig.provider,
|
|
151
|
+
details: testResult.data,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
error: `Connection test failed: ${error.message}`,
|
|
160
|
+
data: { provider: aiConfig.provider },
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
case 'ollama':
|
|
164
|
+
// Check if Ollama service is available
|
|
165
|
+
const host = aiConfig.ollamaHost || 'http://localhost:11434';
|
|
166
|
+
try {
|
|
167
|
+
// Use AbortController for timeout
|
|
168
|
+
const controller = new AbortController();
|
|
169
|
+
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
|
170
|
+
const response = await fetch(`${host}/api/tags`, {
|
|
171
|
+
method: 'GET',
|
|
172
|
+
signal: controller.signal,
|
|
173
|
+
});
|
|
174
|
+
clearTimeout(timeoutId);
|
|
175
|
+
if (response.ok) {
|
|
176
|
+
const data = await response.json();
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
data: {
|
|
180
|
+
provider: aiConfig.provider,
|
|
181
|
+
host: host,
|
|
182
|
+
status: 'connected',
|
|
183
|
+
models: data.models || [],
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
return {
|
|
189
|
+
success: false,
|
|
190
|
+
error: `Ollama service response error: ${response.status}`,
|
|
191
|
+
data: { host: host },
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
error: `Cannot connect to Ollama service: ${error.message}`,
|
|
199
|
+
data: { host: host },
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
case 'local':
|
|
203
|
+
case 'custom':
|
|
204
|
+
// For local and custom providers, try to connect
|
|
205
|
+
const endpoint = aiConfig.apiEndpoint || 'http://localhost:8080/v1/chat/completions';
|
|
206
|
+
try {
|
|
207
|
+
// Use AbortController for timeout
|
|
208
|
+
const controller = new AbortController();
|
|
209
|
+
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
|
210
|
+
const response = await fetch(endpoint, {
|
|
211
|
+
method: 'GET',
|
|
212
|
+
signal: controller.signal,
|
|
213
|
+
});
|
|
214
|
+
clearTimeout(timeoutId);
|
|
215
|
+
if (response.ok || response.status === 404 || response.status === 405) {
|
|
216
|
+
// 404/405 may indicate endpoint exists but doesn't support GET, which is acceptable
|
|
217
|
+
return {
|
|
218
|
+
success: true,
|
|
219
|
+
data: {
|
|
220
|
+
provider: aiConfig.provider,
|
|
221
|
+
endpoint: endpoint,
|
|
222
|
+
status: 'reachable',
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
return {
|
|
228
|
+
success: false,
|
|
229
|
+
error: `Endpoint response error: ${response.status}`,
|
|
230
|
+
data: { endpoint: endpoint },
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
return {
|
|
236
|
+
success: false,
|
|
237
|
+
error: `Cannot connect to endpoint: ${error.message}`,
|
|
238
|
+
data: { endpoint: endpoint },
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
default:
|
|
242
|
+
return {
|
|
243
|
+
success: false,
|
|
244
|
+
error: `Unsupported provider: ${aiConfig.provider}`,
|
|
245
|
+
data: { provider: aiConfig.provider },
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
logger.error(`AI connection test failed: ${error.message}`);
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
error: `Error during testing: ${error.message}`,
|
|
254
|
+
data: null,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Test cloud provider API connection
|
|
259
|
+
static async testCloudProvider(provider, apiKey, model) {
|
|
260
|
+
try {
|
|
261
|
+
// Get endpoint and headers based on provider
|
|
262
|
+
const { endpoint, headers } = this.getProviderConfig(provider, apiKey, model);
|
|
263
|
+
// Create a simple test request
|
|
264
|
+
const testBody = this.createTestRequest(provider, model);
|
|
265
|
+
// Use AbortController for timeout
|
|
266
|
+
const controller = new AbortController();
|
|
267
|
+
const timeoutId = setTimeout(() => controller.abort(), 15000);
|
|
268
|
+
logger.info(`[AI] Testing ${provider} API at ${endpoint}`);
|
|
269
|
+
const response = await fetch(endpoint, {
|
|
270
|
+
method: 'POST',
|
|
271
|
+
headers: headers,
|
|
272
|
+
body: JSON.stringify(testBody),
|
|
273
|
+
signal: controller.signal,
|
|
274
|
+
});
|
|
275
|
+
clearTimeout(timeoutId);
|
|
276
|
+
if (response.ok) {
|
|
277
|
+
const data = await response.json();
|
|
278
|
+
return {
|
|
279
|
+
success: true,
|
|
280
|
+
data: {
|
|
281
|
+
provider: provider,
|
|
282
|
+
model: model,
|
|
283
|
+
responseTime: 'ok',
|
|
284
|
+
details: 'API responded successfully',
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
const errorText = await response.text();
|
|
290
|
+
let errorMessage = `API returned ${response.status}`;
|
|
291
|
+
// Parse error message if possible
|
|
292
|
+
try {
|
|
293
|
+
const errorData = JSON.parse(errorText);
|
|
294
|
+
if (errorData.error?.message) {
|
|
295
|
+
errorMessage = errorData.error.message;
|
|
296
|
+
}
|
|
297
|
+
else if (errorData.message) {
|
|
298
|
+
errorMessage = errorData.message;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// If not JSON, use raw text
|
|
303
|
+
if (errorText && errorText.length < 200) {
|
|
304
|
+
errorMessage = `${errorMessage}: ${errorText}`;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
success: false,
|
|
309
|
+
error: errorMessage,
|
|
310
|
+
data: {
|
|
311
|
+
status: response.status,
|
|
312
|
+
provider: provider,
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
logger.error(`[AI] ${provider} API test failed: ${error.message}`);
|
|
319
|
+
let errorMessage = error.message;
|
|
320
|
+
if (error.name === 'AbortError') {
|
|
321
|
+
errorMessage = 'Request timeout (15 seconds)';
|
|
322
|
+
}
|
|
323
|
+
else if (error.message.includes('fetch failed')) {
|
|
324
|
+
errorMessage = 'Network error - check internet connection';
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
success: false,
|
|
328
|
+
error: errorMessage,
|
|
329
|
+
data: {
|
|
330
|
+
provider: provider,
|
|
331
|
+
errorType: error.name,
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Get provider-specific configuration
|
|
337
|
+
static getProviderConfig(provider, apiKey, model) {
|
|
338
|
+
const headers = {
|
|
339
|
+
'Content-Type': 'application/json',
|
|
340
|
+
};
|
|
341
|
+
let endpoint = '';
|
|
342
|
+
switch (provider) {
|
|
343
|
+
case 'openai':
|
|
344
|
+
endpoint = 'https://api.openai.com/v1/chat/completions';
|
|
345
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
346
|
+
break;
|
|
347
|
+
case 'deepseek':
|
|
348
|
+
endpoint = 'https://api.deepseek.com/v1/chat/completions';
|
|
349
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
350
|
+
break;
|
|
351
|
+
case 'anthropic':
|
|
352
|
+
endpoint = 'https://api.anthropic.com/v1/messages';
|
|
353
|
+
headers['x-api-key'] = apiKey;
|
|
354
|
+
headers['anthropic-version'] = '2023-06-01';
|
|
355
|
+
break;
|
|
356
|
+
case 'cohere':
|
|
357
|
+
endpoint = 'https://api.cohere.ai/v1/generate';
|
|
358
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
359
|
+
break;
|
|
360
|
+
default:
|
|
361
|
+
throw new Error(`Unsupported provider for direct API test: ${provider}`);
|
|
362
|
+
}
|
|
363
|
+
return { endpoint, headers };
|
|
364
|
+
}
|
|
365
|
+
// Create test request for different providers
|
|
366
|
+
static createTestRequest(provider, model) {
|
|
367
|
+
const defaultModel = this.getDefaultModel(provider);
|
|
368
|
+
const testModel = model || defaultModel;
|
|
369
|
+
switch (provider) {
|
|
370
|
+
case 'openai':
|
|
371
|
+
case 'deepseek':
|
|
372
|
+
return {
|
|
373
|
+
model: testModel,
|
|
374
|
+
messages: [
|
|
375
|
+
{
|
|
376
|
+
role: 'system',
|
|
377
|
+
content: 'You are a helpful assistant. Respond with "OK" to confirm the connection.',
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
role: 'user',
|
|
381
|
+
content: 'Test connection - please respond with "OK"',
|
|
382
|
+
},
|
|
383
|
+
],
|
|
384
|
+
max_tokens: 10,
|
|
385
|
+
temperature: 0.1,
|
|
386
|
+
};
|
|
387
|
+
case 'anthropic':
|
|
388
|
+
return {
|
|
389
|
+
model: testModel,
|
|
390
|
+
max_tokens: 10,
|
|
391
|
+
messages: [
|
|
392
|
+
{
|
|
393
|
+
role: 'user',
|
|
394
|
+
content: 'Test connection - please respond with "OK"',
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
};
|
|
398
|
+
case 'cohere':
|
|
399
|
+
return {
|
|
400
|
+
model: testModel,
|
|
401
|
+
prompt: 'Test connection - please respond with "OK"',
|
|
402
|
+
max_tokens: 10,
|
|
403
|
+
temperature: 0.1,
|
|
404
|
+
};
|
|
405
|
+
default:
|
|
406
|
+
throw new Error(`Unsupported provider for test request: ${provider}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Get default model for provider
|
|
410
|
+
static getDefaultModel(provider) {
|
|
411
|
+
switch (provider) {
|
|
412
|
+
case 'openai':
|
|
413
|
+
return 'gpt-3.5-turbo';
|
|
414
|
+
case 'deepseek':
|
|
415
|
+
return 'deepseek-chat';
|
|
416
|
+
case 'anthropic':
|
|
417
|
+
return 'claude-3-haiku-20240307';
|
|
418
|
+
case 'cohere':
|
|
419
|
+
return 'command';
|
|
420
|
+
default:
|
|
421
|
+
return 'default';
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
stop() {
|
|
425
|
+
if (this.server) {
|
|
426
|
+
this.server.stop();
|
|
427
|
+
logger.info('Daemon stopped.');
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (process.argv.includes('--daemon')) {
|
|
432
|
+
const server = new DaemonServer();
|
|
433
|
+
server.start();
|
|
434
|
+
}
|
|
435
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ServiceConfig } from '../core/types';
|
|
2
|
+
export interface ServiceInfo {
|
|
3
|
+
name: string;
|
|
4
|
+
path: string;
|
|
5
|
+
runtime: string;
|
|
6
|
+
status: 'installed' | 'running' | 'stopped' | 'error';
|
|
7
|
+
pid?: number;
|
|
8
|
+
port?: number;
|
|
9
|
+
installedAt: string;
|
|
10
|
+
startedAt?: string;
|
|
11
|
+
stoppedAt?: string;
|
|
12
|
+
config: ServiceConfig;
|
|
13
|
+
}
|
|
14
|
+
export declare class ServiceManager {
|
|
15
|
+
private services;
|
|
16
|
+
private processes;
|
|
17
|
+
constructor();
|
|
18
|
+
private loadServices;
|
|
19
|
+
private saveServices;
|
|
20
|
+
installService(servicePath: string, name?: string): Promise<ServiceInfo>;
|
|
21
|
+
startService(name: string): Promise<ServiceInfo>;
|
|
22
|
+
stopService(name: string): Promise<ServiceInfo>;
|
|
23
|
+
restartService(name: string): Promise<ServiceInfo>;
|
|
24
|
+
uninstallService(name: string): Promise<void>;
|
|
25
|
+
getService(name: string): ServiceInfo | undefined;
|
|
26
|
+
getAllServices(): ServiceInfo[];
|
|
27
|
+
getRunningServices(): ServiceInfo[];
|
|
28
|
+
getServiceLogs(name: string, lines?: number): Promise<string>;
|
|
29
|
+
getServiceStats(name: string): Promise<any>;
|
|
30
|
+
private spawnService;
|
|
31
|
+
private detectEntryPoint;
|
|
32
|
+
private getProcessMemoryUsage;
|
|
33
|
+
healthCheck(name: string): Promise<boolean>;
|
|
34
|
+
cleanup(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=service.d.ts.map
|