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.
- package/README.markdown +150 -150
- package/cli/traceAI.js +32 -7
- package/index.js +8 -4
- package/package.json +46 -45
- package/services/aiService.js +36 -9
- package/services/fileAnalyzer.js +80 -5
- package/services/folderAnalyzer.js +163 -163
- package/services/imageService.js +16 -2
- package/services/systemInfoService.js +956 -956
- package/utils/encryption.js +41 -41
- package/utils/fileUtils.js +7 -2
- package/utils/markdown.js +168 -0
package/utils/encryption.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
const ENCRYPTION_KEY = 'AlzaSyCVKlzUxK';
|
|
2
|
-
|
|
3
|
-
function encodeUnicode(str) {
|
|
4
|
-
const utf8Bytes = new TextEncoder().encode(str);
|
|
5
|
-
return Buffer.from(utf8Bytes).toString('base64');
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function decodeUnicode(str) {
|
|
9
|
-
const bytes = Buffer.from(str, 'base64');
|
|
10
|
-
return new TextDecoder().decode(bytes);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function encryptData(data) {
|
|
14
|
-
const jsonStr = JSON.stringify(data);
|
|
15
|
-
// Match worker.js approach: encode to base64 then reverse
|
|
16
|
-
const base64 = Buffer.from(
|
|
17
|
-
Array.from(new TextEncoder().encode(jsonStr))
|
|
18
|
-
.map(byte => String.fromCharCode(byte))
|
|
19
|
-
.join('')
|
|
20
|
-
).toString('base64');
|
|
21
|
-
|
|
22
|
-
return base64.split('').reverse().join('');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function decryptData(encoded) {
|
|
26
|
-
try {
|
|
27
|
-
// Match worker.js approach: reverse then decode
|
|
28
|
-
const reversed = encoded.split('').reverse().join('');
|
|
29
|
-
const binaryStr = Buffer.from(reversed, 'base64').toString('binary');
|
|
30
|
-
const bytes = Uint8Array.from(binaryStr, char => char.charCodeAt(0));
|
|
31
|
-
const decoded = new TextDecoder().decode(bytes);
|
|
32
|
-
return JSON.parse(decoded);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.error('Decryption failed:', error);
|
|
35
|
-
throw new Error('Invalid encrypted data');
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = {
|
|
40
|
-
encryptData,
|
|
41
|
-
decryptData
|
|
1
|
+
const ENCRYPTION_KEY = 'AlzaSyCVKlzUxK';
|
|
2
|
+
|
|
3
|
+
function encodeUnicode(str) {
|
|
4
|
+
const utf8Bytes = new TextEncoder().encode(str);
|
|
5
|
+
return Buffer.from(utf8Bytes).toString('base64');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function decodeUnicode(str) {
|
|
9
|
+
const bytes = Buffer.from(str, 'base64');
|
|
10
|
+
return new TextDecoder().decode(bytes);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function encryptData(data) {
|
|
14
|
+
const jsonStr = JSON.stringify(data);
|
|
15
|
+
// Match worker.js approach: encode to base64 then reverse
|
|
16
|
+
const base64 = Buffer.from(
|
|
17
|
+
Array.from(new TextEncoder().encode(jsonStr))
|
|
18
|
+
.map(byte => String.fromCharCode(byte))
|
|
19
|
+
.join('')
|
|
20
|
+
).toString('base64');
|
|
21
|
+
|
|
22
|
+
return base64.split('').reverse().join('');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function decryptData(encoded) {
|
|
26
|
+
try {
|
|
27
|
+
// Match worker.js approach: reverse then decode
|
|
28
|
+
const reversed = encoded.split('').reverse().join('');
|
|
29
|
+
const binaryStr = Buffer.from(reversed, 'base64').toString('binary');
|
|
30
|
+
const bytes = Uint8Array.from(binaryStr, char => char.charCodeAt(0));
|
|
31
|
+
const decoded = new TextDecoder().decode(bytes);
|
|
32
|
+
return JSON.parse(decoded);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Decryption failed:', error);
|
|
35
|
+
throw new Error('Invalid encrypted data');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
encryptData,
|
|
41
|
+
decryptData
|
|
42
42
|
};
|
package/utils/fileUtils.js
CHANGED
|
@@ -42,18 +42,22 @@ function getFileType(filePath) {
|
|
|
42
42
|
'.bash': 'Bash Script',
|
|
43
43
|
'.zsh': 'Zsh Script',
|
|
44
44
|
'.ps1': 'PowerShell',
|
|
45
|
-
'.bat': 'Batch Script',
|
|
46
45
|
'.dockerfile': 'Dockerfile',
|
|
47
46
|
'.gitignore': 'Git Ignore',
|
|
48
47
|
'.env': 'Environment Variables'
|
|
49
48
|
};
|
|
50
49
|
|
|
51
50
|
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'];
|
|
51
|
+
const documentExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx'];
|
|
52
52
|
|
|
53
53
|
if (imageExtensions.includes(ext)) {
|
|
54
54
|
return 'image';
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
if (documentExtensions.includes(ext)) {
|
|
58
|
+
return 'document';
|
|
59
|
+
}
|
|
60
|
+
|
|
57
61
|
return codeExtensions[ext] || 'Unknown';
|
|
58
62
|
}
|
|
59
63
|
|
|
@@ -66,7 +70,8 @@ function getMimeType(filePath) {
|
|
|
66
70
|
'.gif': 'image/gif',
|
|
67
71
|
'.webp': 'image/webp',
|
|
68
72
|
'.bmp': 'image/bmp',
|
|
69
|
-
'.svg': 'image/svg+xml'
|
|
73
|
+
'.svg': 'image/svg+xml',
|
|
74
|
+
'.pdf': 'application/pdf'
|
|
70
75
|
};
|
|
71
76
|
return mimeTypes[ext] || 'image/jpeg';
|
|
72
77
|
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// markdown.js
|
|
2
|
+
// Minimal Markdown renderer (safe): supports **bold**, `code`, bullet lists, and fenced code blocks ```lang
|
|
3
|
+
// Additionally provides a terminal-friendly ANSI renderer for Node CLI.
|
|
4
|
+
|
|
5
|
+
function escapeHtml(s) {
|
|
6
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Terminal-friendly Markdown to ANSI using chalk (Node only)
|
|
10
|
+
function markdownToAnsi(md) {
|
|
11
|
+
if (!md) return '';
|
|
12
|
+
const chalk = (typeof window === 'undefined') ? require('chalk') : null;
|
|
13
|
+
const lines = md.replace(/\r\n?/g, '\n').split('\n');
|
|
14
|
+
let out = '';
|
|
15
|
+
let inCode = false;
|
|
16
|
+
let codeLines = [];
|
|
17
|
+
|
|
18
|
+
const flushCode = () => {
|
|
19
|
+
if (!inCode) return;
|
|
20
|
+
const codeText = codeLines.join('\n');
|
|
21
|
+
out += '\n' + chalk.gray('┌─ code ───────────────────────────────') + '\n';
|
|
22
|
+
out += chalk.white(codeText) + '\n';
|
|
23
|
+
out += chalk.gray('└──────────────────────────────────────') + '\n';
|
|
24
|
+
inCode = false;
|
|
25
|
+
codeLines = [];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
for (let raw of lines) {
|
|
29
|
+
const line = raw.trimEnd();
|
|
30
|
+
const fenceMatch = /^```/.test(line);
|
|
31
|
+
if (fenceMatch) {
|
|
32
|
+
if (!inCode) {
|
|
33
|
+
inCode = true; codeLines = []; continue;
|
|
34
|
+
} else {
|
|
35
|
+
flushCode(); continue;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (inCode) { codeLines.push(raw); continue; }
|
|
39
|
+
|
|
40
|
+
const bullet = /^\s*([*-])\s+(.+)$/.exec(line);
|
|
41
|
+
if (bullet) {
|
|
42
|
+
const item = bullet[2]
|
|
43
|
+
.replace(/\*\*(.+?)\*\*/g, (_, t) => chalk.bold(t))
|
|
44
|
+
.replace(/`([^`]+)`/g, (_, t) => chalk.black.bgYellow(` ${t} `));
|
|
45
|
+
out += chalk.blue('• ') + item + '\n';
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (line.trim() === '') { out += '\n'; continue; }
|
|
50
|
+
|
|
51
|
+
const para = line
|
|
52
|
+
.replace(/\*\*(.+?)\*\*/g, (_, t) => chalk.bold(t))
|
|
53
|
+
.replace(/`([^`]+)`/g, (_, t) => chalk.black.bgYellow(` ${t} `));
|
|
54
|
+
out += para + '\n';
|
|
55
|
+
}
|
|
56
|
+
flushCode();
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function markdownToHtml(md) {
|
|
61
|
+
if (!md) return '';
|
|
62
|
+
// Normalize line endings
|
|
63
|
+
md = md.replace(/\r\n?/g, '\n');
|
|
64
|
+
const lines = md.split('\n');
|
|
65
|
+
let html = '';
|
|
66
|
+
let inList = false;
|
|
67
|
+
let inCode = false;
|
|
68
|
+
let codeLang = '';
|
|
69
|
+
let codeLines = [];
|
|
70
|
+
|
|
71
|
+
const flushList = () => { if (inList) { html += '</ul>'; inList = false; } };
|
|
72
|
+
|
|
73
|
+
for (let raw of lines) {
|
|
74
|
+
let line = raw.trimEnd();
|
|
75
|
+
|
|
76
|
+
// Fenced code blocks
|
|
77
|
+
const fenceMatch = /^```\s*([A-Za-z0-9_+-]*)\s*$/.exec(line);
|
|
78
|
+
if (fenceMatch) {
|
|
79
|
+
if (!inCode) {
|
|
80
|
+
// starting code block
|
|
81
|
+
flushList();
|
|
82
|
+
inCode = true;
|
|
83
|
+
codeLang = fenceMatch[1] || '';
|
|
84
|
+
codeLines = [];
|
|
85
|
+
} else {
|
|
86
|
+
// ending code block -> flush code block
|
|
87
|
+
const codeText = codeLines.join('\n');
|
|
88
|
+
const codeEsc = escapeHtml(codeText);
|
|
89
|
+
const langClass = codeLang ? `language-${codeLang}` : '';
|
|
90
|
+
html += `<div class="code-block">\n<button class="copy-btn" title="Copy code" aria-label="Copy code">Copy</button><pre><code class="${langClass}">${codeEsc}</code></pre></div>`;
|
|
91
|
+
inCode = false;
|
|
92
|
+
codeLang = '';
|
|
93
|
+
codeLines = [];
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (inCode) {
|
|
99
|
+
codeLines.push(raw); // preserve exact indentation inside code
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const bulletMatch = /^\s*([*-])\s+(.+)$/.exec(line);
|
|
104
|
+
if (bulletMatch) {
|
|
105
|
+
if (!inList) { html += '<ul>'; inList = true; }
|
|
106
|
+
const item = bulletMatch[2];
|
|
107
|
+
const itemEsc = escapeHtml(item)
|
|
108
|
+
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
109
|
+
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
110
|
+
html += `<li>${itemEsc}</li>`;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Empty line -> paragraph break
|
|
115
|
+
if (line.trim() === '') {
|
|
116
|
+
flushList(); html += '<br/>';
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Normal paragraph line
|
|
121
|
+
flushList();
|
|
122
|
+
const para = escapeHtml(line)
|
|
123
|
+
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
124
|
+
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
125
|
+
html += `<p>${para}</p>`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
flushList();
|
|
129
|
+
|
|
130
|
+
// If code block was never closed, flush it anyway
|
|
131
|
+
if (inCode) {
|
|
132
|
+
const codeText = codeLines.join('\n');
|
|
133
|
+
const codeEsc = escapeHtml(codeText);
|
|
134
|
+
const langClass = codeLang ? `language-${codeLang}` : '';
|
|
135
|
+
html += `<div class="code-block"><button class="copy-btn" title="Copy code" aria-label="Copy code">📋</button><pre><code class="${langClass}">${codeEsc}</code></pre></div>`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return html;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Optional: centralize copy button handler so renderer.js stays clean
|
|
142
|
+
function initMarkdownCopyHandler() {
|
|
143
|
+
document.addEventListener('click', async (e) => {
|
|
144
|
+
const btn = e.target.closest('.copy-btn');
|
|
145
|
+
if (!btn) return;
|
|
146
|
+
try {
|
|
147
|
+
const wrapper = btn.closest('.code-block');
|
|
148
|
+
const codeEl = wrapper ? wrapper.querySelector('code') : null;
|
|
149
|
+
const codeText = codeEl ? codeEl.textContent : '';
|
|
150
|
+
if (!codeText) return;
|
|
151
|
+
await navigator.clipboard.writeText(codeText);
|
|
152
|
+
const prev = btn.textContent;
|
|
153
|
+
btn.textContent = 'Copied!';
|
|
154
|
+
setTimeout(() => { btn.textContent = prev; }, 1200);
|
|
155
|
+
} catch (_) { }
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Expose to browser global when available
|
|
160
|
+
if (typeof window !== 'undefined') {
|
|
161
|
+
window.markdownToHtml = markdownToHtml;
|
|
162
|
+
window.initMarkdownCopyHandler = initMarkdownCopyHandler;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Support CommonJS environments if available (harmless in extension builds)
|
|
166
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
167
|
+
module.exports = { markdownToHtml, initMarkdownCopyHandler, markdownToAnsi };
|
|
168
|
+
}
|