devmentorai-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # devmentorai-server
2
+
3
+ AI-powered DevOps mentoring and writing assistant backend, powered by [GitHub Copilot](https://github.com/features/copilot).
4
+
5
+ Part of the [DevMentorAI](https://github.com/BOTOOM/devmentorai) project.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npx devmentorai-server
11
+ ```
12
+
13
+ That's it! The server starts in the background on port **3847**.
14
+
15
+ ## Installation
16
+
17
+ ### Run without installing (recommended)
18
+
19
+ ```bash
20
+ npx devmentorai-server
21
+ ```
22
+
23
+ ### Install globally
24
+
25
+ ```bash
26
+ npm install -g devmentorai-server
27
+ devmentorai-server
28
+ ```
29
+
30
+ ## Commands
31
+
32
+ | Command | Description |
33
+ |---------|-------------|
34
+ | `devmentorai-server start` | Start the server in background (default) |
35
+ | `devmentorai-server stop` | Stop the running server |
36
+ | `devmentorai-server status` | Show server status (PID, port, health) |
37
+ | `devmentorai-server logs` | View recent server logs |
38
+ | `devmentorai-server doctor` | Check system requirements |
39
+
40
+ ## Options
41
+
42
+ | Option | Description |
43
+ |--------|-------------|
44
+ | `--port <port>` | Port to listen on (default: 3847) |
45
+ | `--foreground, -f` | Run in foreground (don't daemonize) |
46
+ | `--lines, -n` | Number of log lines to show (default: 50) |
47
+ | `--help, -h` | Show help message |
48
+ | `--version, -v` | Show version |
49
+
50
+ ## Examples
51
+
52
+ ```bash
53
+ # Start server on a custom port
54
+ devmentorai-server start --port 4000
55
+
56
+ # Run in foreground (useful for debugging)
57
+ devmentorai-server start --foreground
58
+
59
+ # Check if everything is set up correctly
60
+ devmentorai-server doctor
61
+
62
+ # View last 100 lines of logs
63
+ devmentorai-server logs --lines 100
64
+ ```
65
+
66
+ ## Requirements
67
+
68
+ - **Node.js** >= 20.0.0
69
+ - **GitHub Copilot CLI** (optional — server runs in mock mode without it)
70
+
71
+ ## How It Works
72
+
73
+ The server runs as a background process and stores its data in `~/.devmentorai/`:
74
+
75
+ ```
76
+ ~/.devmentorai/
77
+ ├── devmentorai.db # SQLite database (sessions, messages)
78
+ ├── server.pid # PID of running server
79
+ ├── config.json # User configuration
80
+ ├── logs/
81
+ │ └── server.log # Server logs
82
+ └── images/ # Session image thumbnails
83
+ ```
84
+
85
+ ### API Endpoints
86
+
87
+ | Endpoint | Description |
88
+ |----------|-------------|
89
+ | `GET /api/health` | Health check |
90
+ | `GET /api/sessions` | List sessions |
91
+ | `POST /api/sessions` | Create a new session |
92
+ | `POST /api/chat/:sessionId` | Send a message |
93
+ | `GET /api/models` | List available models |
94
+
95
+ ## Troubleshooting
96
+
97
+ ### Server won't start
98
+
99
+ 1. Run `devmentorai-server doctor` to check requirements
100
+ 2. Check logs: `devmentorai-server logs`
101
+ 3. Try foreground mode: `devmentorai-server start -f`
102
+
103
+ ### Port already in use
104
+
105
+ ```bash
106
+ devmentorai-server start --port 4000
107
+ ```
108
+
109
+ ### Copilot not connected
110
+
111
+ The server requires GitHub Copilot CLI to be installed and authenticated. Without it, the server runs in **mock mode** with simulated responses.
112
+
113
+ ```bash
114
+ # Install Copilot CLI (if not installed)
115
+ npm install -g @github/copilot-cli
116
+
117
+ # Authenticate
118
+ github-copilot auth
119
+ ```
120
+
121
+ ### Reset data
122
+
123
+ ```bash
124
+ rm -rf ~/.devmentorai
125
+ devmentorai-server start
126
+ ```
127
+
128
+ ## Development
129
+
130
+ This package is part of the [DevMentorAI monorepo](https://github.com/BOTOOM/devmentorai).
131
+
132
+ ```bash
133
+ git clone https://github.com/BOTOOM/devmentorai.git
134
+ cd devmentorai
135
+ pnpm install
136
+ pnpm dev:backend
137
+ ```
138
+
139
+ ## License
140
+
141
+ MIT © [BOTOOM](https://github.com/BOTOOM)
@@ -0,0 +1,241 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // ../../packages/shared/src/contracts/api.contracts.ts
9
+ var DEFAULT_CONFIG = {
10
+ DEFAULT_MODEL: "gpt-4.1",
11
+ DEFAULT_PORT: 3847,
12
+ DEFAULT_HOST: "localhost",
13
+ REQUEST_TIMEOUT_MS: 6e4,
14
+ STREAM_TIMEOUT_MS: 3e5
15
+ };
16
+
17
+ // ../../packages/shared/src/types/message.ts
18
+ var IMAGE_CONSTANTS = {
19
+ MAX_IMAGES_PER_MESSAGE: 5,
20
+ MAX_IMAGE_SIZE_BYTES: 5 * 1024 * 1024,
21
+ // 5MB
22
+ SUPPORTED_MIME_TYPES: ["image/png", "image/jpeg", "image/webp"],
23
+ THUMBNAIL_MAX_DIMENSION: 200,
24
+ THUMBNAIL_QUALITY: 60
25
+ };
26
+
27
+ // ../../packages/shared/src/types/context.ts
28
+ var CONTEXT_SIZE_LIMITS = {
29
+ visibleText: 1e4,
30
+ // chars
31
+ htmlSection: 500,
32
+ // chars per section
33
+ totalHTML: 5e3,
34
+ // chars total HTML
35
+ headings: 50,
36
+ // max headings
37
+ errors: 20,
38
+ // max errors
39
+ consoleLogs: 100,
40
+ // lines
41
+ screenshot: 1024 * 1024,
42
+ // 1MB
43
+ selectedText: 5e3
44
+ // chars
45
+ };
46
+
47
+ // ../../packages/shared/src/contracts/session-types.ts
48
+ var SESSION_TYPE_CONFIGS = {
49
+ devops: {
50
+ name: "DevOps Mentor",
51
+ description: "Expert in DevOps, cloud infrastructure, and best practices",
52
+ icon: "\u{1F6E0}\uFE0F",
53
+ defaultModel: "gpt-4.1",
54
+ agent: {
55
+ name: "devops-mentor",
56
+ displayName: "DevOps Mentor",
57
+ description: "Expert in DevOps, cloud infrastructure, and best practices",
58
+ prompt: `You are a DevOps mentor and expert. You help users with:
59
+ - AWS, Azure, GCP cloud services and best practices
60
+ - Kubernetes, Docker, and container orchestration
61
+ - CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, CircleCI)
62
+ - Infrastructure as Code (Terraform, Pulumi, CloudFormation, Ansible)
63
+ - Security best practices and compliance
64
+ - Cost optimization and resource management
65
+ - Error diagnosis, log analysis, and troubleshooting
66
+ - Architecture design and scalability patterns
67
+
68
+ When analyzing configurations or errors:
69
+ 1. Identify the issue or configuration clearly
70
+ 2. Explain WHY something is problematic or recommended
71
+ 3. Provide actionable steps to fix or improve
72
+ 4. Reference official documentation when helpful
73
+ 5. Warn about security implications when relevant
74
+
75
+ Be concise but thorough. Use code blocks for configurations and commands.`
76
+ }
77
+ },
78
+ writing: {
79
+ name: "Writing Assistant",
80
+ description: "Helps with writing, rewriting, and translation",
81
+ icon: "\u270D\uFE0F",
82
+ defaultModel: "gpt-4.1",
83
+ agent: {
84
+ name: "writing-assistant",
85
+ displayName: "Writing Assistant",
86
+ description: "Helps with writing, rewriting, and translation",
87
+ prompt: `You are a professional writing assistant. You help users with:
88
+ - Writing and composing emails (formal, casual, technical)
89
+ - Rewriting text with different tones and styles
90
+ - Grammar, spelling, and clarity improvements
91
+ - Translation between languages (preserving tone and meaning)
92
+ - Summarization and expansion of content
93
+ - Technical documentation writing
94
+ - Business communication and proposals
95
+
96
+ Guidelines:
97
+ 1. Maintain the original meaning and intent
98
+ 2. Match the requested tone (formal, casual, friendly, technical)
99
+ 3. Preserve formatting when rewriting
100
+ 4. For translations, keep cultural nuances in mind
101
+ 5. Provide alternatives when helpful
102
+ 6. Be concise unless expansion is requested
103
+
104
+ When the user provides text to modify, respond with ONLY the modified text unless they ask for explanation.`
105
+ }
106
+ },
107
+ development: {
108
+ name: "Development Helper",
109
+ description: "Assists with code review, debugging, and best practices",
110
+ icon: "\u{1F4BB}",
111
+ defaultModel: "gpt-4.1",
112
+ agent: {
113
+ name: "dev-helper",
114
+ displayName: "Development Helper",
115
+ description: "Assists with code review, debugging, and best practices",
116
+ prompt: `You are a senior software development assistant. You help users with:
117
+ - Code review and improvement suggestions
118
+ - Bug diagnosis and debugging strategies
119
+ - Architecture decisions and design patterns
120
+ - Performance optimization
121
+ - Testing strategies and test writing
122
+ - Documentation and code comments
123
+ - Refactoring and code cleanup
124
+
125
+ Guidelines:
126
+ 1. Be concise and actionable
127
+ 2. Explain the "why" behind suggestions
128
+ 3. Provide code examples when helpful
129
+ 4. Consider edge cases and error handling
130
+ 5. Suggest tests for critical changes
131
+ 6. Reference best practices and patterns
132
+
133
+ When reviewing code, focus on:
134
+ - Correctness and logic errors
135
+ - Security vulnerabilities
136
+ - Performance issues
137
+ - Maintainability and readability
138
+ - Missing error handling`
139
+ }
140
+ },
141
+ general: {
142
+ name: "General Assistant",
143
+ description: "General-purpose AI assistant",
144
+ icon: "\u{1F4AC}",
145
+ defaultModel: "gpt-4.1",
146
+ agent: null
147
+ // Uses default Copilot behavior
148
+ }
149
+ };
150
+ function getAgentConfig(type) {
151
+ return SESSION_TYPE_CONFIGS[type]?.agent ?? null;
152
+ }
153
+ function getDefaultModel(type) {
154
+ return SESSION_TYPE_CONFIGS[type]?.defaultModel ?? "gpt-4.1";
155
+ }
156
+
157
+ // ../../packages/shared/src/utils/helpers.ts
158
+ function generateId(prefix) {
159
+ const timestamp = Date.now().toString(36);
160
+ const random = Math.random().toString(36).substring(2, 9);
161
+ return prefix ? `${prefix}_${timestamp}${random}` : `${timestamp}${random}`;
162
+ }
163
+ function generateSessionId() {
164
+ return generateId("session");
165
+ }
166
+ function generateMessageId() {
167
+ return generateId("msg");
168
+ }
169
+ function formatDate(date = /* @__PURE__ */ new Date()) {
170
+ return date.toISOString();
171
+ }
172
+
173
+ // src/lib/paths.ts
174
+ import path from "path";
175
+ import os from "os";
176
+ import fs from "fs";
177
+ var DATA_DIR = path.join(os.homedir(), ".devmentorai");
178
+ var IMAGES_DIR = path.join(DATA_DIR, "images");
179
+ var LOG_DIR = path.join(DATA_DIR, "logs");
180
+ var LOG_FILE = path.join(LOG_DIR, "server.log");
181
+ var PID_FILE = path.join(DATA_DIR, "server.pid");
182
+ var CONFIG_FILE = path.join(DATA_DIR, "config.json");
183
+ function ensureDir(dirPath) {
184
+ if (!fs.existsSync(dirPath)) {
185
+ fs.mkdirSync(dirPath, { recursive: true });
186
+ }
187
+ }
188
+ function getSessionImagesDir(sessionId) {
189
+ return path.join(IMAGES_DIR, sessionId);
190
+ }
191
+ function getMessageImagesDir(sessionId, messageId) {
192
+ return path.join(IMAGES_DIR, sessionId, messageId);
193
+ }
194
+ function getThumbnailPath(sessionId, messageId, index) {
195
+ return path.join(getMessageImagesDir(sessionId, messageId), `thumb_${index}.jpg`);
196
+ }
197
+ function toRelativePath(absolutePath) {
198
+ return path.relative(DATA_DIR, absolutePath);
199
+ }
200
+ function toImageRelativePath(absolutePath) {
201
+ return path.relative(IMAGES_DIR, absolutePath);
202
+ }
203
+ function toUrlPath(fsPath) {
204
+ return fsPath.split(path.sep).join("/");
205
+ }
206
+ function deleteDir(dirPath) {
207
+ if (fs.existsSync(dirPath)) {
208
+ fs.rmSync(dirPath, { recursive: true, force: true });
209
+ }
210
+ }
211
+ function fileExists(filePath) {
212
+ return fs.existsSync(filePath);
213
+ }
214
+ ensureDir(IMAGES_DIR);
215
+ ensureDir(LOG_DIR);
216
+
217
+ export {
218
+ __require,
219
+ DATA_DIR,
220
+ IMAGES_DIR,
221
+ LOG_DIR,
222
+ LOG_FILE,
223
+ PID_FILE,
224
+ ensureDir,
225
+ getSessionImagesDir,
226
+ getMessageImagesDir,
227
+ getThumbnailPath,
228
+ toRelativePath,
229
+ toImageRelativePath,
230
+ toUrlPath,
231
+ deleteDir,
232
+ fileExists,
233
+ DEFAULT_CONFIG,
234
+ SESSION_TYPE_CONFIGS,
235
+ getAgentConfig,
236
+ getDefaultModel,
237
+ generateSessionId,
238
+ generateMessageId,
239
+ formatDate
240
+ };
241
+ //# sourceMappingURL=chunk-QFZAYHDT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../packages/shared/src/contracts/api.contracts.ts","../../../packages/shared/src/types/message.ts","../../../packages/shared/src/types/context.ts","../../../packages/shared/src/contracts/session-types.ts","../../../packages/shared/src/utils/helpers.ts","../src/lib/paths.ts"],"sourcesContent":["/**\n * API contract definitions for DevMentorAI\n * These define the endpoints and their request/response types\n */\n\nimport type {\n Session,\n CreateSessionRequest,\n UpdateSessionRequest,\n Message,\n SendMessageRequest,\n ApiResponse,\n PaginatedResponse,\n HealthResponse,\n ModelInfo,\n} from '../types/index.js';\n\n/**\n * Backend API endpoints contract\n */\nexport const API_ENDPOINTS = {\n // Health\n HEALTH: '/api/health',\n \n // Sessions\n SESSIONS: '/api/sessions',\n SESSION: (id: string) => `/api/sessions/${id}`,\n SESSION_RESUME: (id: string) => `/api/sessions/${id}/resume`,\n SESSION_ABORT: (id: string) => `/api/sessions/${id}/abort`,\n SESSION_MESSAGES: (id: string) => `/api/sessions/${id}/messages`,\n \n // Chat\n CHAT: (sessionId: string) => `/api/sessions/${sessionId}/chat`,\n CHAT_STREAM: (sessionId: string) => `/api/sessions/${sessionId}/chat/stream`,\n \n // Models\n MODELS: '/api/models',\n} as const;\n\n/**\n * API endpoint type definitions\n */\nexport interface ApiEndpoints {\n // GET /api/health\n 'GET /api/health': {\n response: ApiResponse<HealthResponse>;\n };\n \n // GET /api/sessions\n 'GET /api/sessions': {\n response: ApiResponse<PaginatedResponse<Session>>;\n };\n \n // POST /api/sessions\n 'POST /api/sessions': {\n body: CreateSessionRequest;\n response: ApiResponse<Session>;\n };\n \n // GET /api/sessions/:id\n 'GET /api/sessions/:id': {\n params: { id: string };\n response: ApiResponse<Session>;\n };\n \n // PATCH /api/sessions/:id\n 'PATCH /api/sessions/:id': {\n params: { id: string };\n body: UpdateSessionRequest;\n response: ApiResponse<Session>;\n };\n \n // DELETE /api/sessions/:id\n 'DELETE /api/sessions/:id': {\n params: { id: string };\n response: ApiResponse<void>;\n };\n \n // POST /api/sessions/:id/resume\n 'POST /api/sessions/:id/resume': {\n params: { id: string };\n response: ApiResponse<Session>;\n };\n \n // POST /api/sessions/:id/abort\n 'POST /api/sessions/:id/abort': {\n params: { id: string };\n response: ApiResponse<void>;\n };\n \n // GET /api/sessions/:id/messages\n 'GET /api/sessions/:id/messages': {\n params: { id: string };\n response: ApiResponse<PaginatedResponse<Message>>;\n };\n \n // POST /api/sessions/:id/chat\n 'POST /api/sessions/:id/chat': {\n params: { id: string };\n body: SendMessageRequest;\n response: ApiResponse<Message>;\n };\n \n // POST /api/sessions/:id/chat/stream (SSE)\n 'POST /api/sessions/:id/chat/stream': {\n params: { id: string };\n body: SendMessageRequest;\n response: ReadableStream; // SSE stream\n };\n \n // GET /api/models\n 'GET /api/models': {\n response: ApiResponse<ModelInfo[]>;\n };\n}\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG = {\n DEFAULT_MODEL: 'gpt-4.1',\n DEFAULT_PORT: 3847,\n DEFAULT_HOST: 'localhost',\n REQUEST_TIMEOUT_MS: 60000,\n STREAM_TIMEOUT_MS: 300000,\n} as const;\n","/**\n * Message type definitions for DevMentorAI\n */\n\nexport type MessageRole = 'user' | 'assistant' | 'system';\n\nexport interface Message {\n id: string;\n sessionId: string;\n role: MessageRole;\n content: string;\n timestamp: string; // ISO date string\n metadata?: MessageMetadata;\n}\n\n// ============================================================================\n// Image Attachment Types\n// ============================================================================\n\nexport type ImageSource = 'screenshot' | 'paste' | 'drop';\nexport type ImageMimeType = 'image/png' | 'image/jpeg' | 'image/webp';\n\nexport interface ImageAttachment {\n id: string;\n source: ImageSource;\n mimeType: ImageMimeType;\n dimensions: { width: number; height: number };\n fileSize: number; // bytes\n timestamp: string; // ISO date string\n /** Original base64 data URL - only present in draft/request, not persisted */\n dataUrl?: string;\n /** Backend-provided thumbnail URL for display in chat history */\n thumbnailUrl?: string;\n /** Backend-provided full image URL for lightbox view */\n fullImageUrl?: string;\n}\n\n/** Image data sent in message request (before processing) */\nexport interface ImagePayload {\n id: string;\n dataUrl: string;\n mimeType: ImageMimeType;\n source: ImageSource;\n}\n\n/** Constants for image handling */\nexport const IMAGE_CONSTANTS = {\n MAX_IMAGES_PER_MESSAGE: 5,\n MAX_IMAGE_SIZE_BYTES: 5 * 1024 * 1024, // 5MB\n SUPPORTED_MIME_TYPES: ['image/png', 'image/jpeg', 'image/webp'] as const,\n THUMBNAIL_MAX_DIMENSION: 200,\n THUMBNAIL_QUALITY: 60,\n} as const;\n\n// ============================================================================\n// Message Metadata\n// ============================================================================\n\nexport interface MessageMetadata {\n pageUrl?: string;\n selectedText?: string;\n action?: QuickAction;\n toolCalls?: ToolCall[];\n streamComplete?: boolean;\n /** Attached images for this message */\n images?: ImageAttachment[];\n /** Whether context-aware mode was used */\n contextAware?: boolean;\n}\n\nexport type QuickAction =\n | 'explain'\n | 'translate'\n | 'rewrite'\n | 'fix_grammar'\n | 'summarize'\n | 'expand'\n | 'analyze_config'\n | 'diagnose_error';\n\nexport interface ToolCall {\n toolName: string;\n toolCallId: string;\n status: 'pending' | 'running' | 'completed' | 'error';\n result?: unknown;\n}\n\nexport interface SendMessageRequest {\n prompt: string;\n context?: MessageContext;\n /** Full extracted context payload for context-aware mode */\n fullContext?: unknown; // ContextPayload - imported separately to avoid circular deps\n /** Whether to use context-aware mode */\n useContextAwareMode?: boolean;\n /** Images to attach to this message */\n images?: ImagePayload[];\n}\n\nexport interface MessageContext {\n pageUrl?: string;\n pageTitle?: string;\n selectedText?: string;\n action?: QuickAction;\n}\n\nexport interface StreamEvent {\n type: StreamEventType;\n data: StreamEventData;\n}\n\nexport type StreamEventType =\n | 'message_start'\n | 'message_delta'\n | 'message_complete'\n | 'tool_start'\n | 'tool_complete'\n | 'error'\n | 'done';\n\nexport interface StreamEventData {\n content?: string;\n deltaContent?: string;\n toolName?: string;\n toolCallId?: string;\n error?: string;\n messageId?: string;\n reason?: string; // Reason for completion (e.g., 'completed', 'timeout', 'idle_timeout')\n}\n","/**\n * Context-Aware Mentor Mode Types\n * \n * These types define the structured context payload that is extracted from\n * the browser and sent to the backend for context-aware AI responses.\n */\n\n// ============================================================================\n// Platform Detection Types\n// ============================================================================\n\nexport type PlatformType =\n | 'azure'\n | 'aws'\n | 'gcp'\n | 'github'\n | 'gitlab'\n | 'datadog'\n | 'newrelic'\n | 'grafana'\n | 'jenkins'\n | 'kubernetes'\n | 'docker'\n | 'generic';\n\nexport interface PlatformDetection {\n type: PlatformType;\n confidence: number; // 0-1\n indicators: string[];\n specificProduct?: string; // e.g., \"Azure DevOps\", \"AWS EC2\"\n specificContext?: Record<string, unknown>; // Phase 2: Platform-specific context\n}\n\n// ============================================================================\n// Error Detection Types\n// ============================================================================\n\nexport type ErrorType = 'error' | 'warning' | 'info';\nexport type ErrorSeverity = 'critical' | 'high' | 'medium' | 'low';\nexport type ErrorSource = 'console' | 'ui' | 'network' | 'dom';\n\nexport interface ExtractedError {\n type: ErrorType;\n message: string;\n source?: ErrorSource;\n severity: ErrorSeverity;\n context?: string; // Surrounding text\n element?: HTMLElementSnapshot;\n stackTrace?: string;\n}\n\nexport interface HTMLElementSnapshot {\n tagName: string;\n id?: string;\n className?: string;\n textContent?: string;\n attributes: Record<string, string>;\n}\n\n// ============================================================================\n// HTML Structure Types\n// ============================================================================\n\nexport type SectionPurpose =\n | 'error-container'\n | 'alert'\n | 'panel'\n | 'table'\n | 'form'\n | 'code-block'\n | 'modal'\n | 'generic';\n\nexport interface HTMLSection {\n purpose: SectionPurpose;\n outerHTML: string; // Truncated to ~500 chars\n textContent: string;\n attributes: Record<string, string>;\n xpath?: string;\n}\n\n// ============================================================================\n// Text Extraction Types\n// ============================================================================\n\nexport interface Heading {\n level: 1 | 2 | 3;\n text: string;\n xpath?: string;\n hierarchy?: string; // Parent > Child hierarchy\n}\n\nexport interface ConsoleLogs {\n errors: string[];\n warnings: string[];\n included: boolean;\n truncated: boolean;\n}\n\n// Phase 2: Console log capture type\nexport interface CapturedConsoleLog {\n level: 'log' | 'warn' | 'error' | 'info' | 'debug';\n message: string;\n timestamp: string;\n stackTrace?: string;\n}\n\n// Phase 2: Network error capture type\nexport interface CapturedNetworkError {\n url: string;\n method: string;\n status?: number;\n statusText?: string;\n errorMessage?: string;\n timestamp: string;\n}\n\nexport interface TextExtractionMetadata {\n totalLength: number;\n truncated: boolean;\n truncationReason?: string;\n}\n\n// ============================================================================\n// User Intent Types (DEPRECATED in Phase 3 - kept for backward compatibility)\n// ============================================================================\n\n/**\n * @deprecated Phase 3: The agent (LLM) now determines intent autonomously.\n * This type is kept for backward compatibility but fields are optional.\n */\nexport type IntentType = 'debug' | 'understand' | 'mentor' | 'help' | 'explain' | 'guide';\n\n/**\n * @deprecated Phase 3: Intent is no longer computed by the extension.\n * The agent interprets user intent directly from their message.\n */\nexport interface UserIntent {\n primary: IntentType;\n keywords: string[];\n explicitGoal?: string;\n implicitSignals: string[];\n}\n\n// ============================================================================\n// Session Context Types\n// ============================================================================\n\n/** Session type specifically for context-aware mode (extends base SessionType) */\nexport type ContextSessionType = 'devops' | 'debugging' | 'frontend' | 'general' | 'writing';\n\nexport interface ContextMessage {\n role: 'user' | 'assistant';\n content: string;\n timestamp: string;\n}\n\n// ============================================================================\n// Visual Context Types (Phase 3)\n// ============================================================================\n\nexport interface ScreenshotData {\n dataUrl: string; // base64 encoded image\n format: 'png' | 'jpeg';\n dimensions: { width: number; height: number };\n fileSize: number; // bytes\n quality?: number; // 0-100 for JPEG\n}\n\nexport interface VisualContext {\n screenshot?: ScreenshotData;\n supported: boolean; // Model supports vision\n included: boolean; // Screenshot actually captured\n reason?: string; // Why included/excluded\n}\n\n// ============================================================================\n// Privacy Types\n// ============================================================================\n\nexport interface PrivacyInfo {\n redactedFields: string[];\n sensitiveDataDetected: boolean;\n consentGiven: boolean;\n dataRetention: 'session' | 'none';\n privacyMaskingApplied?: boolean; // Whether privacy masking was applied\n sensitiveDataTypes?: string[]; // Types of data that were masked (e.g., 'email', 'token')\n}\n\n// ============================================================================\n// Main Context Payload\n// ============================================================================\n\nexport interface ContextPayload {\n // Core Metadata\n metadata: {\n captureTimestamp: string; // ISO 8601\n captureMode: 'auto' | 'manual';\n browserInfo: {\n userAgent: string;\n viewport: { width: number; height: number };\n language: string;\n };\n // Phase 2: Extended metadata\n phase2Features?: {\n consoleLogs: number;\n networkErrors: number;\n codeBlocks: number;\n tables: number;\n forms: number;\n hasModal: boolean;\n };\n };\n\n // Page Identity\n page: {\n url: string;\n urlParsed: {\n protocol: string;\n hostname: string;\n pathname: string;\n search: string;\n hash: string;\n };\n title: string;\n favicon?: string;\n platform: PlatformDetection;\n };\n\n // Textual Context\n text: {\n selectedText?: string;\n visibleText: string;\n headings: Heading[];\n errors: ExtractedError[];\n logs?: ConsoleLogs;\n // Phase 2: Console and network error capture\n consoleLogs?: CapturedConsoleLog[];\n networkErrors?: CapturedNetworkError[];\n metadata: TextExtractionMetadata;\n };\n\n // Structural HTML Context\n structure: {\n relevantSections: HTMLSection[];\n errorContainers: HTMLSection[];\n // Phase 2: Extended structure extraction\n codeBlocks?: HTMLSection[];\n tables?: HTMLSection[];\n forms?: HTMLSection[];\n modal?: HTMLSection;\n activeElements: {\n focusedElement?: HTMLElementSnapshot;\n activeModals?: HTMLSection[];\n activePanels?: HTMLSection[];\n };\n metadata: {\n totalNodes: number;\n extractedNodes: number;\n relevanceScore: number; // 0-1 confidence\n };\n };\n\n // Visual Context (Optional - Phase 3)\n visual?: VisualContext;\n\n // Session Context\n session: {\n sessionId: string;\n sessionType: ContextSessionType;\n intent: UserIntent;\n previousMessages: {\n count: number;\n lastN: ContextMessage[];\n };\n userGoal?: string;\n };\n\n // Privacy & Safety\n privacy: PrivacyInfo;\n}\n\n// ============================================================================\n// Extraction Request/Response Types\n// ============================================================================\n\nexport interface ContextExtractionRequest {\n includeScreenshot?: boolean;\n maxTextLength?: number;\n maxHTMLSections?: number;\n selectedTextOnly?: boolean;\n}\n\nexport interface ContextExtractionResponse {\n success: boolean;\n context?: ContextPayload;\n error?: string;\n extractionTimeMs: number;\n}\n\n// ============================================================================\n// Size Limits Configuration\n// ============================================================================\n\nexport const CONTEXT_SIZE_LIMITS = {\n visibleText: 10000, // chars\n htmlSection: 500, // chars per section\n totalHTML: 5000, // chars total HTML\n headings: 50, // max headings\n errors: 20, // max errors\n consoleLogs: 100, // lines\n screenshot: 1024 * 1024, // 1MB\n selectedText: 5000, // chars\n} as const;\n","/**\n * Session type configurations with pre-defined agents\n */\n\nimport type { SessionType } from '../types/session.js';\n\nexport interface AgentConfig {\n name: string;\n displayName: string;\n description: string;\n prompt: string;\n}\n\nexport interface SessionTypeConfig {\n name: string;\n description: string;\n icon: string;\n agent: AgentConfig | null;\n defaultModel: string;\n}\n\nexport const SESSION_TYPE_CONFIGS: Record<SessionType, SessionTypeConfig> = {\n devops: {\n name: 'DevOps Mentor',\n description: 'Expert in DevOps, cloud infrastructure, and best practices',\n icon: '🛠️',\n defaultModel: 'gpt-4.1',\n agent: {\n name: 'devops-mentor',\n displayName: 'DevOps Mentor',\n description: 'Expert in DevOps, cloud infrastructure, and best practices',\n prompt: `You are a DevOps mentor and expert. You help users with:\n- AWS, Azure, GCP cloud services and best practices\n- Kubernetes, Docker, and container orchestration\n- CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, CircleCI)\n- Infrastructure as Code (Terraform, Pulumi, CloudFormation, Ansible)\n- Security best practices and compliance\n- Cost optimization and resource management\n- Error diagnosis, log analysis, and troubleshooting\n- Architecture design and scalability patterns\n\nWhen analyzing configurations or errors:\n1. Identify the issue or configuration clearly\n2. Explain WHY something is problematic or recommended\n3. Provide actionable steps to fix or improve\n4. Reference official documentation when helpful\n5. Warn about security implications when relevant\n\nBe concise but thorough. Use code blocks for configurations and commands.`,\n },\n },\n \n writing: {\n name: 'Writing Assistant',\n description: 'Helps with writing, rewriting, and translation',\n icon: '✍️',\n defaultModel: 'gpt-4.1',\n agent: {\n name: 'writing-assistant',\n displayName: 'Writing Assistant',\n description: 'Helps with writing, rewriting, and translation',\n prompt: `You are a professional writing assistant. You help users with:\n- Writing and composing emails (formal, casual, technical)\n- Rewriting text with different tones and styles\n- Grammar, spelling, and clarity improvements\n- Translation between languages (preserving tone and meaning)\n- Summarization and expansion of content\n- Technical documentation writing\n- Business communication and proposals\n\nGuidelines:\n1. Maintain the original meaning and intent\n2. Match the requested tone (formal, casual, friendly, technical)\n3. Preserve formatting when rewriting\n4. For translations, keep cultural nuances in mind\n5. Provide alternatives when helpful\n6. Be concise unless expansion is requested\n\nWhen the user provides text to modify, respond with ONLY the modified text unless they ask for explanation.`,\n },\n },\n \n development: {\n name: 'Development Helper',\n description: 'Assists with code review, debugging, and best practices',\n icon: '💻',\n defaultModel: 'gpt-4.1',\n agent: {\n name: 'dev-helper',\n displayName: 'Development Helper',\n description: 'Assists with code review, debugging, and best practices',\n prompt: `You are a senior software development assistant. You help users with:\n- Code review and improvement suggestions\n- Bug diagnosis and debugging strategies\n- Architecture decisions and design patterns\n- Performance optimization\n- Testing strategies and test writing\n- Documentation and code comments\n- Refactoring and code cleanup\n\nGuidelines:\n1. Be concise and actionable\n2. Explain the \"why\" behind suggestions\n3. Provide code examples when helpful\n4. Consider edge cases and error handling\n5. Suggest tests for critical changes\n6. Reference best practices and patterns\n\nWhen reviewing code, focus on:\n- Correctness and logic errors\n- Security vulnerabilities\n- Performance issues\n- Maintainability and readability\n- Missing error handling`,\n },\n },\n \n general: {\n name: 'General Assistant',\n description: 'General-purpose AI assistant',\n icon: '💬',\n defaultModel: 'gpt-4.1',\n agent: null, // Uses default Copilot behavior\n },\n};\n\n/**\n * Get the agent config for a session type\n */\nexport function getAgentConfig(type: SessionType): AgentConfig | null {\n return SESSION_TYPE_CONFIGS[type]?.agent ?? null;\n}\n\n/**\n * Get the default model for a session type\n */\nexport function getDefaultModel(type: SessionType): string {\n return SESSION_TYPE_CONFIGS[type]?.defaultModel ?? 'gpt-4.1';\n}\n","/**\n * Utility functions for DevMentorAI\n */\n\n/**\n * Generate a unique ID\n */\nexport function generateId(prefix?: string): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 9);\n return prefix ? `${prefix}_${timestamp}${random}` : `${timestamp}${random}`;\n}\n\n/**\n * Generate a session ID\n */\nexport function generateSessionId(): string {\n return generateId('session');\n}\n\n/**\n * Generate a message ID\n */\nexport function generateMessageId(): string {\n return generateId('msg');\n}\n\n/**\n * Format a date as ISO string\n */\nexport function formatDate(date: Date = new Date()): string {\n return date.toISOString();\n}\n\n/**\n * Parse an ISO date string to Date\n */\nexport function parseDate(dateString: string): Date {\n return new Date(dateString);\n}\n\n/**\n * Truncate text to a maximum length\n */\nexport function truncate(text: string, maxLength: number, suffix = '...'): string {\n if (text.length <= maxLength) return text;\n return text.slice(0, maxLength - suffix.length) + suffix;\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Retry a function with exponential backoff\n */\nexport async function retry<T>(\n fn: () => Promise<T>,\n options: {\n maxAttempts?: number;\n initialDelay?: number;\n maxDelay?: number;\n backoffFactor?: number;\n } = {}\n): Promise<T> {\n const {\n maxAttempts = 3,\n initialDelay = 1000,\n maxDelay = 30000,\n backoffFactor = 2,\n } = options;\n \n let lastError: Error | undefined;\n let delay = initialDelay;\n \n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n \n if (attempt === maxAttempts) break;\n \n await sleep(delay);\n delay = Math.min(delay * backoffFactor, maxDelay);\n }\n }\n \n throw lastError;\n}\n\n/**\n * Create an AbortController with timeout\n */\nexport function createTimeoutController(timeoutMs: number): AbortController {\n const controller = new AbortController();\n setTimeout(() => controller.abort(), timeoutMs);\n return controller;\n}\n","/**\n * Cross-Platform Path Utilities\n * \n * Provides consistent path handling across Windows, macOS, and Linux.\n * Uses the same pattern as db/index.ts for data directory location.\n */\n\nimport path from 'node:path';\nimport os from 'node:os';\nimport fs from 'node:fs';\n\n/** Base data directory: ~/.devmentorai */\nexport const DATA_DIR = path.join(os.homedir(), '.devmentorai');\n\n/** Images directory: ~/.devmentorai/images */\nexport const IMAGES_DIR = path.join(DATA_DIR, 'images');\n\n/** Logs directory: ~/.devmentorai/logs */\nexport const LOG_DIR = path.join(DATA_DIR, 'logs');\n\n/** Server log file: ~/.devmentorai/logs/server.log */\nexport const LOG_FILE = path.join(LOG_DIR, 'server.log');\n\n/** PID file: ~/.devmentorai/server.pid */\nexport const PID_FILE = path.join(DATA_DIR, 'server.pid');\n\n/** Config file: ~/.devmentorai/config.json */\nexport const CONFIG_FILE = path.join(DATA_DIR, 'config.json');\n\n/**\n * Ensure a directory exists, creating it if necessary\n */\nexport function ensureDir(dirPath: string): void {\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true });\n }\n}\n\n/**\n * Get the directory path for a session's images\n * @param sessionId - The session ID\n * @returns Absolute path to the session's images directory\n */\nexport function getSessionImagesDir(sessionId: string): string {\n return path.join(IMAGES_DIR, sessionId);\n}\n\n/**\n * Get the directory path for a message's images\n * @param sessionId - The session ID\n * @param messageId - The message ID\n * @returns Absolute path to the message's images directory\n */\nexport function getMessageImagesDir(sessionId: string, messageId: string): string {\n return path.join(IMAGES_DIR, sessionId, messageId);\n}\n\n/**\n * Get the file path for a thumbnail image\n * @param sessionId - The session ID\n * @param messageId - The message ID\n * @param index - The image index (0-based)\n * @returns Absolute path to the thumbnail file\n */\nexport function getThumbnailPath(sessionId: string, messageId: string, index: number): string {\n return path.join(getMessageImagesDir(sessionId, messageId), `thumb_${index}.jpg`);\n}\n\n/**\n * Convert an absolute path to a relative path from DATA_DIR\n * This is used for storing portable paths in the database\n * @param absolutePath - The absolute file path\n * @returns Relative path from DATA_DIR\n */\nexport function toRelativePath(absolutePath: string): string {\n return path.relative(DATA_DIR, absolutePath);\n}\n\n/**\n * Convert an absolute image path to a relative path from IMAGES_DIR\n * This is used for constructing image URLs\n * @param absolutePath - The absolute file path\n * @returns Relative path from IMAGES_DIR (e.g., \"session_xxx/msg_xxx/thumb_0.jpg\")\n */\nexport function toImageRelativePath(absolutePath: string): string {\n return path.relative(IMAGES_DIR, absolutePath);\n}\n\n/**\n * Convert a relative path (from DB) to an absolute path\n * @param relativePath - The relative path stored in DB\n * @returns Absolute file path\n */\nexport function toAbsolutePath(relativePath: string): string {\n return path.join(DATA_DIR, relativePath);\n}\n\n/**\n * Convert a file system path to a URL-safe path (always forward slashes)\n * @param fsPath - The file system path\n * @returns URL-safe path with forward slashes\n */\nexport function toUrlPath(fsPath: string): string {\n return fsPath.split(path.sep).join('/');\n}\n\n/**\n * Delete a directory and all its contents\n * @param dirPath - The directory to delete\n */\nexport function deleteDir(dirPath: string): void {\n if (fs.existsSync(dirPath)) {\n fs.rmSync(dirPath, { recursive: true, force: true });\n }\n}\n\n/**\n * Check if a file exists\n * @param filePath - The file path to check\n * @returns True if the file exists\n */\nexport function fileExists(filePath: string): boolean {\n return fs.existsSync(filePath);\n}\n\n/**\n * Get file stats if the file exists\n * @param filePath - The file path\n * @returns File stats or null if file doesn't exist\n */\nexport function getFileStats(filePath: string): fs.Stats | null {\n try {\n return fs.statSync(filePath);\n } catch {\n return null;\n }\n}\n\n// Initialize directories on module load\nensureDir(IMAGES_DIR);\nensureDir(LOG_DIR);\n"],"mappings":"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","names":[]}