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,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
  };
@@ -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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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
+ }