trace.ai-cli 1.1.8 → 1.2.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.
@@ -1,16 +1,91 @@
1
1
  const path = require('path');
2
+ const fs = require('fs').promises;
3
+ const pdfParse = require('pdf-parse');
2
4
  const { getFileType, readFileContent } = require('../utils/fileUtils');
3
5
  const { processWithAI } = require('./aiService');
4
6
  const { extractTextFromImage } = require('./imageService');
5
7
 
6
- async function analyzeFile(filePath, query = '') {
8
+ async function analyzeFile(filePath, query = '', mode = 2) {
7
9
  try {
8
10
  const fileType = getFileType(filePath);
9
11
 
12
+ // Handle images - use vision API only for images
10
13
  if (fileType === 'image') {
11
- return await extractTextFromImage(filePath);
14
+ const result = await extractTextFromImage(filePath, query, mode);
15
+ return { text: result };
12
16
  }
13
17
 
18
+ // Handle documents (PDFs, Word, etc.)
19
+ if (fileType === 'document') {
20
+ const fileName = path.basename(filePath);
21
+ const ext = path.extname(filePath).toLowerCase();
22
+
23
+ // Extract text from PDF
24
+ if (ext === '.pdf') {
25
+ try {
26
+ const dataBuffer = await fs.readFile(filePath);
27
+ const pdfData = await pdfParse(dataBuffer);
28
+ const content = pdfData.text;
29
+
30
+ if (!content || content.trim().length === 0) {
31
+ return {
32
+ text: `❌ **Unable to Extract Text from PDF**\n\n` +
33
+ `The PDF "${fileName}" appears to be empty or contains only images/scanned content.\n\n` +
34
+ `**Try using the /image command for scanned PDFs:**\n` +
35
+ `\`/image "${filePath}" ${query || 'analyze this document'}\``
36
+ };
37
+ }
38
+
39
+ // Build prompt for AI analysis
40
+ let prompt;
41
+ if (query) {
42
+ prompt = `PDF Document: ${fileName}
43
+ Content:
44
+ \`\`\`
45
+ ${content}
46
+ \`\`\`
47
+
48
+ User Question: ${query}`;
49
+ } else {
50
+ prompt = `Analyze this PDF document (${fileName}):
51
+
52
+ \`\`\`
53
+ ${content}
54
+ \`\`\`
55
+
56
+ Please provide a comprehensive summary and analysis of the document.`;
57
+ }
58
+
59
+ const result = await processWithAI(prompt, '', mode);
60
+ return { text: result || 'No response generated' };
61
+
62
+ } catch (pdfError) {
63
+ return {
64
+ text: `❌ **Error Reading PDF**\n\n` +
65
+ `Failed to extract text from "${fileName}": ${pdfError.message}\n\n` +
66
+ `**Possible reasons:**\n` +
67
+ `- The PDF is corrupted or password-protected\n` +
68
+ `- The PDF contains only images (scanned document)\n\n` +
69
+ `**Try using /image command for scanned PDFs:**\n` +
70
+ `\`/image "${filePath}" ${query || 'analyze this document'}\``
71
+ };
72
+ }
73
+ }
74
+
75
+ // For other document types (Word, Excel, etc.)
76
+ return {
77
+ text: `❌ **Document Type Not Supported**\n\n` +
78
+ `The file "${fileName}" is a ${ext.toUpperCase()} document.\n\n` +
79
+ `**Supported formats:**\n` +
80
+ `- PDF files (.pdf) - Text extraction supported\n` +
81
+ `- Text files (.txt, .md, etc.)\n` +
82
+ `- Code files (.js, .py, .java, etc.)\n\n` +
83
+ `**For ${ext.toUpperCase()} files:**\n` +
84
+ `Please convert to PDF or text format first.`
85
+ };
86
+ }
87
+
88
+ // Handle text-based files
14
89
  const content = await readFileContent(filePath);
15
90
  const fileName = path.basename(filePath);
16
91
 
@@ -48,8 +123,8 @@ Please provide a summary and analysis of the content.`;
48
123
  }
49
124
  }
50
125
 
51
- const result = await processWithAI(prompt);
52
- return { text: result || 'No response generated' }; // Ensure consistent return format
126
+ const result = await processWithAI(prompt, '', mode);
127
+ return { text: result || 'No response generated' };
53
128
  } catch (error) {
54
129
  throw error;
55
130
  }
@@ -57,4 +132,4 @@ Please provide a summary and analysis of the content.`;
57
132
 
58
133
  module.exports = {
59
134
  analyzeFile
60
- };
135
+ };
@@ -1,164 +1,164 @@
1
- const fs = require('fs').promises;
2
- const path = require('path');
3
- const { getFileType } = require('../utils/fileUtils');
4
- const { processWithAI } = require('./aiService');
5
-
6
- function buildSimpleFileTree(folderPath, items, statsCache, depth = 0) {
7
- let tree = '';
8
- const indent = ' '.repeat(depth);
9
-
10
- for (const item of items) {
11
- const itemPath = path.join(folderPath, item);
12
- const stats = statsCache.get(itemPath);
13
- tree += `${indent}${item}${stats.isDirectory() ? '/' : ''}\n`;
14
-
15
- if (stats.isDirectory()) {
16
- try {
17
- const subItems = fs.readdirSync(itemPath);
18
- const subStatsCache = new Map();
19
- for (const subItem of subItems) {
20
- const subItemPath = path.join(itemPath, subItem);
21
- subStatsCache.set(subItemPath, fs.statSync(subItemPath));
22
- }
23
- tree += buildSimpleFileTree(itemPath, subItems, subStatsCache, depth + 1);
24
- } catch (error) {
25
- tree += `${indent} (error accessing directory)\n`;
26
- }
27
- }
28
- }
29
-
30
- return tree;
31
- }
32
-
33
- async function analyzeFolder(folderPath, maxDepth = 2, currentDepth = 0) {
34
- try {
35
- const stats = await fs.stat(folderPath);
36
- if (!stats.isDirectory()) {
37
- throw new Error('Path is not a directory');
38
- }
39
-
40
- const analysis = {
41
- path: folderPath,
42
- files: [],
43
- folders: [],
44
- fileTree: '', // Store simple file tree
45
- summary: {
46
- totalFiles: 0,
47
- totalFolders: 0,
48
- fileTypes: {},
49
- languages: {},
50
- size: 0
51
- }
52
- };
53
-
54
- const items = await fs.readdir(folderPath);
55
- const statsCache = new Map();
56
-
57
- // Cache stats for all items
58
- for (const item of items) {
59
- const itemPath = path.join(folderPath, item);
60
- statsCache.set(itemPath, await fs.stat(itemPath));
61
- }
62
-
63
- // Build simple file tree
64
- analysis.fileTree = buildSimpleFileTree(folderPath, items, statsCache);
65
-
66
- for (const item of items) {
67
- const itemPath = path.join(folderPath, item);
68
- const itemStats = statsCache.get(itemPath);
69
-
70
- if (itemStats.isDirectory()) {
71
- analysis.folders.push(item);
72
- analysis.summary.totalFolders++;
73
-
74
- if (currentDepth < maxDepth) {
75
- try {
76
- const subAnalysis = await analyzeFolder(itemPath, maxDepth, currentDepth + 1);
77
- analysis.summary.totalFiles += subAnalysis.summary.totalFiles;
78
- analysis.summary.totalFolders += subAnalysis.summary.totalFolders;
79
- analysis.summary.size += subAnalysis.summary.size;
80
-
81
- Object.keys(subAnalysis.summary.fileTypes).forEach(type => {
82
- analysis.summary.fileTypes[type] = (analysis.summary.fileTypes[type] || 0) + subAnalysis.summary.fileTypes[type];
83
- });
84
- Object.keys(subAnalysis.summary.languages).forEach(lang => {
85
- analysis.summary.languages[lang] = (analysis.summary.languages[lang] || 0) + subAnalysis.summary.languages[lang];
86
- });
87
- } catch (error) {
88
- console.warn(`Warning: Cannot access folder ${itemPath}`);
89
- }
90
- }
91
- } else {
92
- const fileType = getFileType(itemPath);
93
- analysis.files.push({
94
- name: item,
95
- type: fileType,
96
- size: itemStats.size,
97
- modified: itemStats.mtime
98
- });
99
-
100
- analysis.summary.totalFiles++;
101
- analysis.summary.size += itemStats.size;
102
- analysis.summary.fileTypes[fileType] = (analysis.summary.fileTypes[fileType] || 0) + 1;
103
-
104
- if (fileType !== 'image' && fileType !== 'Unknown') {
105
- analysis.summary.languages[fileType] = (analysis.summary.languages[fileType] || 0) + 1;
106
- }
107
- }
108
- }
109
-
110
- return analysis;
111
- } catch (error) {
112
- if (error.code === 'ENOENT') {
113
- throw new Error(`Folder not found: ${folderPath}`);
114
- }
115
- if (error.code === 'EACCES') {
116
- throw new Error(`Access denied: ${folderPath}`);
117
- }
118
- throw error;
119
- }
120
- }
121
-
122
- async function analyzeFolderStructure(folderPath, query = '') {
123
- try {
124
- const analysis = await analyzeFolder(folderPath);
125
-
126
- const structureText = `
127
- Folder: ${analysis.path}
128
- Files: ${analysis.summary.totalFiles}
129
- Folders: ${analysis.summary.totalFolders}
130
- Total Size: ${(analysis.summary.size / 1024).toFixed(2)} KB
131
-
132
- Files and Directories:
133
- ${analysis.fileTree}
134
-
135
- File Types:
136
- ${Object.entries(analysis.summary.fileTypes).map(([type, count]) => `- ${type}: ${count}`).join('\n')}
137
-
138
- Languages/Technologies:
139
- ${Object.entries(analysis.summary.languages).map(([lang, count]) => `- ${lang}: ${count} files`).join('\n')}
140
- `;
141
-
142
- let prompt;
143
- if (query) {
144
- prompt = `Project Structure:
145
- ${structureText}
146
-
147
- User Question: ${query}`;
148
- } else {
149
- prompt = `Analyze this project structure:
150
- ${structureText}
151
-
152
- Provide a brief overview of the project based on its file structure.`;
153
- }
154
-
155
- const result = await processWithAI(prompt);
156
- return { text: result || 'No response generated' }; // Ensure text property is always defined
157
- } catch (error) {
158
- throw error;
159
- }
160
- }
161
-
162
- module.exports = {
163
- analyzeFolderStructure
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+ const { getFileType } = require('../utils/fileUtils');
4
+ const { processWithAI } = require('./aiService');
5
+
6
+ function buildSimpleFileTree(folderPath, items, statsCache, depth = 0) {
7
+ let tree = '';
8
+ const indent = ' '.repeat(depth);
9
+
10
+ for (const item of items) {
11
+ const itemPath = path.join(folderPath, item);
12
+ const stats = statsCache.get(itemPath);
13
+ tree += `${indent}${item}${stats.isDirectory() ? '/' : ''}\n`;
14
+
15
+ if (stats.isDirectory()) {
16
+ try {
17
+ const subItems = fs.readdirSync(itemPath);
18
+ const subStatsCache = new Map();
19
+ for (const subItem of subItems) {
20
+ const subItemPath = path.join(itemPath, subItem);
21
+ subStatsCache.set(subItemPath, fs.statSync(subItemPath));
22
+ }
23
+ tree += buildSimpleFileTree(itemPath, subItems, subStatsCache, depth + 1);
24
+ } catch (error) {
25
+ tree += `${indent} (error accessing directory)\n`;
26
+ }
27
+ }
28
+ }
29
+
30
+ return tree;
31
+ }
32
+
33
+ async function analyzeFolder(folderPath, maxDepth = 2, currentDepth = 0) {
34
+ try {
35
+ const stats = await fs.stat(folderPath);
36
+ if (!stats.isDirectory()) {
37
+ throw new Error('Path is not a directory');
38
+ }
39
+
40
+ const analysis = {
41
+ path: folderPath,
42
+ files: [],
43
+ folders: [],
44
+ fileTree: '', // Store simple file tree
45
+ summary: {
46
+ totalFiles: 0,
47
+ totalFolders: 0,
48
+ fileTypes: {},
49
+ languages: {},
50
+ size: 0
51
+ }
52
+ };
53
+
54
+ const items = await fs.readdir(folderPath);
55
+ const statsCache = new Map();
56
+
57
+ // Cache stats for all items
58
+ for (const item of items) {
59
+ const itemPath = path.join(folderPath, item);
60
+ statsCache.set(itemPath, await fs.stat(itemPath));
61
+ }
62
+
63
+ // Build simple file tree
64
+ analysis.fileTree = buildSimpleFileTree(folderPath, items, statsCache);
65
+
66
+ for (const item of items) {
67
+ const itemPath = path.join(folderPath, item);
68
+ const itemStats = statsCache.get(itemPath);
69
+
70
+ if (itemStats.isDirectory()) {
71
+ analysis.folders.push(item);
72
+ analysis.summary.totalFolders++;
73
+
74
+ if (currentDepth < maxDepth) {
75
+ try {
76
+ const subAnalysis = await analyzeFolder(itemPath, maxDepth, currentDepth + 1);
77
+ analysis.summary.totalFiles += subAnalysis.summary.totalFiles;
78
+ analysis.summary.totalFolders += subAnalysis.summary.totalFolders;
79
+ analysis.summary.size += subAnalysis.summary.size;
80
+
81
+ Object.keys(subAnalysis.summary.fileTypes).forEach(type => {
82
+ analysis.summary.fileTypes[type] = (analysis.summary.fileTypes[type] || 0) + subAnalysis.summary.fileTypes[type];
83
+ });
84
+ Object.keys(subAnalysis.summary.languages).forEach(lang => {
85
+ analysis.summary.languages[lang] = (analysis.summary.languages[lang] || 0) + subAnalysis.summary.languages[lang];
86
+ });
87
+ } catch (error) {
88
+ console.warn(`Warning: Cannot access folder ${itemPath}`);
89
+ }
90
+ }
91
+ } else {
92
+ const fileType = getFileType(itemPath);
93
+ analysis.files.push({
94
+ name: item,
95
+ type: fileType,
96
+ size: itemStats.size,
97
+ modified: itemStats.mtime
98
+ });
99
+
100
+ analysis.summary.totalFiles++;
101
+ analysis.summary.size += itemStats.size;
102
+ analysis.summary.fileTypes[fileType] = (analysis.summary.fileTypes[fileType] || 0) + 1;
103
+
104
+ if (fileType !== 'image' && fileType !== 'Unknown') {
105
+ analysis.summary.languages[fileType] = (analysis.summary.languages[fileType] || 0) + 1;
106
+ }
107
+ }
108
+ }
109
+
110
+ return analysis;
111
+ } catch (error) {
112
+ if (error.code === 'ENOENT') {
113
+ throw new Error(`Folder not found: ${folderPath}`);
114
+ }
115
+ if (error.code === 'EACCES') {
116
+ throw new Error(`Access denied: ${folderPath}`);
117
+ }
118
+ throw error;
119
+ }
120
+ }
121
+
122
+ async function analyzeFolderStructure(folderPath, query = '') {
123
+ try {
124
+ const analysis = await analyzeFolder(folderPath);
125
+
126
+ const structureText = `
127
+ Folder: ${analysis.path}
128
+ Files: ${analysis.summary.totalFiles}
129
+ Folders: ${analysis.summary.totalFolders}
130
+ Total Size: ${(analysis.summary.size / 1024).toFixed(2)} KB
131
+
132
+ Files and Directories:
133
+ ${analysis.fileTree}
134
+
135
+ File Types:
136
+ ${Object.entries(analysis.summary.fileTypes).map(([type, count]) => `- ${type}: ${count}`).join('\n')}
137
+
138
+ Languages/Technologies:
139
+ ${Object.entries(analysis.summary.languages).map(([lang, count]) => `- ${lang}: ${count} files`).join('\n')}
140
+ `;
141
+
142
+ let prompt;
143
+ if (query) {
144
+ prompt = `Project Structure:
145
+ ${structureText}
146
+
147
+ User Question: ${query}`;
148
+ } else {
149
+ prompt = `Analyze this project structure:
150
+ ${structureText}
151
+
152
+ Provide a brief overview of the project based on its file structure.`;
153
+ }
154
+
155
+ const result = await processWithAI(prompt);
156
+ return { text: result || 'No response generated' }; // Ensure text property is always defined
157
+ } catch (error) {
158
+ throw error;
159
+ }
160
+ }
161
+
162
+ module.exports = {
163
+ analyzeFolderStructure
164
164
  };
@@ -3,6 +3,20 @@ const fetch = require('node-fetch');
3
3
  const { encryptData, decryptData } = require('../utils/encryption');
4
4
  const { getMimeType } = require('../utils/fileUtils');
5
5
 
6
+ // Model selection helper for vision models
7
+ function getVisionModels(mode) {
8
+ switch (Number(mode)) {
9
+ case 1: // Fast
10
+ return ['g1'];
11
+ case 2: // Balanced
12
+ return ['qvl72', 'mvrk'];
13
+ case 3: // Think
14
+ return ['g1', 'mvrk', 'gma3', 'qvl72', 'sct'];
15
+ default:
16
+ return ['qvl72', 'mvrk'];
17
+ }
18
+ }
19
+
6
20
  async function convertImageToBase64(imagePath) {
7
21
  try {
8
22
  const imageBuffer = await fs.readFile(imagePath);
@@ -14,10 +28,10 @@ async function convertImageToBase64(imagePath) {
14
28
  }
15
29
  }
16
30
 
17
- async function extractTextFromImage(imagePath, question = '') {
31
+ async function extractTextFromImage(imagePath, question = '', mode = 2) {
18
32
  try {
19
33
  const { base64, mimeType } = await convertImageToBase64(imagePath);
20
- const visionModels = ['kimi', 'mvrk', 'gma3', 'qvl72', 'llama-vision'];
34
+ const visionModels = getVisionModels(mode); // Use selected mode
21
35
 
22
36
  const modelRequests = visionModels.map(model =>
23
37
  fetch('https://traceai.dukeindustries7.workers.dev/', {