tokenos 1.1.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.md +571 -0
- package/USAGE.md +451 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +60 -0
- package/dist/config.js.map +1 -0
- package/dist/db/connection.d.ts +3 -0
- package/dist/db/connection.d.ts.map +1 -0
- package/dist/db/connection.js +78 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/index.d.ts +4 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +4 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/memory.d.ts +6 -0
- package/dist/db/memory.d.ts.map +1 -0
- package/dist/db/memory.js +62 -0
- package/dist/db/memory.js.map +1 -0
- package/dist/db/queries.d.ts +29 -0
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +215 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/embeddings/client.d.ts +16 -0
- package/dist/embeddings/client.d.ts.map +1 -0
- package/dist/embeddings/client.js +70 -0
- package/dist/embeddings/client.js.map +1 -0
- package/dist/embeddings/index.d.ts +11 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +37 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/similarity.d.ts +7 -0
- package/dist/embeddings/similarity.d.ts.map +1 -0
- package/dist/embeddings/similarity.js +31 -0
- package/dist/embeddings/similarity.js.map +1 -0
- package/dist/indexer/cli.d.ts +8 -0
- package/dist/indexer/cli.d.ts.map +1 -0
- package/dist/indexer/cli.js +21 -0
- package/dist/indexer/cli.js.map +1 -0
- package/dist/indexer/ignore.d.ts +4 -0
- package/dist/indexer/ignore.d.ts.map +1 -0
- package/dist/indexer/ignore.js +30 -0
- package/dist/indexer/ignore.js.map +1 -0
- package/dist/indexer/index.d.ts +5 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +4 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/indexer.d.ts +13 -0
- package/dist/indexer/indexer.d.ts.map +1 -0
- package/dist/indexer/indexer.js +125 -0
- package/dist/indexer/indexer.js.map +1 -0
- package/dist/indexer/parser.d.ts +10 -0
- package/dist/indexer/parser.d.ts.map +1 -0
- package/dist/indexer/parser.js +444 -0
- package/dist/indexer/parser.js.map +1 -0
- package/dist/indexer/watcher.d.ts +7 -0
- package/dist/indexer/watcher.d.ts.map +1 -0
- package/dist/indexer/watcher.js +64 -0
- package/dist/indexer/watcher.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +92 -0
- package/dist/main.js.map +1 -0
- package/dist/reset.d.ts +6 -0
- package/dist/reset.d.ts.map +1 -0
- package/dist/reset.js +23 -0
- package/dist/reset.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/server.d.ts +4 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +558 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/visualize.d.ts +2 -0
- package/dist/server/visualize.d.ts.map +1 -0
- package/dist/server/visualize.js +299 -0
- package/dist/server/visualize.js.map +1 -0
- package/dist/test-phase1.d.ts +13 -0
- package/dist/test-phase1.d.ts.map +1 -0
- package/dist/test-phase1.js +90 -0
- package/dist/test-phase1.js.map +1 -0
- package/dist/test-phase2.d.ts +13 -0
- package/dist/test-phase2.d.ts.map +1 -0
- package/dist/test-phase2.js +110 -0
- package/dist/test-phase2.js.map +1 -0
- package/dist/test-phase3.d.ts +12 -0
- package/dist/test-phase3.d.ts.map +1 -0
- package/dist/test-phase3.js +85 -0
- package/dist/test-phase3.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cache.d.ts +12 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +45 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/scoring.d.ts +15 -0
- package/dist/utils/scoring.d.ts.map +1 -0
- package/dist/utils/scoring.js +17 -0
- package/dist/utils/scoring.js.map +1 -0
- package/dist/verify-parser.d.ts +6 -0
- package/dist/verify-parser.d.ts.map +1 -0
- package/dist/verify-parser.js +105 -0
- package/dist/verify-parser.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { createServer as createHttpServer } from "http";
|
|
2
|
+
import { getAllNodes, getNeighbors } from "../db/index.js";
|
|
3
|
+
const PORT = parseInt(process.env.GRAPH_UI_PORT ?? "3333", 10);
|
|
4
|
+
// Minimal HTML page with a vis-network force-directed graph
|
|
5
|
+
const GRAPH_HTML = `<!DOCTYPE html>
|
|
6
|
+
<html lang="en">
|
|
7
|
+
<head>
|
|
8
|
+
<meta charset="UTF-8" />
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
10
|
+
<title>TokenOS — Codebase Visualization</title>
|
|
11
|
+
<script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
|
|
12
|
+
<style>
|
|
13
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
14
|
+
body { background: #0f1117; color: #e1e4e8; font-family: system-ui, sans-serif; height: 100vh; display: flex; flex-direction: column; }
|
|
15
|
+
header { padding: 12px 20px; background: #161b22; border-bottom: 1px solid #30363d; display: flex; align-items: center; gap: 12px; }
|
|
16
|
+
header h1 { font-size: 16px; font-weight: 600; color: #58a6ff; }
|
|
17
|
+
header small { color: #8b949e; font-size: 12px; }
|
|
18
|
+
#graph { flex: 1; }
|
|
19
|
+
#info { position: absolute; top: 60px; right: 16px; background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 12px 16px; max-width: 300px; display: none; font-size: 13px; }
|
|
20
|
+
#info h3 { color: #58a6ff; margin-bottom: 6px; }
|
|
21
|
+
#info p { color: #8b949e; line-height: 1.5; }
|
|
22
|
+
.tag { display: inline-block; padding: 1px 6px; border-radius: 4px; font-size: 11px; margin-left: 6px; }
|
|
23
|
+
.function { background: #1f6feb; } .class { background: #388bfd; }
|
|
24
|
+
.file { background: #56d364; color: #0d1117; } .import { background: #6e40c9; } .variable { background: #f78166; color: #0d1117; }
|
|
25
|
+
</style>
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
<header>
|
|
29
|
+
<h1>⬡ TokenOS</h1>
|
|
30
|
+
<small id="stats">Loading...</small>
|
|
31
|
+
</header>
|
|
32
|
+
<div id="graph"></div>
|
|
33
|
+
<div id="info"><h3 id="info-name"></h3><p id="info-detail"></p></div>
|
|
34
|
+
<script>
|
|
35
|
+
const TYPE_COLORS = {
|
|
36
|
+
function: { background: '#1f6feb', border: '#388bfd' },
|
|
37
|
+
class: { background: '#388bfd', border: '#58a6ff' },
|
|
38
|
+
file: { background: '#56d364', border: '#3fb950', font: { color: '#0d1117' } },
|
|
39
|
+
import: { background: '#6e40c9', border: '#a371f7' },
|
|
40
|
+
variable: { background: '#f78166', border: '#ffa198', font: { color: '#0d1117' } },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
async function init() {
|
|
44
|
+
const res = await fetch('/api/graph-data');
|
|
45
|
+
const { nodes: rawNodes, edges: rawEdges } = await res.json();
|
|
46
|
+
|
|
47
|
+
document.getElementById('stats').textContent =
|
|
48
|
+
rawNodes.length + ' nodes · ' + rawEdges.length + ' edges';
|
|
49
|
+
|
|
50
|
+
const nodes = new vis.DataSet(rawNodes.map(n => ({
|
|
51
|
+
id: n.id,
|
|
52
|
+
label: n.name,
|
|
53
|
+
title: n.id,
|
|
54
|
+
value: Math.max(1, n.importance),
|
|
55
|
+
color: TYPE_COLORS[n.type] ?? { background: '#21262d', border: '#30363d' },
|
|
56
|
+
font: { color: '#e1e4e8', size: 12 },
|
|
57
|
+
borderWidth: 1.5,
|
|
58
|
+
})));
|
|
59
|
+
|
|
60
|
+
const edges = new vis.DataSet(rawEdges.map(e => ({
|
|
61
|
+
from: e.from_node, to: e.to_node,
|
|
62
|
+
label: e.type,
|
|
63
|
+
arrows: 'to',
|
|
64
|
+
color: { color: '#30363d', highlight: '#58a6ff' },
|
|
65
|
+
font: { color: '#8b949e', size: 10, align: 'middle' },
|
|
66
|
+
smooth: { type: 'curvedCW', roundness: 0.15 },
|
|
67
|
+
})));
|
|
68
|
+
|
|
69
|
+
const container = document.getElementById('graph');
|
|
70
|
+
const isLarge = nodes.length > 500;
|
|
71
|
+
const network = new vis.Network(container, { nodes, edges }, {
|
|
72
|
+
physics: isLarge ? false : { stabilization: { iterations: 150 }, barnesHut: { gravitationalConstant: -3000 } },
|
|
73
|
+
layout: isLarge ? { improvedLayout: false } : undefined,
|
|
74
|
+
interaction: { hover: true, navigationButtons: true, keyboard: true },
|
|
75
|
+
edges: { smooth: isLarge ? false : { type: 'curvedCW', roundness: 0.15 } }
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
network.on('click', params => {
|
|
79
|
+
if (!params.nodes.length) { document.getElementById('info').style.display = 'none'; return; }
|
|
80
|
+
const n = rawNodes.find(x => x.id === params.nodes[0]);
|
|
81
|
+
if (!n) return;
|
|
82
|
+
document.getElementById('info').style.display = 'block';
|
|
83
|
+
document.getElementById('info-name').textContent = n.name + ' (' + n.type + ')';
|
|
84
|
+
document.getElementById('info-detail').innerHTML =
|
|
85
|
+
'<b>File:</b> ' + n.file_path.split('/').slice(-2).join('/') +
|
|
86
|
+
(n.summary ? '<br><b>Summary:</b> ' + n.summary : '') +
|
|
87
|
+
'<br><b>Importance:</b> ' + n.importance;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
init();
|
|
91
|
+
</script>
|
|
92
|
+
</body>
|
|
93
|
+
</html>`;
|
|
94
|
+
const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
95
|
+
<html lang="en">
|
|
96
|
+
<head>
|
|
97
|
+
<meta charset="UTF-8">
|
|
98
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
99
|
+
<title>TokenOS Dashboard</title>
|
|
100
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
101
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
|
102
|
+
<style>
|
|
103
|
+
body { background: #0f1117; color: #e1e4e8; font-family: system-ui, sans-serif; overflow-x: hidden; }
|
|
104
|
+
/* Antigravity Glassmorphism */
|
|
105
|
+
.glass-card {
|
|
106
|
+
background: rgba(22, 27, 34, 0.4);
|
|
107
|
+
backdrop-filter: blur(12px);
|
|
108
|
+
-webkit-backdrop-filter: blur(12px);
|
|
109
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
110
|
+
border-radius: 16px;
|
|
111
|
+
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
|
|
112
|
+
transition: transform 0.3s ease-out, box-shadow 0.3s ease-out;
|
|
113
|
+
will-change: transform;
|
|
114
|
+
}
|
|
115
|
+
.glass-card:hover {
|
|
116
|
+
transform: translateY(-5px);
|
|
117
|
+
box-shadow: 0 30px 60px rgba(0,0,0,0.3), 0 0 20px rgba(88, 166, 255, 0.1);
|
|
118
|
+
border: 1px solid rgba(88, 166, 255, 0.2);
|
|
119
|
+
}
|
|
120
|
+
/* Floating glow effect */
|
|
121
|
+
.glow-orb {
|
|
122
|
+
position: absolute; width: 400px; height: 400px; background: radial-gradient(circle, rgba(88,166,255,0.1) 0%, rgba(0,0,0,0) 70%);
|
|
123
|
+
top: -100px; left: -100px; z-index: -1; pointer-events: none;
|
|
124
|
+
}
|
|
125
|
+
/* 3D Isometric container */
|
|
126
|
+
.iso-grid { perspective: 1000px; }
|
|
127
|
+
.tag { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: 11px; font-weight: 500; }
|
|
128
|
+
.tag.function { background: rgba(31, 111, 235, 0.2); color: #58a6ff; border: 1px solid rgba(31, 111, 235, 0.3); }
|
|
129
|
+
.tag.class { background: rgba(56, 139, 253, 0.2); color: #79c0ff; border: 1px solid rgba(56, 139, 253, 0.3); }
|
|
130
|
+
.tag.file { background: rgba(86, 211, 100, 0.2); color: #7ee787; border: 1px solid rgba(86, 211, 100, 0.3); }
|
|
131
|
+
.tag.import { background: rgba(110, 64, 201, 0.2); color: #d2a8ff; border: 1px solid rgba(110, 64, 201, 0.3); }
|
|
132
|
+
.tag.variable { background: rgba(247, 129, 102, 0.2); color: #ffbba6; border: 1px solid rgba(247, 129, 102, 0.3); }
|
|
133
|
+
|
|
134
|
+
/* Smooth Scrollbar */
|
|
135
|
+
::-webkit-scrollbar { width: 8px; }
|
|
136
|
+
::-webkit-scrollbar-track { background: #0f1117; }
|
|
137
|
+
::-webkit-scrollbar-thumb { background: #30363d; border-radius: 4px; }
|
|
138
|
+
</style>
|
|
139
|
+
</head>
|
|
140
|
+
<body class="min-h-screen p-8 relative">
|
|
141
|
+
<div class="glow-orb"></div>
|
|
142
|
+
<div class="glow-orb" style="top: auto; bottom: -100px; right: -100px; left: auto; background: radial-gradient(circle, rgba(163,113,247,0.1) 0%, rgba(0,0,0,0) 70%);"></div>
|
|
143
|
+
|
|
144
|
+
<div class="max-w-6xl mx-auto">
|
|
145
|
+
<header class="flex justify-between items-center mb-12 animate-header">
|
|
146
|
+
<div>
|
|
147
|
+
<h1 class="text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-500 tracking-tight">TokenOS Dashboard</h1>
|
|
148
|
+
<p class="text-gray-400 mt-2">Spatial analysis of your codebase.</p>
|
|
149
|
+
</div>
|
|
150
|
+
<a href="/graph" class="glass-card px-6 py-3 font-medium text-blue-400 hover:text-blue-300">
|
|
151
|
+
Open 3D Visualization →
|
|
152
|
+
</a>
|
|
153
|
+
</header>
|
|
154
|
+
|
|
155
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-12 iso-grid" id="stats-grid"></div>
|
|
156
|
+
|
|
157
|
+
<h2 class="text-2xl font-semibold mb-6 text-gray-200">Top Neural Nodes</h2>
|
|
158
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6" id="nodes-grid"></div>
|
|
159
|
+
|
|
160
|
+
<h2 class="text-2xl font-semibold mt-16 mb-6 text-gray-200">Context Explorer</h2>
|
|
161
|
+
<div class="glass-card overflow-hidden animate-table">
|
|
162
|
+
<div class="overflow-x-auto">
|
|
163
|
+
<table class="w-full text-left border-collapse">
|
|
164
|
+
<thead>
|
|
165
|
+
<tr class="bg-black/20 text-gray-400 text-xs uppercase tracking-wider">
|
|
166
|
+
<th class="py-4 px-6 font-semibold">Rank</th>
|
|
167
|
+
<th class="py-4 px-6 font-semibold">Node Name</th>
|
|
168
|
+
<th class="py-4 px-6 font-semibold">Type</th>
|
|
169
|
+
<th class="py-4 px-6 font-semibold">File Source</th>
|
|
170
|
+
<th class="py-4 px-6 font-semibold text-right">Importance Score</th>
|
|
171
|
+
</tr>
|
|
172
|
+
</thead>
|
|
173
|
+
<tbody id="table-body">
|
|
174
|
+
<!-- Table content here -->
|
|
175
|
+
</tbody>
|
|
176
|
+
</table>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<script>
|
|
182
|
+
async function loadData() {
|
|
183
|
+
const res = await fetch('/api/stats');
|
|
184
|
+
const data = await res.json();
|
|
185
|
+
|
|
186
|
+
const statsGrid = document.getElementById('stats-grid');
|
|
187
|
+
const stats = [
|
|
188
|
+
{ label: 'Total Nodes', value: data.totalNodes },
|
|
189
|
+
{ label: 'Files', value: data.files },
|
|
190
|
+
{ label: 'Classes', value: data.classes },
|
|
191
|
+
{ label: 'Functions', value: data.functions }
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
statsGrid.innerHTML = stats.map(s => \`
|
|
195
|
+
<div class="glass-card p-6 flex flex-col justify-center items-center stat-card relative overflow-hidden group">
|
|
196
|
+
<div class="absolute inset-0 bg-gradient-to-b from-white/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity"></div>
|
|
197
|
+
<span class="text-4xl font-bold text-white mb-2">\${s.value.toLocaleString()}</span>
|
|
198
|
+
<span class="text-sm font-medium text-gray-400 uppercase tracking-widest">\${s.label}</span>
|
|
199
|
+
</div>
|
|
200
|
+
\`).join('');
|
|
201
|
+
|
|
202
|
+
const nodesGrid = document.getElementById('nodes-grid');
|
|
203
|
+
nodesGrid.innerHTML = data.topNodes.map(n => \`
|
|
204
|
+
<div class="glass-card p-6 node-card flex flex-col h-full relative overflow-hidden group">
|
|
205
|
+
<div class="absolute top-0 right-0 w-32 h-32 bg-blue-500/10 rounded-full blur-3xl -mr-10 -mt-10 group-hover:bg-blue-400/20 transition-colors"></div>
|
|
206
|
+
<div class="flex justify-between items-start mb-4 relative z-10">
|
|
207
|
+
<h3 class="font-mono text-gray-200 truncate pr-4 text-sm font-semibold" title="\${n.name}">\${n.name}</h3>
|
|
208
|
+
<span class="tag \${n.type}">\${n.type}</span>
|
|
209
|
+
</div>
|
|
210
|
+
<p class="text-xs text-gray-500 font-mono mb-4 truncate relative z-10" title="\${n.file_path}">\${n.file_path.split('/').pop()}</p>
|
|
211
|
+
<div class="mt-auto pt-4 border-t border-gray-800 flex justify-between items-center relative z-10">
|
|
212
|
+
<span class="text-xs text-gray-400">Importance Score</span>
|
|
213
|
+
<span class="text-sm font-bold text-blue-400">\${Number(n.importance).toFixed(1)}</span>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
\`).join('');
|
|
217
|
+
|
|
218
|
+
const tableBody = document.getElementById('table-body');
|
|
219
|
+
tableBody.innerHTML = data.topNodes.slice(0, 100).map((n, i) =>
|
|
220
|
+
'<tr class="border-b border-gray-800/50 hover:bg-white/5 transition-colors cursor-pointer group">' +
|
|
221
|
+
'<td class="py-4 px-6 text-sm text-gray-500 font-mono">' + (i + 1) + '</td>' +
|
|
222
|
+
'<td class="py-4 px-6 text-sm font-medium text-gray-200 group-hover:text-blue-400 transition-colors">' +
|
|
223
|
+
'<div class="truncate max-w-[250px]" title="' + n.name + '">' + n.name + '</div>' +
|
|
224
|
+
'</td>' +
|
|
225
|
+
'<td class="py-4 px-6"><span class="tag ' + n.type + '">' + n.type + '</span></td>' +
|
|
226
|
+
'<td class="py-4 px-6 text-sm text-gray-400">' +
|
|
227
|
+
'<div class="truncate max-w-[250px]" title="' + n.file_path + '">' + n.file_path.split('/').pop() + '</div>' +
|
|
228
|
+
'</td>' +
|
|
229
|
+
'<td class="py-4 px-6 text-sm font-bold text-blue-400 text-right">' + Number(n.importance).toFixed(1) + '</td>' +
|
|
230
|
+
'</tr>'
|
|
231
|
+
).join('');
|
|
232
|
+
|
|
233
|
+
/* GSAP Antigravity Animations */
|
|
234
|
+
gsap.from(".animate-header", { y: -30, opacity: 0, duration: 0.8, ease: "power3.out" });
|
|
235
|
+
gsap.from(".stat-card", { y: 50, opacity: 0, duration: 0.8, stagger: 0.1, ease: "back.out(1.5)" });
|
|
236
|
+
gsap.from(".node-card", { y: 40, opacity: 0, duration: 0.8, stagger: 0.05, ease: "power2.out", delay: 0.3 });
|
|
237
|
+
gsap.from(".animate-table", { y: 30, opacity: 0, duration: 0.8, ease: "power2.out", delay: 0.6 });
|
|
238
|
+
|
|
239
|
+
gsap.to(".stat-card", { y: -5, duration: 2, yoyo: true, repeat: -1, ease: "sine.inOut", stagger: 0.2, delay: 1 });
|
|
240
|
+
}
|
|
241
|
+
loadData();
|
|
242
|
+
</script>
|
|
243
|
+
</body>
|
|
244
|
+
</html>`;
|
|
245
|
+
export async function startVisualizationServer() {
|
|
246
|
+
const server = createHttpServer((req, res) => {
|
|
247
|
+
if (req.url === "/api/stats") {
|
|
248
|
+
const nodes = getAllNodes();
|
|
249
|
+
const stats = {
|
|
250
|
+
totalNodes: nodes.length,
|
|
251
|
+
files: nodes.filter(n => n.type === "file").length,
|
|
252
|
+
functions: nodes.filter(n => n.type === "function").length,
|
|
253
|
+
classes: nodes.filter(n => n.type === "class").length,
|
|
254
|
+
topNodes: nodes.sort((a, b) => b.importance - a.importance).slice(0, 50)
|
|
255
|
+
};
|
|
256
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
257
|
+
res.end(JSON.stringify(stats));
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (req.url === "/api/graph-data") {
|
|
261
|
+
let nodes = getAllNodes();
|
|
262
|
+
// If the graph is massive, limit it to the top 1500 most important nodes
|
|
263
|
+
// to prevent vis-network from crashing the browser's rendering engine.
|
|
264
|
+
if (nodes.length > 1500) {
|
|
265
|
+
nodes = nodes.sort((a, b) => b.importance - a.importance).slice(0, 1500);
|
|
266
|
+
}
|
|
267
|
+
// Get only the edges that connect these filtered nodes
|
|
268
|
+
const nodeIds = new Set(nodes.map(n => n.id));
|
|
269
|
+
const edges = nodes.flatMap((n) => getNeighbors(n.id)).filter(e => nodeIds.has(e.to_node) && nodeIds.has(e.from_node));
|
|
270
|
+
// Deduplicate edges
|
|
271
|
+
const seen = new Set();
|
|
272
|
+
const uniqueEdges = edges.filter((e) => {
|
|
273
|
+
if (e.id === undefined || seen.has(e.id))
|
|
274
|
+
return false;
|
|
275
|
+
seen.add(e.id);
|
|
276
|
+
return true;
|
|
277
|
+
});
|
|
278
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
279
|
+
res.end(JSON.stringify({ nodes, edges: uniqueEdges }));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (req.url === "/graph") {
|
|
283
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
284
|
+
res.end(GRAPH_HTML);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
// Default route: Dashboard
|
|
288
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
289
|
+
res.end(DASHBOARD_HTML);
|
|
290
|
+
});
|
|
291
|
+
await new Promise((resolve, reject) => {
|
|
292
|
+
server.listen(PORT, () => {
|
|
293
|
+
// UI URL is logged by the Vite-like header in main.ts
|
|
294
|
+
resolve();
|
|
295
|
+
});
|
|
296
|
+
server.on("error", reject);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=visualize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualize.js","sourceRoot":"","sources":["../../src/server/visualize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 1 smoke test — run with: npm run test:phase1
|
|
3
|
+
*
|
|
4
|
+
* Verifies:
|
|
5
|
+
* 1. Indexing a fixture file creates expected nodes and edges
|
|
6
|
+
* 2. get_node works
|
|
7
|
+
* 3. getNodesByName works
|
|
8
|
+
* 4. getNeighbors works
|
|
9
|
+
* 5. getConnectedNodes works
|
|
10
|
+
* 6. Re-indexing unchanged file is skipped (hash check)
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=test-phase1.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-phase1.d.ts","sourceRoot":"","sources":["../src/test-phase1.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 1 smoke test — run with: npm run test:phase1
|
|
3
|
+
*
|
|
4
|
+
* Verifies:
|
|
5
|
+
* 1. Indexing a fixture file creates expected nodes and edges
|
|
6
|
+
* 2. get_node works
|
|
7
|
+
* 3. getNodesByName works
|
|
8
|
+
* 4. getNeighbors works
|
|
9
|
+
* 5. getConnectedNodes works
|
|
10
|
+
* 6. Re-indexing unchanged file is skipped (hash check)
|
|
11
|
+
*/
|
|
12
|
+
import { resolve, dirname } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { indexFile } from "./indexer/indexer.js";
|
|
15
|
+
import { getNode, getNodesByName, getNeighbors, getConnectedNodes, getNodesByType, deleteNodesByFile, deleteEdgesByFile, } from "./db/index.js";
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const FIXTURE = resolve(__dirname, "../test-fixtures/sample.ts");
|
|
18
|
+
let passed = 0;
|
|
19
|
+
let failed = 0;
|
|
20
|
+
function assert(label, condition, detail) {
|
|
21
|
+
if (condition) {
|
|
22
|
+
console.log(` ✅ ${label}`);
|
|
23
|
+
passed++;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
console.log(` ❌ ${label}`, detail ?? "");
|
|
27
|
+
failed++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function run() {
|
|
31
|
+
console.log("\n=== Phase 1 Smoke Test ===\n");
|
|
32
|
+
// ── Step 1: Index the fixture file
|
|
33
|
+
console.log("1. Indexing test-fixtures/sample.ts...");
|
|
34
|
+
// Clear any previous data for this fixture so the index is never skipped
|
|
35
|
+
deleteEdgesByFile(FIXTURE);
|
|
36
|
+
deleteNodesByFile(FIXTURE);
|
|
37
|
+
const result = await indexFile(FIXTURE);
|
|
38
|
+
assert("indexFile returns nodes > 0", result.nodes > 0, result);
|
|
39
|
+
assert("indexFile returns edges > 0", result.edges > 0, result);
|
|
40
|
+
assert("indexFile is not skipped", !result.skipped, result);
|
|
41
|
+
console.log(` → ${result.nodes} nodes, ${result.edges} edges\n`);
|
|
42
|
+
// ── Step 2: Check expected nodes exist
|
|
43
|
+
console.log("2. Checking expected nodes...");
|
|
44
|
+
const addFn = getNode(`${FIXTURE}::add`);
|
|
45
|
+
assert("function 'add' exists", !!addFn, addFn);
|
|
46
|
+
assert("add node has type=function", addFn?.type === "function");
|
|
47
|
+
assert("add node has file_path", addFn?.file_path === FIXTURE);
|
|
48
|
+
const calcClass = getNode(`${FIXTURE}::Calculator`);
|
|
49
|
+
assert("class 'Calculator' exists", !!calcClass, calcClass);
|
|
50
|
+
assert("Calculator node has type=class", calcClass?.type === "class");
|
|
51
|
+
const fileNode = getNode(`${FIXTURE}::sample`);
|
|
52
|
+
assert("file node 'sample' exists", !!fileNode, fileNode);
|
|
53
|
+
assert("file node has type=file", fileNode?.type === "file");
|
|
54
|
+
// ── Step 3: Search by name
|
|
55
|
+
console.log("\n3. Searching by name...");
|
|
56
|
+
const greetResults = getNodesByName("greet");
|
|
57
|
+
assert("getNodesByName('greet') returns results", greetResults.length > 0);
|
|
58
|
+
const calcResults = getNodesByName("Calculator");
|
|
59
|
+
assert("getNodesByName('Calculator') returns results", calcResults.length > 0);
|
|
60
|
+
// ── Step 4: Get neighbors
|
|
61
|
+
console.log("\n4. Getting neighbors...");
|
|
62
|
+
const fileNodeId = `${FIXTURE}::sample`;
|
|
63
|
+
const neighbors = getNeighbors(fileNodeId);
|
|
64
|
+
assert("file node has neighbors (DEFINES edges)", neighbors.length > 0, neighbors.length);
|
|
65
|
+
// ── Step 5: Get connected nodes
|
|
66
|
+
console.log("\n5. Getting connected nodes...");
|
|
67
|
+
const connected = getConnectedNodes(fileNodeId);
|
|
68
|
+
assert("file node has connected nodes", connected.length > 0, connected.length);
|
|
69
|
+
// ── Step 6: Functions and classes visible by type
|
|
70
|
+
console.log("\n6. Checking node types...");
|
|
71
|
+
const functions = getNodesByType("function");
|
|
72
|
+
assert("functions exist in DB", functions.length > 0, functions.length);
|
|
73
|
+
const classes = getNodesByType("class");
|
|
74
|
+
assert("classes exist in DB", classes.length > 0, classes.length);
|
|
75
|
+
const imports = getNodesByType("import");
|
|
76
|
+
assert("imports exist in DB", imports.length > 0, imports.length);
|
|
77
|
+
// ── Step 7: Re-index — should be skipped (hash unchanged)
|
|
78
|
+
console.log("\n7. Re-indexing (should be skipped via hash)...");
|
|
79
|
+
const result2 = await indexFile(FIXTURE);
|
|
80
|
+
assert("re-index is skipped (hash unchanged)", result2.skipped, result2);
|
|
81
|
+
// ── Summary
|
|
82
|
+
console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`);
|
|
83
|
+
if (failed > 0)
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
run().catch((err) => {
|
|
87
|
+
console.error("Test runner error:", err);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=test-phase1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-phase1.js","sourceRoot":"","sources":["../src/test-phase1.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 2 smoke test — run with: npm run test:phase2
|
|
3
|
+
*
|
|
4
|
+
* Verifies:
|
|
5
|
+
* 1. Ollama connectivity check
|
|
6
|
+
* 2. generateEmbedding returns a float array
|
|
7
|
+
* 3. cosineSimilarity works correctly
|
|
8
|
+
* 4. backfillEmbeddings stores embeddings in DB
|
|
9
|
+
* 5. Nodes after backfill have embeddings
|
|
10
|
+
* 6. rankBySimilarity returns ordered results
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=test-phase2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-phase2.d.ts","sourceRoot":"","sources":["../src/test-phase2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 2 smoke test — run with: npm run test:phase2
|
|
3
|
+
*
|
|
4
|
+
* Verifies:
|
|
5
|
+
* 1. Ollama connectivity check
|
|
6
|
+
* 2. generateEmbedding returns a float array
|
|
7
|
+
* 3. cosineSimilarity works correctly
|
|
8
|
+
* 4. backfillEmbeddings stores embeddings in DB
|
|
9
|
+
* 5. Nodes after backfill have embeddings
|
|
10
|
+
* 6. rankBySimilarity returns ordered results
|
|
11
|
+
*/
|
|
12
|
+
import { resolve, dirname } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { indexFile } from "./indexer/indexer.js";
|
|
15
|
+
import { getNode, deleteNodesByFile, deleteEdgesByFile } from "./db/index.js";
|
|
16
|
+
import { generateEmbedding, buildEmbeddingInput } from "./embeddings/client.js";
|
|
17
|
+
import { cosineSimilarity, rankBySimilarity } from "./embeddings/similarity.js";
|
|
18
|
+
import { backfillEmbeddings } from "./embeddings/index.js";
|
|
19
|
+
import { getAllNodes } from "./db/index.js";
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const FIXTURE = resolve(__dirname, "../test-fixtures/sample.ts");
|
|
22
|
+
let passed = 0;
|
|
23
|
+
let failed = 0;
|
|
24
|
+
function assert(label, condition, detail) {
|
|
25
|
+
if (condition) {
|
|
26
|
+
console.log(` ✅ ${label}`);
|
|
27
|
+
passed++;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(` ❌ ${label}`, detail ?? "");
|
|
31
|
+
failed++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function run() {
|
|
35
|
+
console.log("\n=== Phase 2 Smoke Test (Embeddings) ===\n");
|
|
36
|
+
// ── Step 1: Check Ollama connectivity
|
|
37
|
+
console.log("1. Checking Ollama connectivity...");
|
|
38
|
+
const testEmbedding = await generateEmbedding("test connection");
|
|
39
|
+
if (!testEmbedding) {
|
|
40
|
+
console.log(" ⚠️ Ollama is offline — skipping embedding tests");
|
|
41
|
+
console.log(" Start Ollama with: ollama serve");
|
|
42
|
+
console.log(" Pull model with: ollama pull nomic-embed-text");
|
|
43
|
+
console.log("\n=== Results: 0 passed (skipped — Ollama offline) ===\n");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
assert("generateEmbedding returns an array", Array.isArray(testEmbedding));
|
|
47
|
+
assert("embedding has length > 0", testEmbedding.length > 0, testEmbedding.length);
|
|
48
|
+
assert("embedding contains numbers", typeof testEmbedding[0] === "number");
|
|
49
|
+
console.log(` → Embedding dimension: ${testEmbedding.length}\n`);
|
|
50
|
+
// ── Step 2: buildEmbeddingInput format
|
|
51
|
+
console.log("2. Testing embedding input format...");
|
|
52
|
+
const input = buildEmbeddingInput({
|
|
53
|
+
name: "add",
|
|
54
|
+
type: "function",
|
|
55
|
+
summary: "Adds two numbers",
|
|
56
|
+
codeSnippet: "function add(a, b) { return a + b; }",
|
|
57
|
+
});
|
|
58
|
+
assert("buildEmbeddingInput contains [NAME]", input.includes("[NAME]"));
|
|
59
|
+
assert("buildEmbeddingInput contains [TYPE]", input.includes("[TYPE]"));
|
|
60
|
+
assert("buildEmbeddingInput contains [SUMMARY]", input.includes("[SUMMARY]"));
|
|
61
|
+
assert("buildEmbeddingInput contains [CODE]", input.includes("[CODE]"));
|
|
62
|
+
// ── Step 3: Cosine similarity
|
|
63
|
+
console.log("\n3. Testing cosine similarity...");
|
|
64
|
+
const a = [1, 0, 0];
|
|
65
|
+
const b = [1, 0, 0];
|
|
66
|
+
const c = [0, 1, 0];
|
|
67
|
+
assert("identical vectors → similarity 1.0", cosineSimilarity(a, b) === 1.0);
|
|
68
|
+
assert("orthogonal vectors → similarity 0.0", cosineSimilarity(a, c) === 0.0);
|
|
69
|
+
const simAB = cosineSimilarity(testEmbedding, testEmbedding);
|
|
70
|
+
assert("same embedding → similarity 1.0", Math.abs(simAB - 1.0) < 1e-6, simAB);
|
|
71
|
+
// ── Step 4: Index fixture + backfill embeddings
|
|
72
|
+
console.log("\n4. Indexing fixture and backfilling embeddings...");
|
|
73
|
+
deleteEdgesByFile(FIXTURE);
|
|
74
|
+
deleteNodesByFile(FIXTURE);
|
|
75
|
+
await indexFile(FIXTURE);
|
|
76
|
+
const { updated, skipped } = await backfillEmbeddings();
|
|
77
|
+
assert("backfill updated at least 1 node", updated > 0, { updated, skipped });
|
|
78
|
+
console.log(` → Updated: ${updated}, Skipped: ${skipped}`);
|
|
79
|
+
// ── Step 5: Nodes have embeddings stored
|
|
80
|
+
console.log("\n5. Checking stored embeddings...");
|
|
81
|
+
const addNode = getNode(`${FIXTURE}::add`);
|
|
82
|
+
assert("add node exists", !!addNode);
|
|
83
|
+
assert("add node has embedding", !!addNode?.embedding);
|
|
84
|
+
if (addNode?.embedding) {
|
|
85
|
+
const parsed = JSON.parse(addNode.embedding);
|
|
86
|
+
assert("stored embedding is float array", Array.isArray(parsed) && typeof parsed[0] === "number");
|
|
87
|
+
assert("stored embedding has correct dimension", parsed.length === testEmbedding.length, parsed.length);
|
|
88
|
+
}
|
|
89
|
+
// ── Step 6: rankBySimilarity
|
|
90
|
+
console.log("\n6. Testing semantic ranking...");
|
|
91
|
+
const queryVec = await generateEmbedding("add two numbers arithmetic");
|
|
92
|
+
assert("query embedding generated", !!queryVec);
|
|
93
|
+
if (queryVec) {
|
|
94
|
+
const nodes = getAllNodes();
|
|
95
|
+
const ranked = rankBySimilarity(queryVec, nodes, 5);
|
|
96
|
+
assert("rankBySimilarity returns results", ranked.length > 0);
|
|
97
|
+
assert("results are sorted by similarity (desc)", ranked[0].similarity >= (ranked[1]?.similarity ?? 0));
|
|
98
|
+
console.log(" Top results:");
|
|
99
|
+
ranked.forEach((n, i) => console.log(` ${i + 1}. ${n.name} (sim: ${n.similarity.toFixed(4)})`));
|
|
100
|
+
}
|
|
101
|
+
// ── Summary
|
|
102
|
+
console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`);
|
|
103
|
+
if (failed > 0)
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
run().catch((err) => {
|
|
107
|
+
console.error("Test runner error:", err);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=test-phase2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-phase2.js","sourceRoot":"","sources":["../src/test-phase2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 smoke test — run with: npm run test:phase3
|
|
3
|
+
*
|
|
4
|
+
* Verifies:
|
|
5
|
+
* 1. computeAllImportance runs and updates nodes
|
|
6
|
+
* 2. Classes score higher than imports (type weight)
|
|
7
|
+
* 3. Highly-referenced nodes score higher than isolated nodes
|
|
8
|
+
* 4. Hash-based skip works correctly on unchanged file
|
|
9
|
+
* 5. Re-index of modified content triggers re-index
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=test-phase3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-phase3.d.ts","sourceRoot":"","sources":["../src/test-phase3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|