matex-cli 1.2.84 → 1.2.87

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.
@@ -1,507 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.commandHistory = exports.CommandHistory = void 0;
40
- exports.extractCommands = extractCommands;
41
- exports.isSafeAutoCommand = isSafeAutoCommand;
42
- exports.askPermission = askPermission;
43
- exports.executeCommand = executeCommand;
44
- exports.executeWithPermission = executeWithPermission;
45
- const fs = __importStar(require("fs"));
46
- const path = __importStar(require("path"));
47
- const chalk_1 = __importDefault(require("chalk"));
48
- const child_process_1 = require("child_process");
49
- const util_1 = require("util");
50
- const inquirer_1 = __importDefault(require("inquirer"));
51
- const tui_1 = require("./tui");
52
- const agent_orchestrator_1 = require("./agent-orchestrator");
53
- const patcher_1 = require("./patcher");
54
- const execAsync = (0, util_1.promisify)(child_process_1.exec);
55
- /**
56
- * Extract executable commands from AI response
57
- */
58
- function extractCommands(response) {
59
- const commands = [];
60
- // Match code blocks with language tags (case-insensitive)
61
- const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/gi;
62
- let match;
63
- while ((match = codeBlockRegex.exec(response)) !== null) {
64
- const language = match[1] || 'bash';
65
- const code = match[2].trim();
66
- // 🛡️ HEURISTIC: Reject directory trees formatted as "commands"
67
- const isTree = /├──|└──|│\s+├──/.test(code);
68
- if (isTree)
69
- continue;
70
- // Only extract shell commands
71
- if (['bash', 'sh', 'zsh', 'shell', 'powershell', 'cmd'].includes(language.toLowerCase())) {
72
- // 🛡️ DIALOGUE FILTER: Strip lines that look like agent banter [Ajay Vai]: etc.
73
- const lines = code.split('\n');
74
- const filteredLines = lines.filter(line => {
75
- const trimmed = line.trim();
76
- // If it starts with [Agent Name] or typical agent markers, it's NOT a command
77
- const isAgent = /^(?:\[\**\s*|\b)(Ajay|Sunil|Sandip|Bishal|Narayan|Big Bro)\s*(?:Vai|Dai)?\s*\**\]?[:\s]*/i.test(trimmed);
78
- return !isAgent;
79
- });
80
- const filteredCode = filteredLines.join('\n').trim();
81
- if (filteredCode) {
82
- commands.push({
83
- language,
84
- code: filteredCode,
85
- dangerous: isDangerousCommand(filteredCode)
86
- });
87
- }
88
- }
89
- }
90
- return commands;
91
- }
92
- /**
93
- * Check if command is potentially dangerous
94
- */
95
- function isDangerousCommand(command) {
96
- const dangerousPatterns = [
97
- /rm\s+-rf\s+\//, // rm -rf /
98
- /rm\s+-rf\s+~/, // rm -rf ~
99
- /rm\s+-rf\s+\*/, // rm -rf *
100
- /:\(\)\{.*\}/, // Fork bomb
101
- /dd\s+if=.*of=\/dev/, // Disk operations
102
- /mkfs/, // Format filesystem
103
- /fdisk/, // Partition operations
104
- />\s*\/dev\/sd/, // Write to disk
105
- /chmod\s+-R\s+777/, // Dangerous permissions
106
- /curl.*\|\s*bash/, // Pipe to bash
107
- /wget.*\|\s*sh/, // Pipe to shell
108
- /sudo\s+rm/, // Sudo remove
109
- /format\s+[A-Z]:/, // Windows format
110
- /del\s+\/[FS]/, // Windows delete
111
- ];
112
- return dangerousPatterns.some(pattern => pattern.test(command));
113
- }
114
- /**
115
- * Check if command is safe to auto-execute
116
- */
117
- function isSafeAutoCommand(command) {
118
- const safePrefixes = [
119
- // File Discovery & Read
120
- 'cat ', 'ls ', 'grep ', 'find ', 'pwd', 'echo ', 'read ', 'type ', 'dir',
121
- // File Operations
122
- 'mkdir ', 'touch ', 'cp ', 'mv ', 'rm ', 'del ', 'copy ', 'move ',
123
- // Dev Tools
124
- 'npm ', 'npx ', 'git ', 'node ', 'python ', 'python3 ', 'pip ', 'pip3 ',
125
- // Cloud & Mobile
126
- 'firebase ', 'gcloud ', 'docker ', 'flutter ', 'react-native ',
127
- // Build & Package
128
- 'cd ', 'tsc ', 'vite ', 'next ', 'cargo ', 'go ', 'swift '
129
- ];
130
- const trimmed = command.trim();
131
- return safePrefixes.some(prefix => trimmed.startsWith(prefix));
132
- }
133
- /**
134
- * Ask user for permission to execute command
135
- */
136
- async function askPermission(command) {
137
- // Clean Codex-style Prompt with Multi-Agent Iconography
138
- console.log();
139
- // Remove auto-execution logic to prevent infinite "run forever" loops
140
- // The user should always be prompted or have the option to safely step through.
141
- console.log(chalk_1.default.yellow('🛡️ SyntaxGuard ') + chalk_1.default.white('Analysis:'));
142
- console.log(chalk_1.default.gray('> ') + chalk_1.default.white(command.code));
143
- console.log();
144
- if (command.dangerous) {
145
- console.log(chalk_1.default.red('🚨 WARNING: Dangerous command'));
146
- }
147
- const { execute } = await inquirer_1.default.prompt([{
148
- type: 'confirm',
149
- name: 'execute',
150
- message: command.dangerous ?
151
- chalk_1.default.red('Execute?') :
152
- chalk_1.default.gray('Execute?'),
153
- default: true // Default to true for seamless flow (Codex style)
154
- }]);
155
- return execute;
156
- }
157
- /**
158
- * Execute a shell command with real-time streaming and interruption support
159
- */
160
- async function executeCommand(command, shell, cwd) {
161
- return new Promise((resolve, reject) => {
162
- const shellPath = shell || process.env.SHELL || '/bin/bash';
163
- const child = (0, child_process_1.spawn)(shellPath, ['-c', command], {
164
- cwd: cwd || process.cwd(),
165
- env: { ...process.env, FORCE_COLOR: 'true' }
166
- });
167
- let stdout = '';
168
- let stderr = '';
169
- let isAborted = false;
170
- const commandStartTime = Date.now();
171
- // Listen for "Enter" or "Escape" to kill the command
172
- const onData = (data) => {
173
- // Check for Enter (13), Newline (10), Escape (27), or Ctrl+C (3)
174
- // 🏁 GRACE PERIOD: Ignore aborts in the first 200ms to prevent stray newlines
175
- if (Date.now() - commandStartTime < 200)
176
- return;
177
- if (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3) {
178
- isAborted = true;
179
- child.kill('SIGINT'); // Send SIGINT to gracefully terminate
180
- }
181
- };
182
- const isRaw = process.stdin.isRaw;
183
- process.stdin.resume();
184
- if (process.stdin.setRawMode)
185
- process.stdin.setRawMode(true);
186
- process.stdin.on('data', onData);
187
- child.stdout.on('data', (data) => {
188
- const chunk = data.toString();
189
- stdout += chunk;
190
- // No direct process.stdout.write here to prevent leaks
191
- });
192
- child.stderr.on('data', (data) => {
193
- const chunk = data.toString();
194
- stderr += chunk;
195
- // No direct process.stderr.write here to prevent leaks
196
- });
197
- child.on('close', (code) => {
198
- // Restore terminal state
199
- process.stdin.removeListener('data', onData);
200
- if (process.stdin.setRawMode)
201
- process.stdin.setRawMode(isRaw);
202
- process.stdin.pause();
203
- if (isAborted) {
204
- resolve({ stdout, stderr, aborted: true });
205
- }
206
- else if (code === 0) {
207
- resolve({ stdout, stderr });
208
- }
209
- else {
210
- resolve({ stdout, stderr }); // Resolve instead of reject to keep agent loop alive
211
- }
212
- });
213
- child.on('error', (err) => {
214
- process.stdin.removeListener('data', onData);
215
- if (process.stdin.setRawMode)
216
- process.stdin.setRawMode(isRaw);
217
- process.stdin.pause();
218
- reject(err);
219
- });
220
- });
221
- }
222
- /**
223
- * Execute commands and surgical patches with user permission
224
- */
225
- async function executeWithPermission(response, currentSessionCwd) {
226
- const commands = extractCommands(response);
227
- const patches = patcher_1.Patcher.parseEditBlocks(response);
228
- const files = patcher_1.Patcher.parseFileBlocks(response);
229
- let activeCwd = currentSessionCwd || process.cwd();
230
- let accumulatedOutput = '';
231
- let accumulatedError = '';
232
- if (commands.length === 0 && patches.length === 0 && files.length === 0) {
233
- return { success: true, executed: false, newCwd: activeCwd };
234
- }
235
- // 1. Handle New File Creation
236
- for (const file of files) {
237
- // Ensure creation happens relative to activeCwd if path is relative
238
- const originalPath = file.filePath;
239
- if (!path.isAbsolute(file.filePath)) {
240
- file.filePath = path.join(activeCwd, file.filePath);
241
- }
242
- let showFull = false;
243
- let decided = false;
244
- while (!decided) {
245
- const isTruncated = patcher_1.Patcher.showDiff(file, showFull);
246
- const choices = [
247
- { name: '✅ Create this new file brother', value: 'create' },
248
- { name: '⏭️ Skip', value: 'skip' }
249
- ];
250
- if (isTruncated && !showFull) {
251
- choices.splice(1, 0, { name: '🔍 View Full Code', value: 'full' });
252
- }
253
- const { action } = await inquirer_1.default.prompt([{
254
- type: 'list',
255
- name: 'action',
256
- message: chalk_1.default.cyan(`Create ${originalPath}?`),
257
- choices: choices
258
- }]);
259
- if (action === 'full') {
260
- showFull = true;
261
- continue;
262
- }
263
- if (action === 'create') {
264
- const result = patcher_1.Patcher.createFile(file);
265
- if (!result.success)
266
- return { success: false, executed: true, error: result.error, newCwd: activeCwd };
267
- }
268
- decided = true;
269
- }
270
- }
271
- // 2. Handle Shell Commands
272
- for (let i = 0; i < commands.length; i++) {
273
- const command = commands[i];
274
- // 🛡️ PRE-EXECUTION HALLUCINATION GUARD: Check for fake directories
275
- const commandLines = command.code.split('\n');
276
- let skipThisCommand = false;
277
- let sanitizedCode = command.code;
278
- for (const line of commandLines) {
279
- const trimmed = line.trim();
280
- // Check standalone cd commands
281
- if (trimmed.startsWith('cd ')) {
282
- let targetDir = trimmed.substring(3).replace(/['"]/g, '').trim();
283
- const potentialCwd = path.isAbsolute(targetDir) ? targetDir : path.resolve(activeCwd, targetDir);
284
- if (!fs.existsSync(potentialCwd) || !fs.statSync(potentialCwd).isDirectory()) {
285
- console.log(chalk_1.default.yellow(`\n ⚠️ Skipped hallucinated directory: "${targetDir}"`));
286
- console.log(chalk_1.default.gray(` (Does not exist at: ${potentialCwd})`));
287
- console.log(chalk_1.default.gray(` Current CWD stays: ${activeCwd}\n`));
288
- skipThisCommand = true;
289
- break;
290
- }
291
- }
292
- // Check cd inside combined commands like: cd "fake" && npm install
293
- const cdChainMatch = trimmed.match(/^cd\s+["']?([^"'&]+)["']?\s*&&/);
294
- if (cdChainMatch) {
295
- let targetDir = cdChainMatch[1].trim();
296
- const potentialCwd = path.isAbsolute(targetDir) ? targetDir : path.resolve(activeCwd, targetDir);
297
- if (!fs.existsSync(potentialCwd) || !fs.statSync(potentialCwd).isDirectory()) {
298
- console.log(chalk_1.default.yellow(`\n ⚠️ Stripping hallucinated cd from command: "${targetDir}"`));
299
- // Remove the cd part and execute the rest
300
- sanitizedCode = trimmed.replace(/^cd\s+["']?[^"'&]+["']?\s*&&\s*/, '');
301
- console.log(chalk_1.default.gray(` Running remaining: ${sanitizedCode}\n`));
302
- }
303
- }
304
- }
305
- if (skipThisCommand)
306
- continue;
307
- // Update command code with sanitized version
308
- command.code = sanitizedCode;
309
- // 🚀 MCP INTERCEPTION: If the command matches an MCP tool directly, use MCPServer
310
- const mcpMatch = command.code.trim().match(/^(web_search|read_file|write_file|list_directory|search_files|run_command|fetch_url|git_status)\s+(.*)$/);
311
- if (mcpMatch) {
312
- const toolName = mcpMatch[1];
313
- const rawArgs = mcpMatch[2].trim();
314
- console.log(chalk_1.default.cyan(`⚡ Intercepted MCP Tool: `) + chalk_1.default.white(`${toolName}(${rawArgs})`));
315
- try {
316
- const { MCPServer } = await Promise.resolve().then(() => __importStar(require('./mcp-server')));
317
- const mcp = new MCPServer(activeCwd);
318
- // Heuristic: If it's just a string, wrap it in a query/path object
319
- let args = {};
320
- if (toolName === 'web_search')
321
- args = { query: rawArgs.replace(/^['"]|['"]$/g, '') };
322
- else if (toolName === 'fetch_url')
323
- args = { url: rawArgs.replace(/^['"]|['"]$/g, '') };
324
- else if (['read_file', 'list_directory', 'git_status'].includes(toolName))
325
- args = { path: rawArgs.replace(/^['"]|['"]$/g, '') };
326
- else
327
- args = { command: rawArgs }; // Default fallback
328
- const result = await mcp.execute(toolName, args);
329
- if (result.success) {
330
- accumulatedOutput += `[MCP ${toolName} Output]:\n${result.output}\n\n`;
331
- agent_orchestrator_1.AgentOrchestrator.terminal(command.code, result.output);
332
- }
333
- else {
334
- accumulatedError += `[MCP ${toolName} Error]: ${result.error}\n`;
335
- agent_orchestrator_1.AgentOrchestrator.terminal(command.code, undefined, result.error);
336
- }
337
- continue; // Move to next command
338
- }
339
- catch (e) {
340
- console.log(chalk_1.default.red(` ❌ MCP Execution Error: ${e.message}`));
341
- }
342
- }
343
- const shouldExecute = await askPermission(command);
344
- if (shouldExecute) {
345
- try {
346
- // Persistent CWD logic: Trace 'cd' commands
347
- const lines = command.code.split('\n');
348
- for (const line of lines) {
349
- const trimmed = line.trim();
350
- if (trimmed.startsWith('cd ')) {
351
- let targetDir = trimmed.substring(3).replace(/['"]/g, '').trim();
352
- // Resolve relative to activeCwd
353
- const potentialCwd = path.isAbsolute(targetDir) ? targetDir : path.resolve(activeCwd, targetDir);
354
- if (fs.existsSync(potentialCwd) && fs.statSync(potentialCwd).isDirectory()) {
355
- activeCwd = potentialCwd;
356
- }
357
- else {
358
- // Directory doesn't exist - skip silently instead of crashing
359
- console.log(chalk_1.default.yellow(` ⚠️ Directory not found: ${targetDir} — staying in ${activeCwd}`));
360
- continue;
361
- }
362
- }
363
- }
364
- tui_1.TUI.drawLiveTerminalStart(command.code);
365
- const shellPath = process.env.SHELL || '/bin/bash';
366
- const child = (0, child_process_1.spawn)(shellPath, ['-c', command.code], {
367
- cwd: activeCwd,
368
- env: { ...process.env, FORCE_COLOR: 'true' }
369
- });
370
- let stdout = '';
371
- let stderr = '';
372
- let isAborted = false;
373
- const commandStartTime = Date.now();
374
- const onData = (data) => {
375
- if (Date.now() - commandStartTime < 500)
376
- return; // Hardened 500ms grace period
377
- if (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3) {
378
- isAborted = true;
379
- child.kill('SIGINT');
380
- }
381
- };
382
- const isRaw = process.stdin.isRaw;
383
- process.stdin.resume();
384
- if (process.stdin.setRawMode)
385
- process.stdin.setRawMode(true);
386
- process.stdin.on('data', onData);
387
- child.stdout.on('data', (data) => {
388
- const chunk = data.toString();
389
- stdout += chunk;
390
- tui_1.TUI.drawLiveTerminalLine(chunk);
391
- });
392
- child.stderr.on('data', (data) => {
393
- const chunk = data.toString();
394
- stderr += chunk;
395
- tui_1.TUI.drawLiveTerminalLine(chunk, true);
396
- });
397
- const { code } = await new Promise(resolve => {
398
- child.on('close', (code) => {
399
- process.stdin.removeListener('data', onData);
400
- if (process.stdin.setRawMode)
401
- process.stdin.setRawMode(isRaw);
402
- process.stdin.pause();
403
- tui_1.TUI.drawLiveTerminalEnd();
404
- resolve({ code });
405
- });
406
- });
407
- accumulatedOutput += stdout;
408
- accumulatedError += stderr;
409
- if (isAborted) {
410
- console.log(chalk_1.default.yellow(' [🛑] Command aborted by brother.\n'));
411
- agent_orchestrator_1.AgentOrchestrator.terminal(command.code, stdout, stderr + '\n(Aborted)');
412
- }
413
- else if (code === 0) {
414
- if (stdout || stderr) {
415
- agent_orchestrator_1.AgentOrchestrator.terminal(command.code, stdout, stderr);
416
- }
417
- else {
418
- agent_orchestrator_1.AgentOrchestrator.terminal(command.code, '✓ Success (No output)');
419
- }
420
- }
421
- else {
422
- agent_orchestrator_1.AgentOrchestrator.terminal(command.code, stdout, stderr || `Failed with code ${code}`);
423
- }
424
- }
425
- catch (error) {
426
- accumulatedError += error.message;
427
- agent_orchestrator_1.AgentOrchestrator.terminal(command.code, undefined, error.message);
428
- return { success: false, executed: true, error: accumulatedError, output: accumulatedOutput, newCwd: activeCwd };
429
- }
430
- }
431
- }
432
- // 3. Handle Surgical Patches
433
- for (const surgicalPatch of patches) {
434
- // Ensure patch happens relative to activeCwd if path is relative
435
- const originalPath = surgicalPatch.filePath;
436
- if (!path.isAbsolute(surgicalPatch.filePath)) {
437
- surgicalPatch.filePath = path.join(activeCwd, surgicalPatch.filePath);
438
- }
439
- let showFull = false;
440
- let decided = false;
441
- while (!decided) {
442
- const isTruncated = patcher_1.Patcher.showDiff(surgicalPatch, showFull);
443
- const choices = [
444
- { name: '✅ Apply this surgical patch brother', value: 'apply' },
445
- { name: '⏭️ Skip', value: 'skip' }
446
- ];
447
- if (isTruncated && !showFull) {
448
- choices.splice(1, 0, { name: '🔍 View Full Diff', value: 'full' });
449
- }
450
- const { action } = await inquirer_1.default.prompt([{
451
- type: 'list',
452
- name: 'action',
453
- message: chalk_1.default.cyan(`Patch ${originalPath}?`),
454
- choices: choices
455
- }]);
456
- if (action === 'full') {
457
- showFull = true;
458
- continue;
459
- }
460
- if (action === 'apply') {
461
- const result = patcher_1.Patcher.applyPatch(surgicalPatch);
462
- if (result.success) {
463
- console.log(chalk_1.default.green(` ✅ Applied surgical patch to ${originalPath}\n`));
464
- }
465
- else {
466
- console.log(chalk_1.default.red(` ❌ Error applying patch: ${result.error}\n`));
467
- return { success: false, executed: true, error: result.error, newCwd: activeCwd };
468
- }
469
- }
470
- else {
471
- console.log(chalk_1.default.gray(' Skipped.\n'));
472
- }
473
- decided = true;
474
- }
475
- }
476
- return {
477
- success: true,
478
- executed: true,
479
- output: accumulatedOutput.trim(),
480
- error: accumulatedError.trim(),
481
- newCwd: activeCwd
482
- };
483
- }
484
- /**
485
- * Command history for undo/rollback
486
- */
487
- class CommandHistory {
488
- constructor() {
489
- this.history = [];
490
- }
491
- add(command, success) {
492
- this.history.push({
493
- command,
494
- timestamp: new Date(),
495
- success
496
- });
497
- }
498
- getHistory() {
499
- return this.history;
500
- }
501
- clear() {
502
- this.history = [];
503
- }
504
- }
505
- exports.CommandHistory = CommandHistory;
506
- exports.commandHistory = new CommandHistory();
507
- //# sourceMappingURL=command-executor.js.map
@@ -1,139 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.configManager = exports.ConfigManager = void 0;
37
- const fs = __importStar(require("fs"));
38
- const path = __importStar(require("path"));
39
- const os = __importStar(require("os"));
40
- class ConfigManager {
41
- constructor() {
42
- this.configDir = path.join(os.homedir(), '.matex');
43
- this.configPath = path.join(this.configDir, 'config.json');
44
- // Ensure config directory exists
45
- if (!fs.existsSync(this.configDir)) {
46
- fs.mkdirSync(this.configDir, { recursive: true });
47
- }
48
- }
49
- /**
50
- * Load config from file
51
- */
52
- loadConfig() {
53
- if (!fs.existsSync(this.configPath)) {
54
- return {};
55
- }
56
- try {
57
- const data = fs.readFileSync(this.configPath, 'utf-8');
58
- return JSON.parse(data);
59
- }
60
- catch (error) {
61
- return {};
62
- }
63
- }
64
- /**
65
- * Save config to file
66
- */
67
- saveConfig(config) {
68
- fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf-8');
69
- }
70
- /**
71
- * Set API key
72
- */
73
- setAPIKey(apiKey) {
74
- const config = this.loadConfig();
75
- config.apiKey = apiKey;
76
- this.saveConfig(config);
77
- }
78
- /**
79
- * Get API key
80
- */
81
- getAPIKey() {
82
- return this.loadConfig().apiKey;
83
- }
84
- /**
85
- * Set default model
86
- */
87
- setDefaultModel(model) {
88
- const config = this.loadConfig();
89
- let normalizedModel = model.toLowerCase().trim().replace(/\s+/g, '');
90
- if (normalizedModel === 'matexfree') {
91
- normalizedModel = 'matex-free';
92
- }
93
- config.defaultModel = normalizedModel;
94
- this.saveConfig(config);
95
- }
96
- /**
97
- * Get default model
98
- */
99
- getDefaultModel() {
100
- return this.loadConfig().defaultModel || 'matexcodex';
101
- }
102
- /**
103
- * Set base URL
104
- */
105
- setBaseURL(url) {
106
- const config = this.loadConfig();
107
- config.baseURL = url;
108
- this.saveConfig(config);
109
- }
110
- /**
111
- * Get base URL
112
- */
113
- getBaseURL() {
114
- return this.loadConfig().baseURL || 'https://matexai-backend-550499663766.us-central1.run.app';
115
- }
116
- /**
117
- * Get all config
118
- */
119
- getAll() {
120
- return this.loadConfig();
121
- }
122
- /**
123
- * Clear all config
124
- */
125
- clear() {
126
- if (fs.existsSync(this.configPath)) {
127
- fs.unlinkSync(this.configPath);
128
- }
129
- }
130
- /**
131
- * Check if API key is set
132
- */
133
- hasAPIKey() {
134
- return !!this.getAPIKey();
135
- }
136
- }
137
- exports.ConfigManager = ConfigManager;
138
- exports.configManager = new ConfigManager();
139
- //# sourceMappingURL=config.js.map