@cccarv82/freya 1.0.51 → 1.0.53
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/cli/web.js +109 -21
- package/package.json +1 -1
package/cli/web.js
CHANGED
|
@@ -8,6 +8,34 @@ const { spawn } = require('child_process');
|
|
|
8
8
|
const { searchWorkspace } = require('../scripts/lib/search-utils');
|
|
9
9
|
const { searchIndex } = require('../scripts/lib/index-utils');
|
|
10
10
|
|
|
11
|
+
function readAppVersion() {
|
|
12
|
+
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
13
|
+
try {
|
|
14
|
+
const json = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
15
|
+
if (json && typeof json.version === 'string' && json.version.trim() !== '') return json.version.trim();
|
|
16
|
+
} catch {
|
|
17
|
+
// Fall back below.
|
|
18
|
+
}
|
|
19
|
+
return 'unknown';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function escapeHtml(str) {
|
|
23
|
+
return String(str)
|
|
24
|
+
.replace(/&/g, '&')
|
|
25
|
+
.replace(/</g, '<')
|
|
26
|
+
.replace(/>/g, '>')
|
|
27
|
+
.replace(/"/g, '"')
|
|
28
|
+
.replace(/'/g, ''');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const APP_VERSION = readAppVersion();
|
|
32
|
+
|
|
33
|
+
const CHAT_ID_PATTERNS = [
|
|
34
|
+
/\bPTI\d{4,}-\d+\b/gi,
|
|
35
|
+
/\bINC\d+\b/gi,
|
|
36
|
+
/\bCHG\d+\b/gi
|
|
37
|
+
];
|
|
38
|
+
|
|
11
39
|
function guessNpmCmd() {
|
|
12
40
|
// We'll execute via cmd.exe on Windows for reliability.
|
|
13
41
|
return process.platform === 'win32' ? 'npm' : 'npm';
|
|
@@ -615,6 +643,61 @@ function isAllowedChatSearchPath(relPath) {
|
|
|
615
643
|
return relPath.startsWith('data/') || relPath.startsWith('logs/') || relPath.startsWith('docs/');
|
|
616
644
|
}
|
|
617
645
|
|
|
646
|
+
function extractChatIds(text) {
|
|
647
|
+
const tokens = new Set();
|
|
648
|
+
const q = String(text || '');
|
|
649
|
+
for (const re of CHAT_ID_PATTERNS) {
|
|
650
|
+
const matches = q.match(re);
|
|
651
|
+
if (matches) {
|
|
652
|
+
for (const m of matches) tokens.add(m.toUpperCase());
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return Array.from(tokens);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function extractProjectToken(text) {
|
|
659
|
+
const raw = String(text || '');
|
|
660
|
+
const m = raw.match(/project\s*[:=]\s*([A-Za-z0-9_\/-]+)/i);
|
|
661
|
+
if (m && m[1]) return m[1].trim();
|
|
662
|
+
const m2 = raw.match(/project\s*\(([^)]+)\)/i);
|
|
663
|
+
if (m2 && m2[1]) return m2[1].trim();
|
|
664
|
+
return '';
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function projectFromPath(relPath) {
|
|
668
|
+
const p = String(relPath || '');
|
|
669
|
+
const m = p.match(/data\/Clients\/([^/]+)\/([^/]+)/i);
|
|
670
|
+
if (m && m[1] && m[2]) return `${m[1]}/${m[2]}`;
|
|
671
|
+
return '';
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function matchKey(m) {
|
|
675
|
+
const ids = extractChatIds(`${m.file || ''} ${m.snippet || ''}`);
|
|
676
|
+
if (ids.length) return `id:${ids[0]}`;
|
|
677
|
+
const proj = projectFromPath(m.file) || extractProjectToken(`${m.file || ''} ${m.snippet || ''}`);
|
|
678
|
+
if (proj) return `proj:${proj.toLowerCase()}`;
|
|
679
|
+
return `file:${m.file || ''}`;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function mergeMatches(primary, secondary, limit = 8) {
|
|
683
|
+
const list = [];
|
|
684
|
+
const seen = new Set();
|
|
685
|
+
const push = (m) => {
|
|
686
|
+
if (!m || !m.file || !isAllowedChatSearchPath(m.file)) return;
|
|
687
|
+
const key = matchKey(m);
|
|
688
|
+
if (seen.has(key)) return;
|
|
689
|
+
seen.add(key);
|
|
690
|
+
list.push(m);
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
(primary || []).forEach(push);
|
|
694
|
+
(secondary || []).forEach(push);
|
|
695
|
+
|
|
696
|
+
const total = list.length;
|
|
697
|
+
const trimmed = list.slice(0, Math.max(1, Math.min(20, limit)));
|
|
698
|
+
return { matches: trimmed, total };
|
|
699
|
+
}
|
|
700
|
+
|
|
618
701
|
async function copilotSearch(workspaceDir, query, opts = {}) {
|
|
619
702
|
const q = String(query || '').trim();
|
|
620
703
|
if (!q) return { ok: false, error: 'Missing query' };
|
|
@@ -701,8 +784,8 @@ async function copilotSearch(workspaceDir, query, opts = {}) {
|
|
|
701
784
|
return { ok: true, answer, matches, evidence };
|
|
702
785
|
}
|
|
703
786
|
|
|
704
|
-
function buildChatAnswer(query, matches, summary, evidence, answer) {
|
|
705
|
-
const count = matches.length;
|
|
787
|
+
function buildChatAnswer(query, matches, summary, evidence, answer, totalCount) {
|
|
788
|
+
const count = typeof totalCount === 'number' ? totalCount : matches.length;
|
|
706
789
|
let summaryText = String(summary || '').trim();
|
|
707
790
|
let answerText = String(answer || '').trim();
|
|
708
791
|
if (!answerText) {
|
|
@@ -718,7 +801,7 @@ function buildChatAnswer(query, matches, summary, evidence, answer) {
|
|
|
718
801
|
|
|
719
802
|
const lines = [];
|
|
720
803
|
lines.push(`Encontrei ${count} registro(s).`);
|
|
721
|
-
lines.push(`
|
|
804
|
+
lines.push(`Resposta curta: ${answerText}`);
|
|
722
805
|
|
|
723
806
|
const evidences = Array.isArray(evidence) && evidence.length
|
|
724
807
|
? evidence
|
|
@@ -727,15 +810,15 @@ function buildChatAnswer(query, matches, summary, evidence, answer) {
|
|
|
727
810
|
if (!evidences.length) return lines.join('\n');
|
|
728
811
|
|
|
729
812
|
lines.push('');
|
|
730
|
-
lines.push('
|
|
813
|
+
lines.push('Detalhes:');
|
|
731
814
|
for (const m of evidences.slice(0, 5)) {
|
|
732
|
-
const parts = [];
|
|
733
|
-
if (m.date) parts.push(`**${m.date}**`);
|
|
734
|
-
if (m.file) parts.push('`' + m.file + '`');
|
|
735
|
-
const prefix = parts.length ? parts.join(' — ') + ':' : '';
|
|
736
815
|
const detail = (m.detail || m.snippet || '').toString().trim();
|
|
737
816
|
if (!detail) continue;
|
|
738
|
-
|
|
817
|
+
const meta = [];
|
|
818
|
+
if (m.file) meta.push(m.file);
|
|
819
|
+
if (m.date) meta.push(m.date);
|
|
820
|
+
const suffix = meta.length ? ` (${meta.join(' · ')})` : '';
|
|
821
|
+
lines.push(`- ${detail}${suffix}`);
|
|
739
822
|
}
|
|
740
823
|
|
|
741
824
|
return lines.join('\n');
|
|
@@ -795,10 +878,11 @@ async function pickDirectoryNative() {
|
|
|
795
878
|
function html(defaultDir) {
|
|
796
879
|
// Aesthetic: “clean workstation” — light-first UI inspired by modern productivity apps.
|
|
797
880
|
const safeDefault = String(defaultDir || './freya').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
798
|
-
return buildHtml(safeDefault);
|
|
881
|
+
return buildHtml(safeDefault, APP_VERSION);
|
|
799
882
|
}
|
|
800
883
|
|
|
801
|
-
function buildHtml(safeDefault) {
|
|
884
|
+
function buildHtml(safeDefault, appVersion) {
|
|
885
|
+
const safeVersion = escapeHtml(appVersion || 'unknown');
|
|
802
886
|
return `<!doctype html>
|
|
803
887
|
<html>
|
|
804
888
|
<head>
|
|
@@ -861,6 +945,7 @@ function buildHtml(safeDefault) {
|
|
|
861
945
|
<div class="topbar">
|
|
862
946
|
<div class="brand"><span class="spark"></span> Assistente de status local-first</div>
|
|
863
947
|
<div class="actions">
|
|
948
|
+
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
864
949
|
<span class="chip" id="chipPort">127.0.0.1:3872</span>
|
|
865
950
|
<button class="toggle" id="themeToggle" onclick="toggleTheme()">Escuro</button>
|
|
866
951
|
</div>
|
|
@@ -1757,24 +1842,27 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
1757
1842
|
if (!query) return safeJson(res, 400, { error: 'Missing query' });
|
|
1758
1843
|
|
|
1759
1844
|
const copilotResult = await copilotSearch(workspaceDir, query, { limit: 8 });
|
|
1845
|
+
const indexMatches = searchIndex(workspaceDir, query, { limit: 12 });
|
|
1846
|
+
const baseMatches = indexMatches.length
|
|
1847
|
+
? indexMatches
|
|
1848
|
+
: searchWorkspace(workspaceDir, query, { limit: 12 });
|
|
1849
|
+
|
|
1760
1850
|
if (copilotResult.ok) {
|
|
1761
|
-
const
|
|
1851
|
+
const merged = mergeMatches(copilotResult.matches || [], baseMatches, 8);
|
|
1762
1852
|
const answer = buildChatAnswer(
|
|
1763
1853
|
query,
|
|
1764
|
-
matches,
|
|
1854
|
+
merged.matches,
|
|
1765
1855
|
copilotResult.summary,
|
|
1766
1856
|
copilotResult.evidence,
|
|
1767
|
-
copilotResult.answer
|
|
1857
|
+
copilotResult.answer,
|
|
1858
|
+
merged.total
|
|
1768
1859
|
);
|
|
1769
|
-
return safeJson(res, 200, { ok: true, sessionId, answer, matches });
|
|
1860
|
+
return safeJson(res, 200, { ok: true, sessionId, answer, matches: merged.matches });
|
|
1770
1861
|
}
|
|
1771
1862
|
|
|
1772
|
-
const
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1775
|
-
: searchWorkspace(workspaceDir, query, { limit: 8 });
|
|
1776
|
-
const answer = buildChatAnswer(query, matches, '');
|
|
1777
|
-
return safeJson(res, 200, { ok: true, sessionId, answer, matches });
|
|
1863
|
+
const merged = mergeMatches(baseMatches, [], 8);
|
|
1864
|
+
const answer = buildChatAnswer(query, merged.matches, '', [], '', merged.total);
|
|
1865
|
+
return safeJson(res, 200, { ok: true, sessionId, answer, matches: merged.matches });
|
|
1778
1866
|
}
|
|
1779
1867
|
|
|
1780
1868
|
// Chat persistence (per session)
|