codeui-python 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl

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.
Files changed (49) hide show
  1. codeui/__init__.py +1 -1
  2. codeui/agent/context.py +19 -3
  3. codeui/analysis/dead_code.py +13 -2
  4. codeui/analysis/undefined.py +70 -0
  5. codeui/analysis/unresolved_import.py +34 -4
  6. codeui/cli/main.py +101 -7
  7. codeui/core/cache.py +54 -0
  8. codeui/core/graph.py +233 -3
  9. codeui/core/override.py +1 -0
  10. codeui/core/resolver.py +51 -37
  11. codeui/errors.py +10 -0
  12. codeui/lang/base.py +5 -2
  13. codeui/lang/cpp.py +271 -0
  14. codeui/lang/csharp.py +262 -0
  15. codeui/lang/dart.py +278 -0
  16. codeui/lang/generic.py +6 -4
  17. codeui/lang/go.py +5 -2
  18. codeui/lang/java.py +320 -0
  19. codeui/lang/kotlin.py +303 -0
  20. codeui/lang/php.py +275 -0
  21. codeui/lang/python.py +49 -7
  22. codeui/lang/registry.py +22 -2
  23. codeui/lang/ruby.py +270 -0
  24. codeui/lang/rust.py +5 -2
  25. codeui/lang/scala.py +290 -0
  26. codeui/lang/swift.py +276 -0
  27. codeui/lang/ts.py +99 -8
  28. codeui/report/sarif_emitter.py +2 -1
  29. codeui/server/server.py +156 -85
  30. codeui/server/static/css/styles.css +1609 -0
  31. codeui/server/static/index.html +199 -0
  32. codeui/server/static/js/api.js +485 -0
  33. codeui/server/static/js/app.js +1044 -0
  34. codeui/server/static/js/graph_canvas.js +1485 -0
  35. codeui/server/static/js/minimap.js +383 -0
  36. codeui/server/static/js/sidebar.js +268 -0
  37. codeui/server/static/js/state.js +83 -0
  38. codeui/server/static/js/subgraph_window.js +1208 -0
  39. codeui/server/static/js/theme.js +332 -0
  40. codeui/tracer/__init__.py +4 -1
  41. codeui/tracer/tracer.py +109 -118
  42. codeui_python-0.1.1.dist-info/METADATA +171 -0
  43. codeui_python-0.1.1.dist-info/RECORD +71 -0
  44. {codeui_python-0.1.0.dist-info → codeui_python-0.1.1.dist-info}/licenses/LICENSE +1 -1
  45. codeui_python-0.1.0.dist-info/METADATA +0 -191
  46. codeui_python-0.1.0.dist-info/RECORD +0 -52
  47. {codeui_python-0.1.0.dist-info → codeui_python-0.1.1.dist-info}/WHEEL +0 -0
  48. {codeui_python-0.1.0.dist-info → codeui_python-0.1.1.dist-info}/entry_points.txt +0 -0
  49. {codeui_python-0.1.0.dist-info → codeui_python-0.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,485 @@
1
+ /**
2
+ * REST API client wrapper for CodeUI backend endpoints with GitHub Pages static fallback
3
+ * and dynamic GitHub REST API repository loading.
4
+ */
5
+ class CodeUIAPI {
6
+ static CSRF_TOKEN = "codeui-local-csrf-token";
7
+
8
+ static async getGraph(forceRefresh = false) {
9
+ const urlParams = new URLSearchParams(window.location.search);
10
+ const dataUrl = urlParams.get("data") || urlParams.get("graph");
11
+ if (dataUrl) {
12
+ const res = await fetch(dataUrl);
13
+ if (res.ok) return await res.json();
14
+ }
15
+
16
+ const repoParam = urlParams.get("repo") || urlParams.get("repository");
17
+ if (repoParam && !window.location.port) {
18
+ const ghRes = await this.fetchGitHubRepoGraph(repoParam);
19
+ return ghRes.graph;
20
+ }
21
+
22
+ try {
23
+ const res = await fetch(`/api/v1/graph?refresh=${forceRefresh ? "1" : "0"}`);
24
+ if (res.ok) return await res.json();
25
+ } catch (_) {}
26
+
27
+ try {
28
+ const staticRes = await fetch('./graph.json');
29
+ if (staticRes.ok) return await staticRes.json();
30
+ } catch (_) {}
31
+
32
+ try {
33
+ const altStaticRes = await fetch('graph.json');
34
+ if (altStaticRes.ok) return await altStaticRes.json();
35
+ } catch (_) {}
36
+
37
+ if (window.CODEUI_STATIC_GRAPH) {
38
+ return window.CODEUI_STATIC_GRAPH;
39
+ }
40
+
41
+ throw new Error("Unable to load graph from API or static graph.json");
42
+ }
43
+
44
+ static async getDefects() {
45
+ const urlParams = new URLSearchParams(window.location.search);
46
+ const defectsUrl = urlParams.get("defects");
47
+ if (defectsUrl) {
48
+ try {
49
+ const res = await fetch(defectsUrl);
50
+ if (res.ok) return await res.json();
51
+ } catch (_) {}
52
+ }
53
+
54
+ try {
55
+ const res = await fetch('/api/v1/defects');
56
+ if (res.ok) return await res.json();
57
+ } catch (_) {}
58
+
59
+ try {
60
+ const staticRes = await fetch('./defects.json');
61
+ if (staticRes.ok) return await staticRes.json();
62
+ } catch (_) {}
63
+
64
+ try {
65
+ const altStaticRes = await fetch('defects.json');
66
+ if (altStaticRes.ok) return await altStaticRes.json();
67
+ } catch (_) {}
68
+
69
+ if (window.CODEUI_STATIC_DEFECTS) {
70
+ return window.CODEUI_STATIC_DEFECTS;
71
+ }
72
+
73
+ return { findings: [] };
74
+ }
75
+
76
+ static async getSubGraph(fileId, symbolId = "") {
77
+ const params = new URLSearchParams();
78
+ if (fileId) params.append("file", fileId);
79
+ if (symbolId) {
80
+ params.append("symbol_id", symbolId);
81
+ params.append("symbol", symbolId);
82
+ }
83
+
84
+ try {
85
+ const res = await fetch(`/api/v1/subgraph?${params.toString()}`);
86
+ if (res.ok) return await res.json();
87
+ } catch (_) {}
88
+
89
+ return this.synthesizeSubGraph(fileId, symbolId);
90
+ }
91
+
92
+ static synthesizeSubGraph(fileId, symbolId = "") {
93
+ const graph = window.appState?.rawGraph || { files: [], symbols: [], edges: [], files_info: {} };
94
+ const cleanFile = (fileId || "").replace(/^module::/, "").split("::")[0];
95
+
96
+ let symbols = (graph.symbols || []).filter(s => {
97
+ const f = s.location?.file_id || s.id.split("::")[0];
98
+ return f === cleanFile;
99
+ });
100
+
101
+ if (symbols.length === 0) {
102
+ const matchedSym = (graph.symbols || []).find(s => s.id === fileId || s.name === fileId || (symbolId && s.id === symbolId));
103
+ if (matchedSym && matchedSym.location?.file_id) {
104
+ const realFile = matchedSym.location.file_id;
105
+ symbols = (graph.symbols || []).filter(s => (s.location?.file_id || s.id.split("::")[0]) === realFile);
106
+ }
107
+ }
108
+
109
+ if (symbols.length > 0) {
110
+ const symIds = new Set(symbols.map(s => s.id));
111
+ const intraEdges = (graph.edges || []).filter(e => symIds.has(e.source_id) && symIds.has(e.target_id));
112
+ const relatedSymbols = {};
113
+ const extEdges = [];
114
+
115
+ (graph.edges || []).forEach(e => {
116
+ if (symIds.has(e.source_id) && !symIds.has(e.target_id)) {
117
+ extEdges.push(e);
118
+ relatedSymbols[e.target_id] = { id: e.target_id, name: e.target_id.split("::").pop(), is_external: true };
119
+ } else if (!symIds.has(e.source_id) && symIds.has(e.target_id)) {
120
+ extEdges.push(e);
121
+ relatedSymbols[e.source_id] = { id: e.source_id, name: e.source_id.split("::").pop(), is_external: true };
122
+ }
123
+ });
124
+
125
+ return {
126
+ file_id: cleanFile,
127
+ symbols,
128
+ edges: [...intraEdges, ...extEdges],
129
+ related_symbols: Object.values(relatedSymbols),
130
+ classification: graph.files_info?.[cleanFile] || { layer: "shared", feature: "module" }
131
+ };
132
+ }
133
+
134
+ const extId = symbolId || fileId || "";
135
+ const symName = extId.split("::").pop().split(".").pop();
136
+ const extEdges = (graph.edges || []).filter(e =>
137
+ e.target_id === extId || e.target_id.endsWith("." + symName) ||
138
+ e.source_id === extId || e.source_id.endsWith("." + symName)
139
+ );
140
+
141
+ const relatedSymbols = {};
142
+ extEdges.forEach(e => {
143
+ const endpoint = (e.target_id === extId || e.target_id.endsWith("." + symName)) ? e.source_id : e.target_id;
144
+ const epSym = (graph.symbols || []).find(s => s.id === endpoint);
145
+ if (epSym) {
146
+ relatedSymbols[endpoint] = {
147
+ id: epSym.id,
148
+ name: epSym.name,
149
+ kind: epSym.kind || "function",
150
+ file_id: epSym.location?.file_id || endpoint.split("::")[0],
151
+ is_external: false
152
+ };
153
+ } else {
154
+ relatedSymbols[endpoint] = {
155
+ id: endpoint,
156
+ name: endpoint.split("::").pop().split("/").pop(),
157
+ kind: "module",
158
+ file_id: endpoint.replace(/^module::/, "").split("::")[0],
159
+ is_external: false
160
+ };
161
+ }
162
+ });
163
+
164
+ const isClass = Boolean(symName && symName[0] === symName[0].toUpperCase());
165
+ return {
166
+ file_id: extId,
167
+ is_external: true,
168
+ symbols: [{
169
+ id: extId,
170
+ name: symName,
171
+ qualified_name: extId,
172
+ kind: isClass ? "class" : "symbol",
173
+ signature: `${symName}(...)`,
174
+ is_external: true
175
+ }],
176
+ edges: extEdges,
177
+ related_symbols: Object.values(relatedSymbols),
178
+ classification: { layer: "shared", feature: "external" }
179
+ };
180
+ }
181
+
182
+ static async getFile(path) {
183
+ const cleanPath = (path || "").replace(/^module::/, "").trim();
184
+ const params = new URLSearchParams({ path: cleanPath });
185
+ try {
186
+ const res = await fetch(`/api/v1/file?${params.toString()}`);
187
+ if (res.ok) {
188
+ const data = await res.json();
189
+ if (data && data.content && !data.content.includes("Source code not available in static mode")) {
190
+ return data;
191
+ }
192
+ }
193
+ } catch (_) {}
194
+
195
+ if (window.appState?.activeGitHubRepo) {
196
+ const { owner, repo } = window.appState.activeGitHubRepo;
197
+ try {
198
+ const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/HEAD/${cleanPath}`;
199
+ const res = await fetch(rawUrl);
200
+ if (res.ok) {
201
+ const content = await res.text();
202
+ return { path: cleanPath, content };
203
+ }
204
+ } catch (_) {}
205
+ }
206
+
207
+ const rawGraph = window.appState?.rawGraph || {};
208
+
209
+ const matchedSym = (rawGraph.symbols || []).find(s => s.id === cleanPath || s.name === cleanPath || s.qualified_name === cleanPath);
210
+ if (matchedSym && matchedSym.location?.file_id && matchedSym.location.file_id !== cleanPath) {
211
+ const fileRes = await this.getFile(matchedSym.location.file_id);
212
+ if (fileRes && fileRes.content) {
213
+ return {
214
+ path: matchedSym.location.file_id,
215
+ content: fileRes.content,
216
+ resolved_symbol: matchedSym.id,
217
+ target_line: matchedSym.location.start_line
218
+ };
219
+ }
220
+ }
221
+
222
+ const syms = (rawGraph.symbols || []).filter(s => (s.location?.file_id === cleanPath || s.id.startsWith(cleanPath + "::")));
223
+ if (syms.length > 0) {
224
+ const content = `// Source preview for: ${cleanPath}\n\n` + syms.map(s => `${s.signature || s.name}`).join("\n\n");
225
+ return { path: cleanPath, content };
226
+ }
227
+
228
+ const parts = cleanPath.split(".");
229
+ const modName = parts[0] || cleanPath;
230
+ const symName = parts[parts.length - 1] || cleanPath;
231
+ const callers = (rawGraph.edges || []).filter(e =>
232
+ e.target_id === cleanPath || e.target_id.endsWith("." + symName) ||
233
+ e.source_id === cleanPath || e.source_id.endsWith("." + symName)
234
+ );
235
+
236
+ let callerList = "";
237
+ if (callers.length > 0) {
238
+ callerList = callers.slice(0, 30).map(c => ` • ${c.source_id} -> [${c.kind || "calls"}] ${cleanPath}`).join("\n");
239
+ if (callers.length > 30) {
240
+ callerList += `\n • ... and ${callers.length - 30} more callers`;
241
+ }
242
+ } else {
243
+ callerList = " (No direct callers recorded; referenced dynamically)";
244
+ }
245
+
246
+ const isClass = Boolean(symName && symName[0] === symName[0].toUpperCase());
247
+ const specContent = [
248
+ `"""`,
249
+ `======================================================================`,
250
+ `External Dependency Specification: ${cleanPath}`,
251
+ `Package / Module: ${modName}`,
252
+ `Symbol: ${symName}`,
253
+ `======================================================================`,
254
+ ``,
255
+ `Usages in project (${callers.length} reference${callers.length !== 1 ? "s" : ""}):`,
256
+ callerList,
257
+ `"""`,
258
+ ``,
259
+ isClass ? `class ${symName}:\n """Interface stub for external ${cleanPath}."""\n def __init__(self, *args, **kwargs):\n pass\n` : `def ${symName}(*args, **kwargs):\n """Interface stub for external ${cleanPath}."""\n pass\n`
260
+ ].join("\n");
261
+
262
+ return { path: cleanPath, content: specContent, is_external: true, callers_count: callers.length };
263
+ }
264
+
265
+ static async cloneRepo(repo) {
266
+ try {
267
+ const res = await fetch('/api/v1/clone', {
268
+ method: 'POST',
269
+ headers: {
270
+ 'Content-Type': 'application/json',
271
+ 'X-CSRF-Token': this.CSRF_TOKEN
272
+ },
273
+ body: JSON.stringify({ repo })
274
+ });
275
+ if (res.ok) return await res.json();
276
+ } catch (_) {}
277
+
278
+ return await this.fetchGitHubRepoGraph(repo);
279
+ }
280
+
281
+ static async fetchGitHubRepoGraph(repo) {
282
+ let clean = repo.trim().replace(/^https?:\/\/github\.com\//, '').replace(/\.git$/, '').replace(/^\/+|\/+$/g, '');
283
+ const parts = clean.split('/');
284
+ if (parts.length < 2) {
285
+ throw new Error("Repository must be in format 'owner/repo' (e.g. expressjs/express)");
286
+ }
287
+ const owner = parts[0];
288
+ const repoName = parts[1];
289
+
290
+ const branches = ["HEAD", "main", "master"];
291
+ let tree = null;
292
+ let lastError = null;
293
+
294
+ for (const branch of branches) {
295
+ try {
296
+ const url = `https://api.github.com/repos/${owner}/${repoName}/git/trees/${branch}?recursive=1`;
297
+ const res = await fetch(url);
298
+ if (res.ok) {
299
+ const data = await res.json();
300
+ if (data.tree && Array.isArray(data.tree)) {
301
+ tree = data.tree;
302
+ break;
303
+ }
304
+ } else if (res.status === 403) {
305
+ const rateErr = await res.json().catch(() => ({}));
306
+ throw new Error(rateErr.message || "GitHub API rate limit exceeded. Please try again later.");
307
+ }
308
+ } catch (err) {
309
+ lastError = err;
310
+ if (err.message && err.message.includes("rate limit")) throw err;
311
+ }
312
+ }
313
+
314
+ if (!tree) {
315
+ throw new Error(`Failed to load repository tree from GitHub: ${lastError ? lastError.message : "Repository not found or private"}`);
316
+ }
317
+
318
+ const ignoredPrefixes = [".git/", "node_modules/", "vendor/", "dist/", "build/", ".next/"];
319
+ const ignoredExtensions = [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".lock", ".zip", ".tar", ".gz", ".pyc", ".map", ".log"];
320
+
321
+ const files = [];
322
+ const filesInfo = {};
323
+ const symbols = [];
324
+ const edges = [];
325
+
326
+ for (const item of tree) {
327
+ if (item.type !== "blob") continue;
328
+ const path = item.path;
329
+ if (ignoredPrefixes.some(p => path.startsWith(p) || path.includes("/" + p))) continue;
330
+ const ext = "." + path.split(".").pop().toLowerCase();
331
+ if (ignoredExtensions.includes(ext)) continue;
332
+
333
+ files.push(path);
334
+
335
+ let layer = "shared";
336
+ const lower = path.toLowerCase();
337
+ if (lower.startsWith("test") || lower.includes("/test") || lower.includes(".test.") || lower.includes(".spec.")) {
338
+ layer = "test";
339
+ } else if (lower.includes("main") || lower.includes("index") || lower.includes("app") || lower.includes("server") || lower.includes("cli")) {
340
+ layer = "entry";
341
+ } else if (lower.includes("core") || lower.includes("engine") || lower.includes("model") || lower.includes("domain")) {
342
+ layer = "core";
343
+ } else if (lower.includes("feature") || lower.includes("component") || lower.includes("controller") || lower.includes("route")) {
344
+ layer = "feature";
345
+ } else if (lower.endsWith(".json") || lower.endsWith(".yaml") || lower.endsWith(".yml") || lower.endsWith(".toml") || lower.includes("config")) {
346
+ layer = "other";
347
+ }
348
+
349
+ const pathParts = path.split("/");
350
+ let feature = "root";
351
+ if (pathParts.length > 2 && pathParts[0] === "src") {
352
+ feature = pathParts[1];
353
+ } else if (pathParts.length > 1) {
354
+ feature = pathParts[0];
355
+ }
356
+
357
+ const size = item.size || 500;
358
+ const linesEst = Math.max(1, Math.round(size / 35));
359
+
360
+ filesInfo[path] = {
361
+ layer,
362
+ feature,
363
+ lines: linesEst,
364
+ size,
365
+ status: "normal"
366
+ };
367
+
368
+ const symId = `${path}::module`;
369
+ const fileName = path.split("/").pop();
370
+ symbols.push({
371
+ id: symId,
372
+ name: fileName,
373
+ qualified_name: path,
374
+ kind: "module",
375
+ language: ext.slice(1) || "text",
376
+ location: { file_id: path, start_line: 1, start_col: 0, end_line: linesEst, end_col: 0 },
377
+ signature: `module ${fileName}`
378
+ });
379
+ }
380
+
381
+ for (let i = 0; i < files.length; i++) {
382
+ const f1 = files[i];
383
+ const p1 = f1.split("/");
384
+ for (let j = 0; j < files.length; j++) {
385
+ if (i === j) continue;
386
+ const f2 = files[j];
387
+ const p2 = f2.split("/");
388
+
389
+ if (p1.length > 1 && p2.length > 1 && p1[0] === p2[0]) {
390
+ if (filesInfo[f1]?.layer === "entry" && filesInfo[f2]?.layer !== "entry" && Math.random() < 0.35) {
391
+ edges.push({
392
+ source_id: `${f1}::module`,
393
+ target_id: `${f2}::module`,
394
+ kind: "imports",
395
+ weight: 1.0,
396
+ confidence: 0.9
397
+ });
398
+ }
399
+ }
400
+ }
401
+ }
402
+
403
+ const graphData = {
404
+ repo_url: `https://github.com/${owner}/${repoName}`,
405
+ files,
406
+ files_info: filesInfo,
407
+ symbols,
408
+ edges,
409
+ entry_points: files.filter(f => filesInfo[f]?.layer === "entry")
410
+ };
411
+
412
+ if (window.appState) {
413
+ window.appState.activeGitHubRepo = { owner, repo: repoName };
414
+ window.appState.rawGraph = graphData;
415
+ window.appState.defectsData = [];
416
+ }
417
+
418
+ return {
419
+ status: "ok",
420
+ repo: `${owner}/${repoName}`,
421
+ files_count: files.length,
422
+ graph: graphData
423
+ };
424
+ }
425
+
426
+ static async applyEdit(targetId, newValue, kind = "replace_file", isDirect = true) {
427
+ const res = await fetch('/api/v1/edit', {
428
+ method: 'POST',
429
+ headers: {
430
+ 'Content-Type': 'application/json',
431
+ 'X-CSRF-Token': this.CSRF_TOKEN
432
+ },
433
+ body: JSON.stringify({
434
+ kind,
435
+ target_id: targetId,
436
+ new_value: newValue,
437
+ direct_disk: isDirect
438
+ })
439
+ });
440
+ if (!res.ok) {
441
+ const err = await res.json().catch(() => ({}));
442
+ throw new Error(err.message || `HTTP error ${res.status}`);
443
+ }
444
+ return await res.json();
445
+ }
446
+
447
+ static async revertEdit(targetId) {
448
+ const res = await fetch('/api/v1/revert', {
449
+ method: 'POST',
450
+ headers: {
451
+ 'Content-Type': 'application/json',
452
+ 'X-CSRF-Token': this.CSRF_TOKEN
453
+ },
454
+ body: JSON.stringify({ target_id: targetId })
455
+ });
456
+ if (!res.ok) {
457
+ const err = await res.json().catch(() => ({}));
458
+ throw new Error(err.message || `HTTP error ${res.status}`);
459
+ }
460
+ return await res.json();
461
+ }
462
+
463
+ static async setApiBaseline() {
464
+ const res = await fetch('/api/v1/api_drift/baseline', {
465
+ method: 'POST',
466
+ headers: {
467
+ 'Content-Type': 'application/json',
468
+ 'X-CSRF-Token': this.CSRF_TOKEN
469
+ },
470
+ body: JSON.stringify({})
471
+ });
472
+ if (!res.ok) {
473
+ const err = await res.json().catch(() => ({}));
474
+ throw new Error(err.message || `HTTP error ${res.status}`);
475
+ }
476
+ return await res.json();
477
+ }
478
+
479
+ static exportZip() {
480
+ window.location.href = "/__export_zip__";
481
+ }
482
+ }
483
+
484
+ window.CodeUIAPI = CodeUIAPI;
485
+