mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-02-03 21:40:53 +00:00
Removes outdated prompt files
Removes the `Chat Prompt.txt`, `VSCode Agent/Prompt.txt`, `Warp.dev/Prompt.txt`, and `v0 Prompts and Tools/Prompt.txt` files. These files likely contain outdated prompts or configurations that are no longer needed in the current project. Removing them helps to clean up the codebase and prevent potential confusion or conflicts.
This commit is contained in:
60
Nowhere_AI_Agent/backend/.gitignore
vendored
Normal file
60
Nowhere_AI_Agent/backend/.gitignore
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids/
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
21
Nowhere_AI_Agent/backend/env.example
Normal file
21
Nowhere_AI_Agent/backend/env.example
Normal file
@@ -0,0 +1,21 @@
|
||||
# AI Models
|
||||
OPENAI_API_KEY=your_openai_api_key_here
|
||||
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
||||
|
||||
# Database (Optional - for full features)
|
||||
REDIS_URL=redis://localhost:6379
|
||||
POSTGRES_URL=postgresql://username:password@localhost:5432/nowhere_db
|
||||
|
||||
# Security
|
||||
JWT_SECRET=your_jwt_secret_here
|
||||
RATE_LIMIT_WINDOW=900000
|
||||
RATE_LIMIT_MAX_REQUESTS=100
|
||||
|
||||
# Voice (Optional)
|
||||
AZURE_SPEECH_KEY=your_azure_speech_key_here
|
||||
AZURE_SPEECH_REGION=your_azure_region_here
|
||||
|
||||
# Server Configuration
|
||||
PORT=3001
|
||||
NODE_ENV=development
|
||||
LOG_LEVEL=info
|
||||
7858
Nowhere_AI_Agent/backend/package-lock.json
generated
Normal file
7858
Nowhere_AI_Agent/backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
65
Nowhere_AI_Agent/backend/package.json
Normal file
65
Nowhere_AI_Agent/backend/package.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "nowhere-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "Advanced Nowhere AI Agent Backend with TypeScript",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "ts-node src/index.ts",
|
||||
"dev:watch": "nodemon --exec ts-node src/index.ts",
|
||||
"test": "jest",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"format": "prettier --write src/**/*.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"cors": "^2.8.5",
|
||||
"helmet": "^7.1.0",
|
||||
"compression": "^1.7.4",
|
||||
"dotenv": "^16.3.1",
|
||||
"socket.io": "^4.7.4",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"winston": "^3.11.0",
|
||||
"express-rate-limit": "^7.1.5",
|
||||
"redis": "^4.6.10",
|
||||
"pg": "^8.11.3",
|
||||
"openai": "^4.20.1",
|
||||
"@anthropic-ai/sdk": "^0.9.1",
|
||||
"axios": "^1.6.2",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/compression": "^1.7.5",
|
||||
"@types/jsonwebtoken": "^9.0.5",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/multer": "^1.4.11",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@types/pg": "^8.10.9",
|
||||
"typescript": "^5.3.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"nodemon": "^3.0.1",
|
||||
"eslint": "^8.55.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
||||
"@typescript-eslint/parser": "^6.13.1",
|
||||
"prettier": "^3.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"@types/jest": "^29.5.8"
|
||||
},
|
||||
"keywords": [
|
||||
"ai",
|
||||
"coding-assistant",
|
||||
"voice-integration",
|
||||
"autopilot",
|
||||
"nowhere",
|
||||
"typescript"
|
||||
],
|
||||
"author": "Nowhere Team",
|
||||
"license": "MIT"
|
||||
}
|
||||
473
Nowhere_AI_Agent/backend/server.js
Normal file
473
Nowhere_AI_Agent/backend/server.js
Normal file
@@ -0,0 +1,473 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const helmet = require('helmet');
|
||||
const compression = require('compression');
|
||||
const dotenv = require('dotenv');
|
||||
const { createServer } = require('http');
|
||||
const { Server: SocketIOServer } = require('socket.io');
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
const server = createServer(app);
|
||||
const io = new SocketIOServer(server, {
|
||||
cors: {
|
||||
origin: process.env.FRONTEND_URL || "http://localhost:3000",
|
||||
methods: ["GET", "POST"]
|
||||
}
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
// Middleware
|
||||
app.use(helmet());
|
||||
app.use(compression());
|
||||
app.use(cors({
|
||||
origin: process.env.FRONTEND_URL || "http://localhost:3000",
|
||||
credentials: true
|
||||
}));
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
||||
|
||||
// Simple logging
|
||||
const log = (level, message, meta = {}) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
console.log(`[${timestamp}] [${level.toUpperCase()}] ${message}`, meta);
|
||||
};
|
||||
|
||||
// Mock AI Core
|
||||
class NowhereCore {
|
||||
constructor() {
|
||||
this.isAutopilotEnabled = false;
|
||||
this.memory = new Map();
|
||||
}
|
||||
|
||||
async processCommand(request) {
|
||||
try {
|
||||
log('info', 'Processing command', {
|
||||
command: request.command?.substring(0, 100),
|
||||
userId: request.userId
|
||||
});
|
||||
|
||||
// Simple command parsing
|
||||
const command = request.command.toLowerCase();
|
||||
let response = { success: true, message: '', data: {} };
|
||||
|
||||
if (command.includes('autopilot')) {
|
||||
this.isAutopilotEnabled = !this.isAutopilotEnabled;
|
||||
response.message = `Autopilot mode ${this.isAutopilotEnabled ? 'enabled' : 'disabled'}`;
|
||||
response.data.autopilot = this.isAutopilotEnabled;
|
||||
} else if (command.includes('analyze') || command.includes('code')) {
|
||||
response.message = 'Code analysis completed. Found 3 potential improvements.';
|
||||
response.data.analysis = {
|
||||
complexity: 5,
|
||||
lines: 150,
|
||||
issues: ['Consider extracting this function', 'Add error handling', 'Optimize imports']
|
||||
};
|
||||
} else if (command.includes('search') || command.includes('find')) {
|
||||
response.message = 'Search completed. Found relevant documentation and examples.';
|
||||
response.data.results = [
|
||||
{ title: 'Search Results', url: 'https://example.com', snippet: 'Relevant information found.' }
|
||||
];
|
||||
} else if (command.includes('create') || command.includes('new')) {
|
||||
response.message = 'File created successfully.';
|
||||
response.data.file = 'new-component.js';
|
||||
} else if (command.includes('run') || command.includes('execute')) {
|
||||
response.message = 'Command executed successfully.';
|
||||
response.data.output = 'Command completed with exit code 0';
|
||||
} else {
|
||||
response.message = `I understand you want to ${command}. Let me help you with that.`;
|
||||
}
|
||||
|
||||
// Update memory
|
||||
this.memory.set(request.userId || 'default', {
|
||||
lastCommand: request.command,
|
||||
lastResult: response,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
return response;
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'Error processing command', { error: error.message });
|
||||
return {
|
||||
success: false,
|
||||
message: 'Failed to process command',
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async toggleAutopilot(enabled) {
|
||||
this.isAutopilotEnabled = enabled;
|
||||
log('info', 'Autopilot mode toggled', { enabled });
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Autopilot mode ${enabled ? 'enabled' : 'disabled'}`,
|
||||
data: { autopilot: enabled }
|
||||
};
|
||||
}
|
||||
|
||||
async getStatus() {
|
||||
return {
|
||||
autopilot: this.isAutopilotEnabled,
|
||||
memory: { size: this.memory.size },
|
||||
tools: { status: 'operational' },
|
||||
voice: { status: 'available' }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const nowhere = new NowhereCore();
|
||||
|
||||
// API Routes
|
||||
app.post('/api/v1/command', async (req, res) => {
|
||||
try {
|
||||
const { command, userId, context, autopilot } = req.body;
|
||||
|
||||
if (!command) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Command is required'
|
||||
});
|
||||
}
|
||||
|
||||
log('info', 'Processing command request', {
|
||||
command: command.substring(0, 100),
|
||||
userId,
|
||||
autopilot
|
||||
});
|
||||
|
||||
const request = {
|
||||
command,
|
||||
userId: userId || 'default',
|
||||
context,
|
||||
autopilot: autopilot || false
|
||||
};
|
||||
|
||||
const response = await nowhere.processCommand(request);
|
||||
res.json(response);
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'Command processing error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Internal server error',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/v1/voice', async (req, res) => {
|
||||
try {
|
||||
const { audioData, userId, context } = req.body;
|
||||
|
||||
if (!audioData) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Audio data is required'
|
||||
});
|
||||
}
|
||||
|
||||
log('info', 'Processing voice request', {
|
||||
audioSize: audioData.length,
|
||||
userId
|
||||
});
|
||||
|
||||
// Mock voice processing
|
||||
const mockVoiceCommands = [
|
||||
'Nowhere, analyze this code',
|
||||
'Create a new React component',
|
||||
'Search for documentation',
|
||||
'Enable autopilot mode',
|
||||
'What do you remember from our conversation?',
|
||||
'Run the tests and show me the results'
|
||||
];
|
||||
|
||||
const voiceCommand = mockVoiceCommands[Math.floor(Math.random() * mockVoiceCommands.length)];
|
||||
|
||||
const request = {
|
||||
command: voiceCommand,
|
||||
userId: userId || 'default',
|
||||
context,
|
||||
voice: true
|
||||
};
|
||||
|
||||
const response = await nowhere.processCommand(request);
|
||||
|
||||
res.json({
|
||||
...response,
|
||||
voiceCommand
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'Voice processing error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Voice processing failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/v1/autopilot', async (req, res) => {
|
||||
try {
|
||||
const { enabled, userId } = req.body;
|
||||
|
||||
log('info', 'Toggling autopilot mode', { enabled, userId });
|
||||
|
||||
const response = await nowhere.toggleAutopilot(enabled);
|
||||
res.json(response);
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'Autopilot toggle error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to toggle autopilot mode',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/v1/memory/:userId', async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
const { query } = req.query;
|
||||
|
||||
log('info', 'Getting user memory', { userId, query });
|
||||
|
||||
const userMemory = nowhere.memory.get(userId) || {
|
||||
userId,
|
||||
preferences: { voiceEnabled: true, autopilotEnabled: false },
|
||||
recentCommands: ['analyze this code', 'create a new component'],
|
||||
projectContext: { currentProject: 'nowhere-ai-agent' },
|
||||
learningHistory: [],
|
||||
lastInteraction: new Date().toISOString()
|
||||
};
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: userMemory
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'Memory retrieval error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to retrieve memory',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/v1/status', async (req, res) => {
|
||||
try {
|
||||
log('info', 'Getting system status');
|
||||
|
||||
const status = await nowhere.getStatus();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: status
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'Status retrieval error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to get system status',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime(),
|
||||
memory: process.memoryUsage(),
|
||||
version: process.env.npm_package_version || '1.0.0'
|
||||
});
|
||||
});
|
||||
|
||||
// WebSocket handling
|
||||
io.on('connection', (socket) => {
|
||||
log('info', 'Client connected', {
|
||||
id: socket.id,
|
||||
ip: socket.handshake.address
|
||||
});
|
||||
|
||||
// Send welcome message
|
||||
socket.emit('welcome', {
|
||||
message: 'Welcome to Nowhere AI Agent!',
|
||||
timestamp: new Date().toISOString(),
|
||||
features: [
|
||||
'Voice Commands',
|
||||
'Autopilot Mode',
|
||||
'Memory System',
|
||||
'Real-time Communication'
|
||||
]
|
||||
});
|
||||
|
||||
// Handle text commands
|
||||
socket.on('command', async (data) => {
|
||||
try {
|
||||
log('info', 'Processing WebSocket command', {
|
||||
socketId: socket.id,
|
||||
command: data.command?.substring(0, 100)
|
||||
});
|
||||
|
||||
const request = {
|
||||
command: data.command,
|
||||
userId: data.userId || socket.id,
|
||||
context: data.context,
|
||||
autopilot: data.autopilot || false
|
||||
};
|
||||
|
||||
const response = await nowhere.processCommand(request);
|
||||
socket.emit('command_response', response);
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'WebSocket command error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Command processing failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle autopilot toggle
|
||||
socket.on('toggle_autopilot', async (data) => {
|
||||
try {
|
||||
log('info', 'Toggling autopilot via WebSocket', {
|
||||
socketId: socket.id,
|
||||
enabled: data.enabled
|
||||
});
|
||||
|
||||
const response = await nowhere.toggleAutopilot(data.enabled);
|
||||
socket.emit('autopilot_response', response);
|
||||
|
||||
// Broadcast to all clients
|
||||
io.emit('autopilot_status', {
|
||||
enabled: data.enabled,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'WebSocket autopilot toggle error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Autopilot toggle failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle status requests
|
||||
socket.on('get_status', async () => {
|
||||
try {
|
||||
log('info', 'Getting status via WebSocket', { socketId: socket.id });
|
||||
|
||||
const status = await nowhere.getStatus();
|
||||
|
||||
socket.emit('status_response', {
|
||||
success: true,
|
||||
data: status
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log('error', 'WebSocket status error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Status retrieval failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle disconnection
|
||||
socket.on('disconnect', (reason) => {
|
||||
log('info', 'Client disconnected', {
|
||||
id: socket.id,
|
||||
reason
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Start server
|
||||
server.listen(PORT, () => {
|
||||
log('info', '🚀 Nowhere AI Agent Server Started', {
|
||||
port: PORT,
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Log startup information
|
||||
console.log(`
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ 🚀 Nowhere AI Agent ║
|
||||
║ ║
|
||||
║ 🌐 Server running on: http://localhost:${PORT} ║
|
||||
║ 📡 WebSocket available at: ws://localhost:${PORT} ║
|
||||
║ 🔧 Environment: ${process.env.NODE_ENV || 'development'} ║
|
||||
║ 📊 Health check: http://localhost:${PORT}/health ║
|
||||
║ ║
|
||||
║ 🎤 Voice Integration: Available ║
|
||||
║ 🧠 Memory System: In-Memory ║
|
||||
║ 🤖 Autopilot Mode: Available ║
|
||||
║ ║
|
||||
║ 📋 Available Endpoints: ║
|
||||
║ • POST /api/v1/command - Process text commands ║
|
||||
║ • POST /api/v1/voice - Process voice commands ║
|
||||
║ • POST /api/v1/autopilot - Toggle autopilot mode ║
|
||||
║ • GET /api/v1/memory/:userId - Get user memory ║
|
||||
║ • GET /api/v1/status - Get system status ║
|
||||
║ ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
`);
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGTERM', () => {
|
||||
log('info', 'SIGTERM received, shutting down gracefully');
|
||||
server.close(() => {
|
||||
log('info', 'Server closed');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
log('info', 'SIGINT received, shutting down gracefully');
|
||||
server.close(() => {
|
||||
log('info', 'Server closed');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle uncaught exceptions
|
||||
process.on('uncaughtException', (error) => {
|
||||
log('error', 'Uncaught Exception', { error: error.message, stack: error.stack });
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
log('error', 'Unhandled Rejection', { reason, promise });
|
||||
process.exit(1);
|
||||
});
|
||||
250
Nowhere_AI_Agent/backend/setup.js
Normal file
250
Nowhere_AI_Agent/backend/setup.js
Normal file
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
console.log('🚀 Setting up Nowhere AI Agent Backend...\n');
|
||||
|
||||
// Create comprehensive package.json
|
||||
const packageJson = {
|
||||
"name": "nowhere-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "Advanced Nowhere AI Agent Backend with TypeScript",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "ts-node src/index.ts",
|
||||
"dev:watch": "nodemon --exec ts-node src/index.ts",
|
||||
"test": "jest",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"format": "prettier --write src/**/*.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"cors": "^2.8.5",
|
||||
"helmet": "^7.1.0",
|
||||
"compression": "^1.7.4",
|
||||
"dotenv": "^16.3.1",
|
||||
"socket.io": "^4.7.4",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"winston": "^3.11.0",
|
||||
"rate-limiter-flexible": "^3.0.8",
|
||||
"redis": "^4.6.10",
|
||||
"pg": "^8.11.3",
|
||||
"openai": "^4.20.1",
|
||||
"@anthropic-ai/sdk": "^0.9.1",
|
||||
"axios": "^1.6.2",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/compression": "^1.7.5",
|
||||
"@types/jsonwebtoken": "^9.0.5",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/multer": "^1.4.11",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@types/pg": "^8.10.9",
|
||||
"typescript": "^5.3.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"nodemon": "^3.0.1",
|
||||
"eslint": "^8.55.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
||||
"@typescript-eslint/parser": "^6.13.1",
|
||||
"prettier": "^3.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"@types/jest": "^29.5.8"
|
||||
},
|
||||
"keywords": [
|
||||
"ai",
|
||||
"coding-assistant",
|
||||
"voice-integration",
|
||||
"autopilot",
|
||||
"nowhere",
|
||||
"typescript"
|
||||
],
|
||||
"author": "Nowhere Team",
|
||||
"license": "MIT"
|
||||
};
|
||||
|
||||
// Create TypeScript config
|
||||
const tsConfig = {
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2020"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
};
|
||||
|
||||
// Create directory structure
|
||||
const directories = [
|
||||
'src',
|
||||
'src/core',
|
||||
'src/memory',
|
||||
'src/tools',
|
||||
'src/voice',
|
||||
'src/routes',
|
||||
'src/middleware',
|
||||
'src/utils',
|
||||
'logs',
|
||||
'dist'
|
||||
];
|
||||
|
||||
console.log('📁 Creating directory structure...');
|
||||
directories.forEach(dir => {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
console.log(` ✅ Created: ${dir}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Write package.json
|
||||
console.log('\n📦 Creating package.json...');
|
||||
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
|
||||
console.log(' ✅ Created: package.json');
|
||||
|
||||
// Write tsconfig.json
|
||||
console.log('\n⚙️ Creating TypeScript configuration...');
|
||||
fs.writeFileSync('tsconfig.json', JSON.stringify(tsConfig, null, 2));
|
||||
console.log(' ✅ Created: tsconfig.json');
|
||||
|
||||
// Create .env.example
|
||||
const envExample = `# AI Models
|
||||
OPENAI_API_KEY=your_openai_api_key_here
|
||||
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
||||
|
||||
# Database (Optional - for full features)
|
||||
REDIS_URL=redis://localhost:6379
|
||||
POSTGRES_URL=postgresql://username:password@localhost:5432/nowhere_db
|
||||
|
||||
# Security
|
||||
JWT_SECRET=your_jwt_secret_here
|
||||
RATE_LIMIT_WINDOW=900000
|
||||
RATE_LIMIT_MAX_REQUESTS=100
|
||||
|
||||
# Voice (Optional)
|
||||
AZURE_SPEECH_KEY=your_azure_speech_key_here
|
||||
AZURE_SPEECH_REGION=your_azure_region_here
|
||||
|
||||
# Server Configuration
|
||||
PORT=3001
|
||||
NODE_ENV=development
|
||||
LOG_LEVEL=info
|
||||
`;
|
||||
|
||||
console.log('\n🔧 Creating environment template...');
|
||||
fs.writeFileSync('env.example', envExample);
|
||||
console.log(' ✅ Created: env.example');
|
||||
|
||||
// Create .gitignore
|
||||
const gitignore = `# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids/
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
`;
|
||||
|
||||
console.log('\n🚫 Creating .gitignore...');
|
||||
fs.writeFileSync('.gitignore', gitignore);
|
||||
console.log(' ✅ Created: .gitignore');
|
||||
|
||||
console.log('\n📦 Installing dependencies...');
|
||||
try {
|
||||
execSync('npm install', { stdio: 'inherit' });
|
||||
console.log(' ✅ Dependencies installed successfully');
|
||||
} catch (error) {
|
||||
console.log(' ⚠️ npm install failed, but setup is complete');
|
||||
console.log(' 💡 You can run "npm install" manually later');
|
||||
}
|
||||
|
||||
console.log('\n🎉 Setup complete!');
|
||||
console.log('\n📋 Next steps:');
|
||||
console.log(' 1. Copy env.example to .env and add your API keys');
|
||||
console.log(' 2. Run: npm run build');
|
||||
console.log(' 3. Run: npm run dev');
|
||||
console.log(' 4. Open frontend/index.html in your browser');
|
||||
console.log('\n🚀 Nowhere AI Agent is ready to launch!');
|
||||
218
Nowhere_AI_Agent/backend/src/core/nowhere.ts
Normal file
218
Nowhere_AI_Agent/backend/src/core/nowhere.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import { Logger } from '../utils/logger';
|
||||
import { MemoryManager } from '../memory/memory-manager';
|
||||
import { ToolExecutor } from '../tools/tool-executor';
|
||||
import { VoiceProcessor } from '../voice/voice-processor';
|
||||
|
||||
export interface AIResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data?: any;
|
||||
error?: string;
|
||||
memory?: any;
|
||||
autopilot?: boolean;
|
||||
}
|
||||
|
||||
export interface CommandRequest {
|
||||
command: string;
|
||||
userId?: string;
|
||||
context?: any;
|
||||
voice?: boolean;
|
||||
autopilot?: boolean;
|
||||
}
|
||||
|
||||
export class NowhereCore {
|
||||
private logger: Logger;
|
||||
private memory: MemoryManager;
|
||||
private tools: ToolExecutor;
|
||||
private voice: VoiceProcessor;
|
||||
private isAutopilotEnabled: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.logger = new Logger('NowhereCore');
|
||||
this.memory = new MemoryManager();
|
||||
this.tools = new ToolExecutor();
|
||||
this.voice = new VoiceProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a command from the user
|
||||
*/
|
||||
async processCommand(request: CommandRequest): Promise<AIResponse> {
|
||||
try {
|
||||
this.logger.info('Processing command', { command: request.command, userId: request.userId });
|
||||
|
||||
// Load user context and memory
|
||||
const userContext = await this.memory.getUserContext(request.userId);
|
||||
|
||||
// Parse and understand the command
|
||||
const parsedCommand = await this.parseCommand(request.command, userContext);
|
||||
|
||||
// Execute the command
|
||||
const result = await this.executeCommand(parsedCommand, request);
|
||||
|
||||
// Update memory with the interaction
|
||||
await this.memory.updateUserContext(request.userId, {
|
||||
lastCommand: request.command,
|
||||
lastResult: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: result.message,
|
||||
data: result.data,
|
||||
memory: userContext
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Error processing command', { error: error.message });
|
||||
return {
|
||||
success: false,
|
||||
message: 'Failed to process command',
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and understand the user's command
|
||||
*/
|
||||
private async parseCommand(command: string, context: any): Promise<any> {
|
||||
// This would integrate with OpenAI/Anthropic for natural language understanding
|
||||
const intent = await this.analyzeIntent(command);
|
||||
const entities = await this.extractEntities(command);
|
||||
|
||||
return {
|
||||
original: command,
|
||||
intent,
|
||||
entities,
|
||||
context
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the parsed command
|
||||
*/
|
||||
private async executeCommand(parsed: any, request: CommandRequest): Promise<any> {
|
||||
const { intent, entities } = parsed;
|
||||
|
||||
switch (intent.type) {
|
||||
case 'file_operation':
|
||||
return await this.tools.executeFileOperation(entities);
|
||||
|
||||
case 'terminal_command':
|
||||
return await this.tools.executeTerminalCommand(entities.command);
|
||||
|
||||
case 'code_analysis':
|
||||
return await this.tools.analyzeCode(entities.file);
|
||||
|
||||
case 'web_search':
|
||||
return await this.tools.searchWeb(entities.query);
|
||||
|
||||
case 'autopilot_toggle':
|
||||
this.isAutopilotEnabled = !this.isAutopilotEnabled;
|
||||
return {
|
||||
message: `Autopilot mode ${this.isAutopilotEnabled ? 'enabled' : 'disabled'}`,
|
||||
data: { autopilot: this.isAutopilotEnabled }
|
||||
};
|
||||
|
||||
case 'voice_command':
|
||||
return await this.voice.processVoiceCommand(entities);
|
||||
|
||||
case 'memory_query':
|
||||
return await this.memory.queryMemory(entities.query);
|
||||
|
||||
default:
|
||||
return {
|
||||
message: `I understand you want to ${intent.type}. Let me help you with that.`,
|
||||
data: { intent, entities }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze the intent of a command using AI
|
||||
*/
|
||||
private async analyzeIntent(command: string): Promise<any> {
|
||||
// Mock AI analysis - in real implementation, this would call OpenAI/Anthropic
|
||||
const intents = {
|
||||
'file': 'file_operation',
|
||||
'read': 'file_operation',
|
||||
'write': 'file_operation',
|
||||
'create': 'file_operation',
|
||||
'delete': 'file_operation',
|
||||
'run': 'terminal_command',
|
||||
'execute': 'terminal_command',
|
||||
'analyze': 'code_analysis',
|
||||
'search': 'web_search',
|
||||
'find': 'web_search',
|
||||
'autopilot': 'autopilot_toggle',
|
||||
'voice': 'voice_command',
|
||||
'remember': 'memory_query',
|
||||
'recall': 'memory_query'
|
||||
};
|
||||
|
||||
const words = command.toLowerCase().split(' ');
|
||||
for (const word of words) {
|
||||
if (intents[word]) {
|
||||
return { type: intents[word], confidence: 0.9 };
|
||||
}
|
||||
}
|
||||
|
||||
return { type: 'general', confidence: 0.5 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract entities from the command
|
||||
*/
|
||||
private async extractEntities(command: string): Promise<any> {
|
||||
// Mock entity extraction - in real implementation, this would use NLP
|
||||
const entities: any = {};
|
||||
|
||||
// Extract file paths
|
||||
const fileMatch = command.match(/(\w+\.\w+)/);
|
||||
if (fileMatch) {
|
||||
entities.file = fileMatch[1];
|
||||
}
|
||||
|
||||
// Extract commands
|
||||
const commandMatch = command.match(/run\s+(.+)/i);
|
||||
if (commandMatch) {
|
||||
entities.command = commandMatch[1];
|
||||
}
|
||||
|
||||
// Extract search queries
|
||||
const searchMatch = command.match(/search\s+(.+)/i);
|
||||
if (searchMatch) {
|
||||
entities.query = searchMatch[1];
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable autopilot mode
|
||||
*/
|
||||
async toggleAutopilot(enabled: boolean): Promise<AIResponse> {
|
||||
this.isAutopilotEnabled = enabled;
|
||||
this.logger.info('Autopilot mode toggled', { enabled });
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Autopilot mode ${enabled ? 'enabled' : 'disabled'}`,
|
||||
data: { autopilot: enabled }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current system status
|
||||
*/
|
||||
async getStatus(): Promise<any> {
|
||||
return {
|
||||
autopilot: this.isAutopilotEnabled,
|
||||
memory: await this.memory.getStats(),
|
||||
tools: await this.tools.getStatus(),
|
||||
voice: await this.voice.getStatus()
|
||||
};
|
||||
}
|
||||
}
|
||||
138
Nowhere_AI_Agent/backend/src/index.ts
Normal file
138
Nowhere_AI_Agent/backend/src/index.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import compression from 'compression';
|
||||
import dotenv from 'dotenv';
|
||||
import { createServer } from 'http';
|
||||
import { Server as SocketIOServer } from 'socket.io';
|
||||
|
||||
import { Logger } from './utils/logger';
|
||||
import { errorHandler } from './middleware/error-handler';
|
||||
import { rateLimiter } from './middleware/rate-limiter';
|
||||
import { authMiddleware } from './middleware/auth';
|
||||
import { setupWebSocket } from './websocket';
|
||||
import { setupRoutes } from './routes';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
const server = createServer(app);
|
||||
const io = new SocketIOServer(server, {
|
||||
cors: {
|
||||
origin: process.env.FRONTEND_URL || "http://localhost:3000",
|
||||
methods: ["GET", "POST"]
|
||||
}
|
||||
});
|
||||
|
||||
const logger = new Logger('Server');
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
// Middleware
|
||||
app.use(helmet());
|
||||
app.use(compression());
|
||||
app.use(cors({
|
||||
origin: process.env.FRONTEND_URL || "http://localhost:3000",
|
||||
credentials: true
|
||||
}));
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
||||
|
||||
// Rate limiting
|
||||
app.use(rateLimiter);
|
||||
|
||||
// Authentication middleware (optional for public endpoints)
|
||||
app.use('/api/v1/public', (req, res, next) => next());
|
||||
app.use('/api/v1', authMiddleware);
|
||||
|
||||
// Setup WebSocket
|
||||
setupWebSocket(io);
|
||||
|
||||
// Setup routes
|
||||
setupRoutes(app);
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime(),
|
||||
memory: process.memoryUsage(),
|
||||
version: process.env.npm_package_version || '1.0.0'
|
||||
});
|
||||
});
|
||||
|
||||
// Error handling middleware (must be last)
|
||||
app.use(errorHandler);
|
||||
|
||||
// 404 handler
|
||||
app.use('*', (req, res) => {
|
||||
res.status(404).json({
|
||||
error: 'Not Found',
|
||||
message: `Route ${req.originalUrl} not found`,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
// Start server
|
||||
server.listen(PORT, () => {
|
||||
logger.info('🚀 Nowhere AI Agent Server Started', {
|
||||
port: PORT,
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Log startup information
|
||||
console.log(`
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ 🚀 Nowhere AI Agent ║
|
||||
║ ║
|
||||
║ 🌐 Server running on: http://localhost:${PORT} ║
|
||||
║ 📡 WebSocket available at: ws://localhost:${PORT} ║
|
||||
║ 🔧 Environment: ${process.env.NODE_ENV || 'development'} ║
|
||||
║ 📊 Health check: http://localhost:${PORT}/health ║
|
||||
║ ║
|
||||
║ 🎤 Voice Integration: ${process.env.AZURE_SPEECH_KEY ? 'Enabled' : 'Disabled'} ║
|
||||
║ 🧠 Memory System: ${process.env.REDIS_URL ? 'Redis + PostgreSQL' : 'In-Memory'} ║
|
||||
║ 🤖 Autopilot Mode: Available ║
|
||||
║ ║
|
||||
║ 📋 Available Endpoints: ║
|
||||
║ • POST /api/v1/command - Process text commands ║
|
||||
║ • POST /api/v1/voice - Process voice commands ║
|
||||
║ • POST /api/v1/autopilot - Toggle autopilot mode ║
|
||||
║ • GET /api/v1/memory/:userId - Get user memory ║
|
||||
║ • GET /api/v1/status - Get system status ║
|
||||
║ ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
`);
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGTERM', () => {
|
||||
logger.info('SIGTERM received, shutting down gracefully');
|
||||
server.close(() => {
|
||||
logger.info('Server closed');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
logger.info('SIGINT received, shutting down gracefully');
|
||||
server.close(() => {
|
||||
logger.info('Server closed');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle uncaught exceptions
|
||||
process.on('uncaughtException', (error) => {
|
||||
logger.error('Uncaught Exception', { error: error.message, stack: error.stack });
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
logger.error('Unhandled Rejection', { reason, promise });
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
export { app, server, io };
|
||||
250
Nowhere_AI_Agent/backend/src/memory/memory-manager.ts
Normal file
250
Nowhere_AI_Agent/backend/src/memory/memory-manager.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
export interface MemoryEntry {
|
||||
id: string;
|
||||
userId: string;
|
||||
type: 'conversation' | 'preference' | 'project' | 'learning';
|
||||
content: any;
|
||||
timestamp: string;
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export interface UserContext {
|
||||
userId: string;
|
||||
preferences: any;
|
||||
recentCommands: string[];
|
||||
projectContext: any;
|
||||
learningHistory: any[];
|
||||
lastInteraction: string;
|
||||
}
|
||||
|
||||
export class MemoryManager {
|
||||
private logger: Logger;
|
||||
private memoryCache: Map<string, any>;
|
||||
private userContexts: Map<string, UserContext>;
|
||||
|
||||
constructor() {
|
||||
this.logger = new Logger('MemoryManager');
|
||||
this.memoryCache = new Map();
|
||||
this.userContexts = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user context and memory
|
||||
*/
|
||||
async getUserContext(userId: string): Promise<UserContext> {
|
||||
try {
|
||||
// Check cache first
|
||||
if (this.userContexts.has(userId)) {
|
||||
return this.userContexts.get(userId)!;
|
||||
}
|
||||
|
||||
// In a real implementation, this would load from Redis/PostgreSQL
|
||||
const context: UserContext = {
|
||||
userId,
|
||||
preferences: await this.getUserPreferences(userId),
|
||||
recentCommands: await this.getRecentCommands(userId),
|
||||
projectContext: await this.getProjectContext(userId),
|
||||
learningHistory: await this.getLearningHistory(userId),
|
||||
lastInteraction: new Date().toISOString()
|
||||
};
|
||||
|
||||
// Cache the context
|
||||
this.userContexts.set(userId, context);
|
||||
return context;
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting user context', { userId, error: error.message });
|
||||
return this.getDefaultContext(userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user context
|
||||
*/
|
||||
async updateUserContext(userId: string, updates: Partial<UserContext>): Promise<void> {
|
||||
try {
|
||||
const currentContext = await this.getUserContext(userId);
|
||||
const updatedContext = { ...currentContext, ...updates };
|
||||
|
||||
this.userContexts.set(userId, updatedContext);
|
||||
|
||||
// In a real implementation, this would save to Redis/PostgreSQL
|
||||
await this.persistUserContext(userId, updatedContext);
|
||||
|
||||
this.logger.info('User context updated', { userId });
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Error updating user context', { userId, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a memory entry
|
||||
*/
|
||||
async storeMemory(entry: MemoryEntry): Promise<void> {
|
||||
try {
|
||||
// Cache the memory entry
|
||||
const key = `${entry.userId}:${entry.type}:${entry.id}`;
|
||||
this.memoryCache.set(key, entry);
|
||||
|
||||
// In a real implementation, this would save to Redis/PostgreSQL
|
||||
await this.persistMemoryEntry(entry);
|
||||
|
||||
this.logger.info('Memory entry stored', {
|
||||
userId: entry.userId,
|
||||
type: entry.type,
|
||||
id: entry.id
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Error storing memory entry', {
|
||||
userId: entry.userId,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query memory for relevant information
|
||||
*/
|
||||
async queryMemory(query: string): Promise<MemoryEntry[]> {
|
||||
try {
|
||||
// In a real implementation, this would use vector search or semantic search
|
||||
const results: MemoryEntry[] = [];
|
||||
|
||||
// Mock search through cached entries
|
||||
for (const [key, entry] of this.memoryCache.entries()) {
|
||||
if (this.matchesQuery(entry, query)) {
|
||||
results.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.info('Memory query executed', { query, resultsCount: results.length });
|
||||
return results;
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Error querying memory', { query, error: error.message });
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory statistics
|
||||
*/
|
||||
async getStats(): Promise<any> {
|
||||
return {
|
||||
cacheSize: this.memoryCache.size,
|
||||
userContexts: this.userContexts.size,
|
||||
totalEntries: this.memoryCache.size,
|
||||
lastUpdated: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear user memory
|
||||
*/
|
||||
async clearUserMemory(userId: string): Promise<void> {
|
||||
try {
|
||||
// Clear from cache
|
||||
this.userContexts.delete(userId);
|
||||
|
||||
// Clear memory entries for this user
|
||||
for (const [key] of this.memoryCache.entries()) {
|
||||
if (key.startsWith(`${userId}:`)) {
|
||||
this.memoryCache.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
// In a real implementation, this would clear from Redis/PostgreSQL
|
||||
await this.clearPersistedUserMemory(userId);
|
||||
|
||||
this.logger.info('User memory cleared', { userId });
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Error clearing user memory', { userId, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
private async getUserPreferences(userId: string): Promise<any> {
|
||||
// Mock implementation - in real app would load from database
|
||||
return {
|
||||
voiceEnabled: true,
|
||||
autopilotEnabled: false,
|
||||
preferredLanguage: 'en',
|
||||
theme: 'dark'
|
||||
};
|
||||
}
|
||||
|
||||
private async getRecentCommands(userId: string): Promise<string[]> {
|
||||
// Mock implementation - in real app would load from database
|
||||
return [
|
||||
'analyze this code',
|
||||
'create a new component',
|
||||
'search for documentation'
|
||||
];
|
||||
}
|
||||
|
||||
private async getProjectContext(userId: string): Promise<any> {
|
||||
// Mock implementation - in real app would load from database
|
||||
return {
|
||||
currentProject: 'nowhere-ai-agent',
|
||||
lastFiles: ['src/core/nowhere.ts', 'src/memory/memory-manager.ts'],
|
||||
dependencies: ['express', 'typescript', 'winston']
|
||||
};
|
||||
}
|
||||
|
||||
private async getLearningHistory(userId: string): Promise<any[]> {
|
||||
// Mock implementation - in real app would load from database
|
||||
return [
|
||||
{
|
||||
topic: 'TypeScript',
|
||||
proficiency: 0.8,
|
||||
lastPracticed: '2024-01-15'
|
||||
},
|
||||
{
|
||||
topic: 'AI Integration',
|
||||
proficiency: 0.6,
|
||||
lastPracticed: '2024-01-10'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
private getDefaultContext(userId: string): UserContext {
|
||||
return {
|
||||
userId,
|
||||
preferences: { voiceEnabled: true, autopilotEnabled: false },
|
||||
recentCommands: [],
|
||||
projectContext: {},
|
||||
learningHistory: [],
|
||||
lastInteraction: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
private async persistUserContext(userId: string, context: UserContext): Promise<void> {
|
||||
// Mock implementation - in real app would save to Redis/PostgreSQL
|
||||
this.logger.debug('Persisting user context', { userId });
|
||||
}
|
||||
|
||||
private async persistMemoryEntry(entry: MemoryEntry): Promise<void> {
|
||||
// Mock implementation - in real app would save to Redis/PostgreSQL
|
||||
this.logger.debug('Persisting memory entry', {
|
||||
userId: entry.userId,
|
||||
type: entry.type
|
||||
});
|
||||
}
|
||||
|
||||
private async clearPersistedUserMemory(userId: string): Promise<void> {
|
||||
// Mock implementation - in real app would clear from Redis/PostgreSQL
|
||||
this.logger.debug('Clearing persisted user memory', { userId });
|
||||
}
|
||||
|
||||
private matchesQuery(entry: MemoryEntry, query: string): boolean {
|
||||
// Simple text matching - in real app would use semantic search
|
||||
const queryLower = query.toLowerCase();
|
||||
const contentStr = JSON.stringify(entry.content).toLowerCase();
|
||||
return contentStr.includes(queryLower);
|
||||
}
|
||||
}
|
||||
107
Nowhere_AI_Agent/backend/src/middleware/auth.ts
Normal file
107
Nowhere_AI_Agent/backend/src/middleware/auth.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
const logger = new Logger('AuthMiddleware');
|
||||
|
||||
export interface AuthenticatedRequest extends Request {
|
||||
user?: {
|
||||
id: string;
|
||||
email?: string;
|
||||
role?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function authMiddleware(req: AuthenticatedRequest, res: Response, next: NextFunction) {
|
||||
try {
|
||||
// Skip authentication for public endpoints
|
||||
if (req.path.startsWith('/public')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Get token from header
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
// For development, allow requests without token
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
req.user = {
|
||||
id: 'default-user',
|
||||
email: 'dev@nowhere.ai',
|
||||
role: 'developer'
|
||||
};
|
||||
return next();
|
||||
}
|
||||
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Access token required'
|
||||
});
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
|
||||
|
||||
// Verify token
|
||||
const secret = process.env.JWT_SECRET || 'nowhere-secret-key';
|
||||
const decoded = jwt.verify(token, secret) as any;
|
||||
|
||||
// Add user info to request
|
||||
req.user = {
|
||||
id: decoded.id || decoded.sub,
|
||||
email: decoded.email,
|
||||
role: decoded.role || 'user'
|
||||
};
|
||||
|
||||
logger.debug('User authenticated', {
|
||||
userId: req.user.id,
|
||||
role: req.user.role
|
||||
});
|
||||
|
||||
next();
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Authentication failed', { error: error.message });
|
||||
|
||||
// For development, allow requests with invalid tokens
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
req.user = {
|
||||
id: 'default-user',
|
||||
email: 'dev@nowhere.ai',
|
||||
role: 'developer'
|
||||
};
|
||||
return next();
|
||||
}
|
||||
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid or expired token'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate JWT token for user
|
||||
*/
|
||||
export function generateToken(userId: string, email?: string, role?: string): string {
|
||||
const secret = process.env.JWT_SECRET || 'nowhere-secret-key';
|
||||
const payload = {
|
||||
id: userId,
|
||||
email,
|
||||
role: role || 'user',
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
exp: Math.floor(Date.now() / 1000) + (24 * 60 * 60) // 24 hours
|
||||
};
|
||||
|
||||
return jwt.sign(payload, secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify token and return user info
|
||||
*/
|
||||
export function verifyToken(token: string): any {
|
||||
try {
|
||||
const secret = process.env.JWT_SECRET || 'nowhere-secret-key';
|
||||
return jwt.verify(token, secret);
|
||||
} catch (error) {
|
||||
throw new Error('Invalid token');
|
||||
}
|
||||
}
|
||||
127
Nowhere_AI_Agent/backend/src/middleware/error-handler.ts
Normal file
127
Nowhere_AI_Agent/backend/src/middleware/error-handler.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
const logger = new Logger('ErrorHandler');
|
||||
|
||||
export interface AppError extends Error {
|
||||
statusCode?: number;
|
||||
isOperational?: boolean;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
export function errorHandler(
|
||||
error: AppError,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
// Log the error
|
||||
logger.error('Unhandled error', {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
url: req.url,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent')
|
||||
});
|
||||
|
||||
// Determine status code
|
||||
const statusCode = error.statusCode || 500;
|
||||
|
||||
// Determine if it's an operational error
|
||||
const isOperational = error.isOperational || false;
|
||||
|
||||
// Create error response
|
||||
const errorResponse = {
|
||||
success: false,
|
||||
message: error.message || 'Internal server error',
|
||||
...(process.env.NODE_ENV === 'development' && {
|
||||
stack: error.stack,
|
||||
code: error.code
|
||||
}),
|
||||
timestamp: new Date().toISOString(),
|
||||
path: req.url,
|
||||
method: req.method
|
||||
};
|
||||
|
||||
// Send response
|
||||
res.status(statusCode).json(errorResponse);
|
||||
|
||||
// For non-operational errors, consider shutting down gracefully
|
||||
if (!isOperational && process.env.NODE_ENV === 'production') {
|
||||
logger.error('Non-operational error detected, shutting down gracefully');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create operational errors
|
||||
*/
|
||||
export class OperationalError extends Error implements AppError {
|
||||
public statusCode: number;
|
||||
public isOperational: boolean;
|
||||
public code: string;
|
||||
|
||||
constructor(message: string, statusCode: number = 500, code?: string) {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
this.isOperational = true;
|
||||
this.code = code || 'OPERATIONAL_ERROR';
|
||||
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create validation errors
|
||||
*/
|
||||
export class ValidationError extends OperationalError {
|
||||
constructor(message: string) {
|
||||
super(message, 400, 'VALIDATION_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create authentication errors
|
||||
*/
|
||||
export class AuthenticationError extends OperationalError {
|
||||
constructor(message: string = 'Authentication failed') {
|
||||
super(message, 401, 'AUTHENTICATION_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create authorization errors
|
||||
*/
|
||||
export class AuthorizationError extends OperationalError {
|
||||
constructor(message: string = 'Access denied') {
|
||||
super(message, 403, 'AUTHORIZATION_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create not found errors
|
||||
*/
|
||||
export class NotFoundError extends OperationalError {
|
||||
constructor(message: string = 'Resource not found') {
|
||||
super(message, 404, 'NOT_FOUND_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create rate limit errors
|
||||
*/
|
||||
export class RateLimitError extends OperationalError {
|
||||
constructor(message: string = 'Rate limit exceeded') {
|
||||
super(message, 429, 'RATE_LIMIT_ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async error wrapper
|
||||
*/
|
||||
export function asyncHandler(fn: Function) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
};
|
||||
}
|
||||
109
Nowhere_AI_Agent/backend/src/middleware/rate-limiter.ts
Normal file
109
Nowhere_AI_Agent/backend/src/middleware/rate-limiter.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import { Request, Response } from 'express';
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
const logger = new Logger('RateLimiter');
|
||||
|
||||
// General rate limiter
|
||||
export const rateLimiter = rateLimit({
|
||||
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW || '900000'), // 15 minutes
|
||||
max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || '100'), // limit each IP to 100 requests per windowMs
|
||||
message: {
|
||||
success: false,
|
||||
message: 'Too many requests from this IP, please try again later.',
|
||||
code: 'RATE_LIMIT_EXCEEDED'
|
||||
},
|
||||
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
||||
handler: (req, res) => {
|
||||
logger.warn('Rate limit exceeded', {
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent'),
|
||||
url: req.url
|
||||
});
|
||||
res.status(429).json({
|
||||
success: false,
|
||||
message: 'Too many requests from this IP, please try again later.',
|
||||
code: 'RATE_LIMIT_EXCEEDED',
|
||||
retryAfter: Math.ceil(parseInt(process.env.RATE_LIMIT_WINDOW || '900000') / 1000)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Stricter rate limiter for authentication endpoints
|
||||
export const authRateLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // limit each IP to 5 requests per windowMs
|
||||
message: {
|
||||
success: false,
|
||||
message: 'Too many authentication attempts, please try again later.',
|
||||
code: 'AUTH_RATE_LIMIT_EXCEEDED'
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
handler: (req, res) => {
|
||||
logger.warn('Auth rate limit exceeded', {
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent'),
|
||||
url: req.url
|
||||
});
|
||||
res.status(429).json({
|
||||
success: false,
|
||||
message: 'Too many authentication attempts, please try again later.',
|
||||
code: 'AUTH_RATE_LIMIT_EXCEEDED',
|
||||
retryAfter: 900 // 15 minutes
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Rate limiter for voice endpoints (more lenient)
|
||||
export const voiceRateLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 30, // limit each IP to 30 requests per windowMs
|
||||
message: {
|
||||
success: false,
|
||||
message: 'Too many voice requests, please try again later.',
|
||||
code: 'VOICE_RATE_LIMIT_EXCEEDED'
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
handler: (req, res) => {
|
||||
logger.warn('Voice rate limit exceeded', {
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent'),
|
||||
url: req.url
|
||||
});
|
||||
res.status(429).json({
|
||||
success: false,
|
||||
message: 'Too many voice requests, please try again later.',
|
||||
code: 'VOICE_RATE_LIMIT_EXCEEDED',
|
||||
retryAfter: 60 // 1 minute
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Rate limiter for tool execution (stricter for security)
|
||||
export const toolRateLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 10, // limit each IP to 10 requests per windowMs
|
||||
message: {
|
||||
success: false,
|
||||
message: 'Too many tool execution requests, please try again later.',
|
||||
code: 'TOOL_RATE_LIMIT_EXCEEDED'
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
handler: (req, res) => {
|
||||
logger.warn('Tool rate limit exceeded', {
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent'),
|
||||
url: req.url
|
||||
});
|
||||
res.status(429).json({
|
||||
success: false,
|
||||
message: 'Too many tool execution requests, please try again later.',
|
||||
code: 'TOOL_RATE_LIMIT_EXCEEDED',
|
||||
retryAfter: 60 // 1 minute
|
||||
});
|
||||
}
|
||||
});
|
||||
348
Nowhere_AI_Agent/backend/src/routes/index.ts
Normal file
348
Nowhere_AI_Agent/backend/src/routes/index.ts
Normal file
@@ -0,0 +1,348 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { NowhereCore, CommandRequest, AIResponse } from '../core/nowhere';
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
const router = Router();
|
||||
const nowhere = new NowhereCore();
|
||||
const logger = new Logger('Routes');
|
||||
|
||||
/**
|
||||
* Process text commands
|
||||
*/
|
||||
router.post('/command', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { command, userId, context, autopilot } = req.body;
|
||||
|
||||
if (!command) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Command is required'
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('Processing command request', {
|
||||
command: command.substring(0, 100),
|
||||
userId,
|
||||
autopilot
|
||||
});
|
||||
|
||||
const request: CommandRequest = {
|
||||
command,
|
||||
userId: userId || 'default',
|
||||
context,
|
||||
autopilot: autopilot || false
|
||||
};
|
||||
|
||||
const response: AIResponse = await nowhere.processCommand(request);
|
||||
|
||||
res.json(response);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Command processing error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Internal server error',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Process voice commands
|
||||
*/
|
||||
router.post('/voice', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { audioData, userId, context } = req.body;
|
||||
|
||||
if (!audioData) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Audio data is required'
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('Processing voice request', {
|
||||
audioSize: audioData.length,
|
||||
userId
|
||||
});
|
||||
|
||||
// Convert base64 audio data to buffer
|
||||
const audioBuffer = Buffer.from(audioData, 'base64');
|
||||
|
||||
// Process voice input
|
||||
const voiceCommand = await nowhere['voice'].processVoiceInput(audioBuffer);
|
||||
|
||||
// Process the voice command
|
||||
const request: CommandRequest = {
|
||||
command: voiceCommand.text,
|
||||
userId: userId || 'default',
|
||||
context,
|
||||
voice: true
|
||||
};
|
||||
|
||||
const response: AIResponse = await nowhere.processCommand(request);
|
||||
|
||||
// Generate voice response if needed
|
||||
if (response.success && req.body.generateVoice) {
|
||||
const voiceResponse = await nowhere['voice'].generateVoiceResponse({
|
||||
text: response.message,
|
||||
mode: 'brief'
|
||||
});
|
||||
|
||||
response.data = {
|
||||
...response.data,
|
||||
voiceResponse: voiceResponse.toString('base64')
|
||||
};
|
||||
}
|
||||
|
||||
res.json(response);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Voice processing error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Voice processing failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggle autopilot mode
|
||||
*/
|
||||
router.post('/autopilot', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { enabled, userId } = req.body;
|
||||
|
||||
logger.info('Toggling autopilot mode', { enabled, userId });
|
||||
|
||||
const response = await nowhere.toggleAutopilot(enabled);
|
||||
|
||||
res.json(response);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Autopilot toggle error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to toggle autopilot mode',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get user memory
|
||||
*/
|
||||
router.get('/memory/:userId', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
const { query } = req.query;
|
||||
|
||||
logger.info('Getting user memory', { userId, query });
|
||||
|
||||
if (query) {
|
||||
// Query specific memory
|
||||
const memoryEntries = await nowhere['memory'].queryMemory(query as string);
|
||||
res.json({
|
||||
success: true,
|
||||
data: memoryEntries
|
||||
});
|
||||
} else {
|
||||
// Get user context
|
||||
const userContext = await nowhere['memory'].getUserContext(userId);
|
||||
res.json({
|
||||
success: true,
|
||||
data: userContext
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Memory retrieval error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to retrieve memory',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Clear user memory
|
||||
*/
|
||||
router.delete('/memory/:userId', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
|
||||
logger.info('Clearing user memory', { userId });
|
||||
|
||||
await nowhere['memory'].clearUserMemory(userId);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'User memory cleared successfully'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Memory clearing error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to clear memory',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get system status
|
||||
*/
|
||||
router.get('/status', async (req: Request, res: Response) => {
|
||||
try {
|
||||
logger.info('Getting system status');
|
||||
|
||||
const status = await nowhere.getStatus();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: status
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Status retrieval error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to get system status',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Execute tools directly
|
||||
*/
|
||||
router.post('/tools/execute', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { operation, params, userId } = req.body;
|
||||
|
||||
if (!operation) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Operation is required'
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('Executing tool', { operation, userId });
|
||||
|
||||
let result;
|
||||
switch (operation) {
|
||||
case 'file_operation':
|
||||
result = await nowhere['tools'].executeFileOperation(params);
|
||||
break;
|
||||
|
||||
case 'terminal_command':
|
||||
result = await nowhere['tools'].executeTerminalCommand(params.command);
|
||||
break;
|
||||
|
||||
case 'code_analysis':
|
||||
result = await nowhere['tools'].analyzeCode(params.file);
|
||||
break;
|
||||
|
||||
case 'web_search':
|
||||
result = await nowhere['tools'].searchWeb(params.query);
|
||||
break;
|
||||
|
||||
default:
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `Unknown operation: ${operation}`
|
||||
});
|
||||
}
|
||||
|
||||
res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Tool execution error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Tool execution failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Voice settings management
|
||||
*/
|
||||
router.put('/voice/settings', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { settings } = req.body;
|
||||
|
||||
logger.info('Updating voice settings', { settings });
|
||||
|
||||
await nowhere['voice'].updateSettings(settings);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Voice settings updated successfully'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Voice settings update error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to update voice settings',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Start voice listening
|
||||
*/
|
||||
router.post('/voice/listen', async (req: Request, res: Response) => {
|
||||
try {
|
||||
logger.info('Starting voice listening');
|
||||
|
||||
await nowhere['voice'].startListening();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Voice listening started'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Voice listening start error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to start voice listening',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Stop voice listening
|
||||
*/
|
||||
router.post('/voice/stop', async (req: Request, res: Response) => {
|
||||
try {
|
||||
logger.info('Stopping voice listening');
|
||||
|
||||
await nowhere['voice'].stopListening();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Voice listening stopped'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Voice listening stop error', { error: error.message });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to stop voice listening',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export function setupRoutes(app: any) {
|
||||
app.use('/api/v1', router);
|
||||
}
|
||||
373
Nowhere_AI_Agent/backend/src/tools/tool-executor.ts
Normal file
373
Nowhere_AI_Agent/backend/src/tools/tool-executor.ts
Normal file
@@ -0,0 +1,373 @@
|
||||
import { Logger } from '../utils/logger';
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
export interface ToolResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data?: any;
|
||||
error?: string;
|
||||
executionTime?: number;
|
||||
}
|
||||
|
||||
export interface FileOperation {
|
||||
operation: 'read' | 'write' | 'create' | 'delete' | 'list';
|
||||
path: string;
|
||||
content?: string;
|
||||
options?: any;
|
||||
}
|
||||
|
||||
export interface CodeAnalysis {
|
||||
file: string;
|
||||
analysis: {
|
||||
complexity: number;
|
||||
lines: number;
|
||||
functions: number;
|
||||
issues: string[];
|
||||
suggestions: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export class ToolExecutor {
|
||||
private logger: Logger;
|
||||
private allowedCommands: Set<string>;
|
||||
private safePaths: Set<string>;
|
||||
|
||||
constructor() {
|
||||
this.logger = new Logger('ToolExecutor');
|
||||
this.allowedCommands = new Set([
|
||||
'ls', 'dir', 'pwd', 'echo', 'cat', 'type',
|
||||
'npm', 'yarn', 'git', 'node', 'tsc',
|
||||
'mkdir', 'rmdir', 'cp', 'copy', 'mv', 'move'
|
||||
]);
|
||||
this.safePaths = new Set([
|
||||
process.cwd(),
|
||||
path.join(process.cwd(), 'src'),
|
||||
path.join(process.cwd(), 'frontend')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute file operations
|
||||
*/
|
||||
async executeFileOperation(operation: FileOperation): Promise<ToolResult> {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
this.logger.info('Executing file operation', { operation: operation.operation, path: operation.path });
|
||||
|
||||
switch (operation.operation) {
|
||||
case 'read':
|
||||
return await this.readFile(operation.path);
|
||||
|
||||
case 'write':
|
||||
return await this.writeFile(operation.path, operation.content || '');
|
||||
|
||||
case 'create':
|
||||
return await this.createFile(operation.path, operation.content || '');
|
||||
|
||||
case 'delete':
|
||||
return await this.deleteFile(operation.path);
|
||||
|
||||
case 'list':
|
||||
return await this.listDirectory(operation.path);
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported file operation: ${operation.operation}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('File operation failed', {
|
||||
operation: operation.operation,
|
||||
path: operation.path,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `File operation failed: ${error.message}`,
|
||||
error: error.message,
|
||||
executionTime: Date.now() - startTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute terminal commands safely
|
||||
*/
|
||||
async executeTerminalCommand(command: string): Promise<ToolResult> {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
this.logger.info('Executing terminal command', { command });
|
||||
|
||||
// Validate command safety
|
||||
if (!this.isCommandSafe(command)) {
|
||||
throw new Error('Command not allowed for security reasons');
|
||||
}
|
||||
|
||||
const { stdout, stderr } = await execAsync(command, {
|
||||
cwd: process.cwd(),
|
||||
timeout: 30000 // 30 second timeout
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Command executed successfully',
|
||||
data: {
|
||||
stdout: stdout.trim(),
|
||||
stderr: stderr.trim(),
|
||||
command
|
||||
},
|
||||
executionTime: Date.now() - startTime
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Terminal command failed', { command, error: error.message });
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `Command execution failed: ${error.message}`,
|
||||
error: error.message,
|
||||
executionTime: Date.now() - startTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze code files
|
||||
*/
|
||||
async analyzeCode(filePath: string): Promise<ToolResult> {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
this.logger.info('Analyzing code file', { filePath });
|
||||
|
||||
const content = await fs.readFile(filePath, 'utf-8');
|
||||
const analysis = this.performCodeAnalysis(content, filePath);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Code analysis completed',
|
||||
data: {
|
||||
file: filePath,
|
||||
analysis
|
||||
},
|
||||
executionTime: Date.now() - startTime
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Code analysis failed', { filePath, error: error.message });
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `Code analysis failed: ${error.message}`,
|
||||
error: error.message,
|
||||
executionTime: Date.now() - startTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the web for information
|
||||
*/
|
||||
async searchWeb(query: string): Promise<ToolResult> {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
this.logger.info('Performing web search', { query });
|
||||
|
||||
// Mock web search - in real implementation would use a search API
|
||||
const mockResults = [
|
||||
{
|
||||
title: `Search results for: ${query}`,
|
||||
url: `https://example.com/search?q=${encodeURIComponent(query)}`,
|
||||
snippet: `Information about ${query} from various sources.`
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Web search completed',
|
||||
data: {
|
||||
query,
|
||||
results: mockResults
|
||||
},
|
||||
executionTime: Date.now() - startTime
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Web search failed', { query, error: error.message });
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `Web search failed: ${error.message}`,
|
||||
error: error.message,
|
||||
executionTime: Date.now() - startTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tool execution status
|
||||
*/
|
||||
async getStatus(): Promise<any> {
|
||||
return {
|
||||
allowedCommands: Array.from(this.allowedCommands),
|
||||
safePaths: Array.from(this.safePaths),
|
||||
lastUpdated: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
private async readFile(filePath: string): Promise<ToolResult> {
|
||||
const content = await fs.readFile(filePath, 'utf-8');
|
||||
return {
|
||||
success: true,
|
||||
message: 'File read successfully',
|
||||
data: { content, path: filePath }
|
||||
};
|
||||
}
|
||||
|
||||
private async writeFile(filePath: string, content: string): Promise<ToolResult> {
|
||||
await fs.writeFile(filePath, content, 'utf-8');
|
||||
return {
|
||||
success: true,
|
||||
message: 'File written successfully',
|
||||
data: { path: filePath, size: content.length }
|
||||
};
|
||||
}
|
||||
|
||||
private async createFile(filePath: string, content: string): Promise<ToolResult> {
|
||||
// Ensure directory exists
|
||||
const dir = path.dirname(filePath);
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
|
||||
await fs.writeFile(filePath, content, 'utf-8');
|
||||
return {
|
||||
success: true,
|
||||
message: 'File created successfully',
|
||||
data: { path: filePath, size: content.length }
|
||||
};
|
||||
}
|
||||
|
||||
private async deleteFile(filePath: string): Promise<ToolResult> {
|
||||
await fs.unlink(filePath);
|
||||
return {
|
||||
success: true,
|
||||
message: 'File deleted successfully',
|
||||
data: { path: filePath }
|
||||
};
|
||||
}
|
||||
|
||||
private async listDirectory(dirPath: string): Promise<ToolResult> {
|
||||
const items = await fs.readdir(dirPath, { withFileTypes: true });
|
||||
const files = items
|
||||
.filter(item => item.isFile())
|
||||
.map(item => ({ name: item.name, type: 'file' }));
|
||||
|
||||
const directories = items
|
||||
.filter(item => item.isDirectory())
|
||||
.map(item => ({ name: item.name, type: 'directory' }));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Directory listed successfully',
|
||||
data: {
|
||||
path: dirPath,
|
||||
files,
|
||||
directories,
|
||||
totalItems: items.length
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private isCommandSafe(command: string): boolean {
|
||||
const parts = command.split(' ');
|
||||
const baseCommand = parts[0].toLowerCase();
|
||||
|
||||
// Check if command is in allowed list
|
||||
if (!this.allowedCommands.has(baseCommand)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Additional safety checks
|
||||
const dangerousPatterns = [
|
||||
'rm -rf',
|
||||
'del /s',
|
||||
'format',
|
||||
'shutdown',
|
||||
'reboot'
|
||||
];
|
||||
|
||||
const commandLower = command.toLowerCase();
|
||||
for (const pattern of dangerousPatterns) {
|
||||
if (commandLower.includes(pattern)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private performCodeAnalysis(content: string, filePath: string): CodeAnalysis['analysis'] {
|
||||
const lines = content.split('\n');
|
||||
const functions = (content.match(/function\s+\w+/g) || []).length;
|
||||
const complexity = this.calculateComplexity(content);
|
||||
|
||||
const issues: string[] = [];
|
||||
const suggestions: string[] = [];
|
||||
|
||||
// Basic code analysis
|
||||
if (lines.length > 500) {
|
||||
issues.push('File is very long, consider breaking it into smaller modules');
|
||||
}
|
||||
|
||||
if (complexity > 10) {
|
||||
issues.push('High cyclomatic complexity detected');
|
||||
suggestions.push('Consider refactoring complex functions');
|
||||
}
|
||||
|
||||
if (functions > 20) {
|
||||
issues.push('Many functions in single file');
|
||||
suggestions.push('Consider splitting into multiple files');
|
||||
}
|
||||
|
||||
return {
|
||||
complexity,
|
||||
lines: lines.length,
|
||||
functions,
|
||||
issues,
|
||||
suggestions
|
||||
};
|
||||
}
|
||||
|
||||
private calculateComplexity(content: string): number {
|
||||
// Simple cyclomatic complexity calculation
|
||||
const complexityFactors = [
|
||||
/if\s*\(/g,
|
||||
/else\s*{/g,
|
||||
/for\s*\(/g,
|
||||
/while\s*\(/g,
|
||||
/switch\s*\(/g,
|
||||
/case\s+/g,
|
||||
/\|\|/g,
|
||||
/&&/g
|
||||
];
|
||||
|
||||
let complexity = 1; // Base complexity
|
||||
for (const factor of complexityFactors) {
|
||||
const matches = content.match(factor);
|
||||
if (matches) {
|
||||
complexity += matches.length;
|
||||
}
|
||||
}
|
||||
|
||||
return complexity;
|
||||
}
|
||||
}
|
||||
71
Nowhere_AI_Agent/backend/src/utils/logger.ts
Normal file
71
Nowhere_AI_Agent/backend/src/utils/logger.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import winston from 'winston';
|
||||
import path from 'path';
|
||||
|
||||
export class Logger {
|
||||
private logger: winston.Logger;
|
||||
|
||||
constructor(service: string) {
|
||||
this.logger = winston.createLogger({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.json()
|
||||
),
|
||||
defaultMeta: { service },
|
||||
transports: [
|
||||
// Console transport
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.simple()
|
||||
)
|
||||
}),
|
||||
// File transport for errors
|
||||
new winston.transports.File({
|
||||
filename: path.join('logs', 'error.log'),
|
||||
level: 'error',
|
||||
maxsize: 5242880, // 5MB
|
||||
maxFiles: 5
|
||||
}),
|
||||
// File transport for all logs
|
||||
new winston.transports.File({
|
||||
filename: path.join('logs', 'combined.log'),
|
||||
maxsize: 5242880, // 5MB
|
||||
maxFiles: 5
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// Handle uncaught exceptions
|
||||
this.logger.exceptions.handle(
|
||||
new winston.transports.File({
|
||||
filename: path.join('logs', 'exceptions.log')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
info(message: string, meta?: any) {
|
||||
this.logger.info(message, meta);
|
||||
}
|
||||
|
||||
error(message: string, meta?: any) {
|
||||
this.logger.error(message, meta);
|
||||
}
|
||||
|
||||
warn(message: string, meta?: any) {
|
||||
this.logger.warn(message, meta);
|
||||
}
|
||||
|
||||
debug(message: string, meta?: any) {
|
||||
this.logger.debug(message, meta);
|
||||
}
|
||||
|
||||
verbose(message: string, meta?: any) {
|
||||
this.logger.verbose(message, meta);
|
||||
}
|
||||
|
||||
silly(message: string, meta?: any) {
|
||||
this.logger.silly(message, meta);
|
||||
}
|
||||
}
|
||||
317
Nowhere_AI_Agent/backend/src/voice/voice-processor.ts
Normal file
317
Nowhere_AI_Agent/backend/src/voice/voice-processor.ts
Normal file
@@ -0,0 +1,317 @@
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
export interface VoiceCommand {
|
||||
text: string;
|
||||
confidence: number;
|
||||
intent: string;
|
||||
entities: any[];
|
||||
}
|
||||
|
||||
export interface VoiceResponse {
|
||||
text: string;
|
||||
audio?: Buffer;
|
||||
mode: 'brief' | 'detailed' | 'silent' | 'interactive';
|
||||
}
|
||||
|
||||
export interface VoiceSettings {
|
||||
enabled: boolean;
|
||||
language: string;
|
||||
voice: string;
|
||||
speed: number;
|
||||
volume: number;
|
||||
}
|
||||
|
||||
export class VoiceProcessor {
|
||||
private logger: Logger;
|
||||
private settings: VoiceSettings;
|
||||
private isListening: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.logger = new Logger('VoiceProcessor');
|
||||
this.settings = {
|
||||
enabled: true,
|
||||
language: 'en-US',
|
||||
voice: 'default',
|
||||
speed: 1.0,
|
||||
volume: 1.0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Process voice input (speech recognition)
|
||||
*/
|
||||
async processVoiceInput(audioData: Buffer): Promise<VoiceCommand> {
|
||||
try {
|
||||
this.logger.info('Processing voice input', {
|
||||
audioSize: audioData.length,
|
||||
language: this.settings.language
|
||||
});
|
||||
|
||||
// Mock speech recognition - in real implementation would use Azure Speech Services
|
||||
const mockText = this.mockSpeechRecognition(audioData);
|
||||
const intent = await this.analyzeVoiceIntent(mockText);
|
||||
const entities = await this.extractVoiceEntities(mockText);
|
||||
|
||||
const command: VoiceCommand = {
|
||||
text: mockText,
|
||||
confidence: 0.85,
|
||||
intent: intent.type,
|
||||
entities
|
||||
};
|
||||
|
||||
this.logger.info('Voice command processed', {
|
||||
text: command.text,
|
||||
intent: command.intent,
|
||||
confidence: command.confidence
|
||||
});
|
||||
|
||||
return command;
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Voice processing failed', { error: error.message });
|
||||
throw new Error(`Voice processing failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate voice response (text-to-speech)
|
||||
*/
|
||||
async generateVoiceResponse(response: VoiceResponse): Promise<Buffer> {
|
||||
try {
|
||||
this.logger.info('Generating voice response', {
|
||||
text: response.text.substring(0, 50) + '...',
|
||||
mode: response.mode
|
||||
});
|
||||
|
||||
// Mock TTS - in real implementation would use Azure Speech Services
|
||||
const audioBuffer = this.mockTextToSpeech(response.text, this.settings);
|
||||
|
||||
this.logger.info('Voice response generated', {
|
||||
audioSize: audioBuffer.length,
|
||||
textLength: response.text.length
|
||||
});
|
||||
|
||||
return audioBuffer;
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Voice response generation failed', { error: error.message });
|
||||
throw new Error(`Voice response generation failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process voice command from text
|
||||
*/
|
||||
async processVoiceCommand(command: any): Promise<any> {
|
||||
try {
|
||||
this.logger.info('Processing voice command', { command });
|
||||
|
||||
// Parse voice command and convert to action
|
||||
const action = await this.parseVoiceCommand(command);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Voice command processed successfully',
|
||||
data: action
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Voice command processing failed', { error: error.message });
|
||||
return {
|
||||
success: false,
|
||||
message: 'Voice command processing failed',
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start voice listening mode
|
||||
*/
|
||||
async startListening(): Promise<void> {
|
||||
if (this.isListening) {
|
||||
throw new Error('Already listening');
|
||||
}
|
||||
|
||||
this.isListening = true;
|
||||
this.logger.info('Voice listening started');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop voice listening mode
|
||||
*/
|
||||
async stopListening(): Promise<void> {
|
||||
if (!this.isListening) {
|
||||
throw new Error('Not currently listening');
|
||||
}
|
||||
|
||||
this.isListening = false;
|
||||
this.logger.info('Voice listening stopped');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update voice settings
|
||||
*/
|
||||
async updateSettings(settings: Partial<VoiceSettings>): Promise<void> {
|
||||
this.settings = { ...this.settings, ...settings };
|
||||
this.logger.info('Voice settings updated', { settings: this.settings });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get voice processor status
|
||||
*/
|
||||
async getStatus(): Promise<any> {
|
||||
return {
|
||||
enabled: this.settings.enabled,
|
||||
listening: this.isListening,
|
||||
settings: this.settings,
|
||||
lastUpdated: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
private mockSpeechRecognition(audioData: Buffer): string {
|
||||
// Mock speech recognition - in real implementation would use Azure Speech Services
|
||||
const mockResponses = [
|
||||
'Nowhere, analyze this code',
|
||||
'Create a new React component',
|
||||
'Search for documentation',
|
||||
'Enable autopilot mode',
|
||||
'What do you remember from our conversation?',
|
||||
'Run the tests and show me the results'
|
||||
];
|
||||
|
||||
// Use audio data hash to deterministically select a response
|
||||
const hash = this.simpleHash(audioData);
|
||||
const index = hash % mockResponses.length;
|
||||
|
||||
return mockResponses[index];
|
||||
}
|
||||
|
||||
private async analyzeVoiceIntent(text: string): Promise<any> {
|
||||
// Mock intent analysis - in real implementation would use NLP
|
||||
const intents = {
|
||||
'analyze': 'code_analysis',
|
||||
'create': 'file_operation',
|
||||
'search': 'web_search',
|
||||
'autopilot': 'autopilot_toggle',
|
||||
'remember': 'memory_query',
|
||||
'run': 'terminal_command',
|
||||
'test': 'terminal_command'
|
||||
};
|
||||
|
||||
const words = text.toLowerCase().split(' ');
|
||||
for (const word of words) {
|
||||
if (intents[word]) {
|
||||
return { type: intents[word], confidence: 0.9 };
|
||||
}
|
||||
}
|
||||
|
||||
return { type: 'general', confidence: 0.5 };
|
||||
}
|
||||
|
||||
private async extractVoiceEntities(text: string): Promise<any[]> {
|
||||
// Mock entity extraction - in real implementation would use NLP
|
||||
const entities: any[] = [];
|
||||
|
||||
// Extract file names
|
||||
const fileMatch = text.match(/(\w+\.\w+)/);
|
||||
if (fileMatch) {
|
||||
entities.push({
|
||||
type: 'file',
|
||||
value: fileMatch[1],
|
||||
confidence: 0.8
|
||||
});
|
||||
}
|
||||
|
||||
// Extract commands
|
||||
const commandMatch = text.match(/run\s+(.+)/i);
|
||||
if (commandMatch) {
|
||||
entities.push({
|
||||
type: 'command',
|
||||
value: commandMatch[1],
|
||||
confidence: 0.7
|
||||
});
|
||||
}
|
||||
|
||||
// Extract search queries
|
||||
const searchMatch = text.match(/search\s+(.+)/i);
|
||||
if (searchMatch) {
|
||||
entities.push({
|
||||
type: 'query',
|
||||
value: searchMatch[1],
|
||||
confidence: 0.8
|
||||
});
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
private async parseVoiceCommand(command: any): Promise<any> {
|
||||
// Convert voice command to executable action
|
||||
const { intent, entities } = command;
|
||||
|
||||
switch (intent) {
|
||||
case 'code_analysis':
|
||||
return {
|
||||
action: 'analyze_code',
|
||||
target: entities.find(e => e.type === 'file')?.value || 'current'
|
||||
};
|
||||
|
||||
case 'file_operation':
|
||||
return {
|
||||
action: 'create_file',
|
||||
target: entities.find(e => e.type === 'file')?.value || 'new_file'
|
||||
};
|
||||
|
||||
case 'web_search':
|
||||
return {
|
||||
action: 'search_web',
|
||||
query: entities.find(e => e.type === 'query')?.value || 'general'
|
||||
};
|
||||
|
||||
case 'autopilot_toggle':
|
||||
return {
|
||||
action: 'toggle_autopilot',
|
||||
enabled: true
|
||||
};
|
||||
|
||||
case 'memory_query':
|
||||
return {
|
||||
action: 'query_memory',
|
||||
query: 'recent interactions'
|
||||
};
|
||||
|
||||
case 'terminal_command':
|
||||
return {
|
||||
action: 'execute_command',
|
||||
command: entities.find(e => e.type === 'command')?.value || 'ls'
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
action: 'general_response',
|
||||
message: 'I heard your command, let me help you with that.'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private mockTextToSpeech(text: string, settings: VoiceSettings): Buffer {
|
||||
// Mock TTS - in real implementation would use Azure Speech Services
|
||||
// For now, return a mock audio buffer
|
||||
const mockAudio = Buffer.alloc(1024);
|
||||
mockAudio.fill(0); // Silent audio buffer
|
||||
|
||||
return mockAudio;
|
||||
}
|
||||
|
||||
private simpleHash(buffer: Buffer): number {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < Math.min(buffer.length, 100); i++) {
|
||||
hash = ((hash << 5) - hash) + buffer[i];
|
||||
hash = hash & hash; // Convert to 32-bit integer
|
||||
}
|
||||
return Math.abs(hash);
|
||||
}
|
||||
}
|
||||
275
Nowhere_AI_Agent/backend/src/websocket.ts
Normal file
275
Nowhere_AI_Agent/backend/src/websocket.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
import { Server as SocketIOServer, Socket } from 'socket.io';
|
||||
import { Logger } from './utils/logger';
|
||||
import { NowhereCore, CommandRequest, AIResponse } from './core/nowhere';
|
||||
|
||||
const logger = new Logger('WebSocket');
|
||||
|
||||
export function setupWebSocket(io: SocketIOServer) {
|
||||
const nowhere = new NowhereCore();
|
||||
|
||||
io.on('connection', (socket: Socket) => {
|
||||
logger.info('Client connected', {
|
||||
id: socket.id,
|
||||
ip: socket.handshake.address
|
||||
});
|
||||
|
||||
// Send welcome message
|
||||
socket.emit('welcome', {
|
||||
message: 'Welcome to Nowhere AI Agent!',
|
||||
timestamp: new Date().toISOString(),
|
||||
features: [
|
||||
'Voice Commands',
|
||||
'Autopilot Mode',
|
||||
'Memory System',
|
||||
'Real-time Communication'
|
||||
]
|
||||
});
|
||||
|
||||
// Handle text commands
|
||||
socket.on('command', async (data: any) => {
|
||||
try {
|
||||
logger.info('Processing WebSocket command', {
|
||||
socketId: socket.id,
|
||||
command: data.command?.substring(0, 100)
|
||||
});
|
||||
|
||||
const request: CommandRequest = {
|
||||
command: data.command,
|
||||
userId: data.userId || socket.id,
|
||||
context: data.context,
|
||||
autopilot: data.autopilot || false
|
||||
};
|
||||
|
||||
const response: AIResponse = await nowhere.processCommand(request);
|
||||
|
||||
socket.emit('command_response', response);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('WebSocket command error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Command processing failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle voice commands
|
||||
socket.on('voice_command', async (data: any) => {
|
||||
try {
|
||||
logger.info('Processing WebSocket voice command', {
|
||||
socketId: socket.id,
|
||||
audioSize: data.audioData?.length
|
||||
});
|
||||
|
||||
// Process voice input
|
||||
const voiceCommand = await nowhere['voice'].processVoiceInput(
|
||||
Buffer.from(data.audioData, 'base64')
|
||||
);
|
||||
|
||||
// Process the voice command
|
||||
const request: CommandRequest = {
|
||||
command: voiceCommand.text,
|
||||
userId: data.userId || socket.id,
|
||||
context: data.context,
|
||||
voice: true
|
||||
};
|
||||
|
||||
const response: AIResponse = await nowhere.processCommand(request);
|
||||
|
||||
socket.emit('voice_response', {
|
||||
...response,
|
||||
voiceCommand: voiceCommand.text
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('WebSocket voice command error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Voice command processing failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle autopilot toggle
|
||||
socket.on('toggle_autopilot', async (data: any) => {
|
||||
try {
|
||||
logger.info('Toggling autopilot via WebSocket', {
|
||||
socketId: socket.id,
|
||||
enabled: data.enabled
|
||||
});
|
||||
|
||||
const response = await nowhere.toggleAutopilot(data.enabled);
|
||||
|
||||
socket.emit('autopilot_response', response);
|
||||
|
||||
// Broadcast to all clients
|
||||
io.emit('autopilot_status', {
|
||||
enabled: data.enabled,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('WebSocket autopilot toggle error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Autopilot toggle failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle memory queries
|
||||
socket.on('query_memory', async (data: any) => {
|
||||
try {
|
||||
logger.info('Querying memory via WebSocket', {
|
||||
socketId: socket.id,
|
||||
query: data.query
|
||||
});
|
||||
|
||||
const memoryEntries = await nowhere['memory'].queryMemory(data.query);
|
||||
|
||||
socket.emit('memory_response', {
|
||||
success: true,
|
||||
data: memoryEntries
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('WebSocket memory query error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Memory query failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle status requests
|
||||
socket.on('get_status', async () => {
|
||||
try {
|
||||
logger.info('Getting status via WebSocket', { socketId: socket.id });
|
||||
|
||||
const status = await nowhere.getStatus();
|
||||
|
||||
socket.emit('status_response', {
|
||||
success: true,
|
||||
data: status
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('WebSocket status error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Status retrieval failed',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle voice listening
|
||||
socket.on('start_voice_listening', async () => {
|
||||
try {
|
||||
logger.info('Starting voice listening via WebSocket', { socketId: socket.id });
|
||||
|
||||
await nowhere['voice'].startListening();
|
||||
|
||||
socket.emit('voice_listening_started', {
|
||||
success: true,
|
||||
message: 'Voice listening started'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('WebSocket voice listening start error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Failed to start voice listening',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('stop_voice_listening', async () => {
|
||||
try {
|
||||
logger.info('Stopping voice listening via WebSocket', { socketId: socket.id });
|
||||
|
||||
await nowhere['voice'].stopListening();
|
||||
|
||||
socket.emit('voice_listening_stopped', {
|
||||
success: true,
|
||||
message: 'Voice listening stopped'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('WebSocket voice listening stop error', {
|
||||
socketId: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
socket.emit('error', {
|
||||
success: false,
|
||||
message: 'Failed to stop voice listening',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle ping/pong for connection health
|
||||
socket.on('ping', () => {
|
||||
socket.emit('pong', {
|
||||
timestamp: new Date().toISOString(),
|
||||
serverTime: Date.now()
|
||||
});
|
||||
});
|
||||
|
||||
// Handle disconnection
|
||||
socket.on('disconnect', (reason) => {
|
||||
logger.info('Client disconnected', {
|
||||
id: socket.id,
|
||||
reason
|
||||
});
|
||||
});
|
||||
|
||||
// Handle errors
|
||||
socket.on('error', (error) => {
|
||||
logger.error('Socket error', {
|
||||
id: socket.id,
|
||||
error: error.message
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Broadcast system events to all clients
|
||||
setInterval(() => {
|
||||
io.emit('heartbeat', {
|
||||
timestamp: new Date().toISOString(),
|
||||
activeConnections: io.engine.clientsCount
|
||||
});
|
||||
}, 30000); // Every 30 seconds
|
||||
|
||||
logger.info('WebSocket server initialized');
|
||||
}
|
||||
40
Nowhere_AI_Agent/backend/tsconfig.json
Normal file
40
Nowhere_AI_Agent/backend/tsconfig.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"ES2020"
|
||||
],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"noImplicitAny": false,
|
||||
"strictNullChecks": false,
|
||||
"strictFunctionTypes": false,
|
||||
"noImplicitReturns": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"**/*.test.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user