toolnet-memory 0.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/.env.example +28 -0
- package/README.md +147 -0
- package/bin/toolnet-memory +415 -0
- package/dist/packages/cli/commands.js +1 -0
- package/dist/packages/cli/index.js +1 -0
- package/dist/packages/sdk/client.js +78 -0
- package/dist/packages/sdk/index.js +1 -0
- package/dist/packages/sdk/types.js +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/api/routes/health.js +1 -0
- package/dist/src/api/routes/index.js +1 -0
- package/dist/src/api/routes/memory.js +1 -0
- package/dist/src/api/routes/projects.js +1 -0
- package/dist/src/api/routes/search.js +1 -0
- package/dist/src/api/server.js +1 -0
- package/dist/src/capture/activity-capture.js +21 -0
- package/dist/src/capture/command-capture.js +12 -0
- package/dist/src/capture/error-capture.js +14 -0
- package/dist/src/capture/event-deduplicator.js +28 -0
- package/dist/src/capture/event-queue.js +20 -0
- package/dist/src/capture/file-capture.js +23 -0
- package/dist/src/capture/index.js +8 -0
- package/dist/src/capture/session-capture.js +18 -0
- package/dist/src/capture/tool-capture.js +13 -0
- package/dist/src/code-intelligence/analysis/analysis-engine.js +26 -0
- package/dist/src/code-intelligence/analysis/dead-code-analyzer.js +127 -0
- package/dist/src/code-intelligence/analysis/dependency-analyzer.js +66 -0
- package/dist/src/code-intelligence/analysis/index.js +4 -0
- package/dist/src/code-intelligence/analysis/types.js +1 -0
- package/dist/src/code-intelligence/architecture/architecture-engine.js +48 -0
- package/dist/src/code-intelligence/architecture/clusters.js +322 -0
- package/dist/src/code-intelligence/architecture/entry-points.js +150 -0
- package/dist/src/code-intelligence/architecture/hotspots.js +136 -0
- package/dist/src/code-intelligence/architecture/index.js +6 -0
- package/dist/src/code-intelligence/architecture/layers.js +197 -0
- package/dist/src/code-intelligence/architecture/types.js +1 -0
- package/dist/src/code-intelligence/chunks/index.js +2 -0
- package/dist/src/code-intelligence/chunks/smart-chunker.js +125 -0
- package/dist/src/code-intelligence/chunks/types.js +1 -0
- package/dist/src/code-intelligence/git/git-diff.js +109 -0
- package/dist/src/code-intelligence/git/index.js +1 -0
- package/dist/src/code-intelligence/graph/architecture.js +26 -0
- package/dist/src/code-intelligence/graph/graph-builder.js +213 -0
- package/dist/src/code-intelligence/graph/graph-store.js +57 -0
- package/dist/src/code-intelligence/graph/index.js +1 -0
- package/dist/src/code-intelligence/graph/relationships.js +1 -0
- package/dist/src/code-intelligence/graph/trace.js +65 -0
- package/dist/src/code-intelligence/impact/blast-radius.js +160 -0
- package/dist/src/code-intelligence/impact/change-mapper.js +69 -0
- package/dist/src/code-intelligence/impact/edit-tool-detector.js +92 -0
- package/dist/src/code-intelligence/impact/impact-analyzer.js +60 -0
- package/dist/src/code-intelligence/impact/impact-guard.js +33 -0
- package/dist/src/code-intelligence/impact/index.js +5 -0
- package/dist/src/code-intelligence/incremental/file-hash.js +8 -0
- package/dist/src/code-intelligence/incremental/incremental-indexer.js +106 -0
- package/dist/src/code-intelligence/incremental/manifest-builder.js +19 -0
- package/dist/src/code-intelligence/incremental/manifest.js +31 -0
- package/dist/src/code-intelligence/index.js +23 -0
- package/dist/src/code-intelligence/indexer/file-watcher.js +1 -0
- package/dist/src/code-intelligence/indexer/incremental-indexer.js +1 -0
- package/dist/src/code-intelligence/indexer/index.js +1 -0
- package/dist/src/code-intelligence/indexer/repository-indexer.js +31 -0
- package/dist/src/code-intelligence/indexer/repository-scanner.js +50 -0
- package/dist/src/code-intelligence/parsers/index.js +1 -0
- package/dist/src/code-intelligence/parsers/tree-sitter/index.js +1 -0
- package/dist/src/code-intelligence/parsers/tree-sitter/languages.js +1 -0
- package/dist/src/code-intelligence/parsers/tree-sitter/parser.js +1 -0
- package/dist/src/code-intelligence/parsers/typescript-parser.js +190 -0
- package/dist/src/code-intelligence/query/graph-query-engine.js +275 -0
- package/dist/src/code-intelligence/query/index.js +2 -0
- package/dist/src/code-intelligence/query/types.js +1 -0
- package/dist/src/code-intelligence/resolution/index.js +2 -0
- package/dist/src/code-intelligence/resolution/types.js +1 -0
- package/dist/src/code-intelligence/resolution/typescript-resolver.js +316 -0
- package/dist/src/code-intelligence/rich/index.js +1 -0
- package/dist/src/code-intelligence/rich/rich-graph-enricher.js +463 -0
- package/dist/src/code-intelligence/semantic/index.js +2 -0
- package/dist/src/code-intelligence/semantic/semantic-code-engine.js +213 -0
- package/dist/src/code-intelligence/semantic/source-priority.js +22 -0
- package/dist/src/code-intelligence/symbols/index.js +1 -0
- package/dist/src/code-intelligence/symbols/reference-resolver.js +21 -0
- package/dist/src/code-intelligence/symbols/symbol-extractor.js +1 -0
- package/dist/src/code-intelligence/test-analysis.js +72 -0
- package/dist/src/code-intelligence/test-architecture.js +67 -0
- package/dist/src/code-intelligence/test-impact.js +47 -0
- package/dist/src/code-intelligence/test-incremental.js +24 -0
- package/dist/src/code-intelligence/test-index.js +39 -0
- package/dist/src/code-intelligence/test-resolution.js +54 -0
- package/dist/src/code-intelligence/test-rich-graph.js +48 -0
- package/dist/src/code-intelligence/test-semantic.js +57 -0
- package/dist/src/code-intelligence/test-visualization.js +36 -0
- package/dist/src/code-intelligence/types.js +1 -0
- package/dist/src/code-intelligence/visualization/index.js +2 -0
- package/dist/src/code-intelligence/visualization/types.js +1 -0
- package/dist/src/code-intelligence/visualization/visualization-builder.js +104 -0
- package/dist/src/core/config.js +66 -0
- package/dist/src/core/global-env.js +70 -0
- package/dist/src/core/importance.js +30 -0
- package/dist/src/core/index.js +5 -0
- package/dist/src/core/memory-engine.js +183 -0
- package/dist/src/core/project-manager.js +243 -0
- package/dist/src/core/types.js +1 -0
- package/dist/src/embeddings/batch-embedder.js +1 -0
- package/dist/src/embeddings/cache.js +1 -0
- package/dist/src/embeddings/embedder.js +51 -0
- package/dist/src/embeddings/index.js +4 -0
- package/dist/src/embeddings/local.js +37 -0
- package/dist/src/embeddings/provider.js +1 -0
- package/dist/src/embeddings/remote.js +76 -0
- package/dist/src/embeddings/test-connection.js +16 -0
- package/dist/src/hooks/after-edit.js +3 -0
- package/dist/src/hooks/after-tool.js +3 -0
- package/dist/src/hooks/auto-context.js +68 -0
- package/dist/src/hooks/auto-impact-guard.js +94 -0
- package/dist/src/hooks/auto-retrieval.js +27 -0
- package/dist/src/hooks/before-agent.js +18 -0
- package/dist/src/hooks/before-compact.js +3 -0
- package/dist/src/hooks/before-edit.js +20 -0
- package/dist/src/hooks/index.js +11 -0
- package/dist/src/hooks/runtime.js +110 -0
- package/dist/src/hooks/session-end.js +3 -0
- package/dist/src/hooks/session-start.js +3 -0
- package/dist/src/index.js +22 -0
- package/dist/src/mcp/bootstrap.js +68 -0
- package/dist/src/mcp/context.js +1 -0
- package/dist/src/mcp/index.js +3 -0
- package/dist/src/mcp/server.js +45 -0
- package/dist/src/mcp/tools/analyze-impact.js +41 -0
- package/dist/src/mcp/tools/dead-code.js +55 -0
- package/dist/src/mcp/tools/find-callers.js +24 -0
- package/dist/src/mcp/tools/find-dependencies.js +57 -0
- package/dist/src/mcp/tools/find-symbol.js +7 -0
- package/dist/src/mcp/tools/get-architecture.js +11 -0
- package/dist/src/mcp/tools/graph-dependents.js +24 -0
- package/dist/src/mcp/tools/graph-neighborhood.js +58 -0
- package/dist/src/mcp/tools/graph-path.js +43 -0
- package/dist/src/mcp/tools/graph-query-utils.js +32 -0
- package/dist/src/mcp/tools/impact-guard.js +34 -0
- package/dist/src/mcp/tools/index-repository.js +1 -0
- package/dist/src/mcp/tools/index.js +20 -0
- package/dist/src/mcp/tools/memory-decisions.js +1 -0
- package/dist/src/mcp/tools/memory-forget.js +14 -0
- package/dist/src/mcp/tools/memory-recent.js +1 -0
- package/dist/src/mcp/tools/memory-remember.js +33 -0
- package/dist/src/mcp/tools/memory-rules.js +1 -0
- package/dist/src/mcp/tools/memory-search.js +18 -0
- package/dist/src/mcp/tools/memory-todos.js +1 -0
- package/dist/src/mcp/tools/project-context.js +25 -0
- package/dist/src/mcp/tools/search-code.js +21 -0
- package/dist/src/mcp/tools/semantic-code-search.js +33 -0
- package/dist/src/mcp/tools/snapshot-create.js +13 -0
- package/dist/src/mcp/tools/snapshot-list.js +7 -0
- package/dist/src/mcp/tools/snapshot-restore.js +13 -0
- package/dist/src/mcp/tools/trace-calls.js +37 -0
- package/dist/src/memory/activity.js +5 -0
- package/dist/src/memory/conflict-detector.js +105 -0
- package/dist/src/memory/consolidator.js +104 -0
- package/dist/src/memory/decay.js +59 -0
- package/dist/src/memory/decisions.js +3 -0
- package/dist/src/memory/deduplicate.js +25 -0
- package/dist/src/memory/index.js +11 -0
- package/dist/src/memory/maintenance.js +16 -0
- package/dist/src/memory/remember.js +3 -0
- package/dist/src/memory/rules.js +3 -0
- package/dist/src/memory/search.js +3 -0
- package/dist/src/memory/summaries.js +1 -0
- package/dist/src/memory/todos.js +3 -0
- package/dist/src/processor/decision-extractor.js +13 -0
- package/dist/src/processor/importance-scorer.js +22 -0
- package/dist/src/processor/index.js +7 -0
- package/dist/src/processor/memory-extractor.js +59 -0
- package/dist/src/processor/memory-processor.js +105 -0
- package/dist/src/processor/rule-extractor.js +13 -0
- package/dist/src/processor/summarizer.js +71 -0
- package/dist/src/processor/todo-extractor.js +12 -0
- package/dist/src/production/config-check.js +34 -0
- package/dist/src/production/doctor.js +58 -0
- package/dist/src/production/full-index.js +114 -0
- package/dist/src/production/health.js +58 -0
- package/dist/src/production/index-pipeline.js +151 -0
- package/dist/src/production/index.js +4 -0
- package/dist/src/production/project-lock.js +95 -0
- package/dist/src/production/project-status.js +16 -0
- package/dist/src/production/recovery.js +15 -0
- package/dist/src/production/snapshot-cli.js +58 -0
- package/dist/src/project-manual/cli.js +77 -0
- package/dist/src/project-manual/index.js +2 -0
- package/dist/src/project-manual/manager.js +147 -0
- package/dist/src/project-manual/types.js +1 -0
- package/dist/src/retrieval/bm25/index.js +1 -0
- package/dist/src/retrieval/bm25/search.js +1 -0
- package/dist/src/retrieval/bm25.js +30 -0
- package/dist/src/retrieval/context-budget.js +1 -0
- package/dist/src/retrieval/context-builder.js +22 -0
- package/dist/src/retrieval/graph/index.js +1 -0
- package/dist/src/retrieval/graph/search.js +1 -0
- package/dist/src/retrieval/hybrid/hybrid-search.js +1 -0
- package/dist/src/retrieval/hybrid/index.js +1 -0
- package/dist/src/retrieval/hybrid-search.js +68 -0
- package/dist/src/retrieval/importance.js +16 -0
- package/dist/src/retrieval/index.js +12 -0
- package/dist/src/retrieval/query-analyzer.js +32 -0
- package/dist/src/retrieval/query-classifier.js +1 -0
- package/dist/src/retrieval/query-expansion.js +1 -0
- package/dist/src/retrieval/recency.js +12 -0
- package/dist/src/retrieval/reranker/index.js +1 -0
- package/dist/src/retrieval/reranker/reranker.js +1 -0
- package/dist/src/retrieval/retrieval-engine.js +17 -0
- package/dist/src/retrieval/tokenizer.js +9 -0
- package/dist/src/retrieval/type-priority.js +10 -0
- package/dist/src/retrieval/types.js +1 -0
- package/dist/src/retrieval/vector/index.js +4 -0
- package/dist/src/retrieval/vector/metadata-filter.js +1 -0
- package/dist/src/retrieval/vector/persistence.js +67 -0
- package/dist/src/retrieval/vector/search.js +12 -0
- package/dist/src/retrieval/vector/test-persistence.js +36 -0
- package/dist/src/retrieval/vector/upsert.js +40 -0
- package/dist/src/retrieval/vector/vector-store.js +89 -0
- package/dist/src/retrieval/vector-hybrid-engine.js +91 -0
- package/dist/src/runtime/bootstrap.js +28 -0
- package/dist/src/runtime/graceful.js +17 -0
- package/dist/src/runtime/index.js +3 -0
- package/dist/src/runtime/toolnet-memory-runtime.js +156 -0
- package/dist/src/security/encryption.js +1 -0
- package/dist/src/security/file-filter.js +30 -0
- package/dist/src/security/index.js +3 -0
- package/dist/src/security/sanitizer.js +49 -0
- package/dist/src/security/secret-scanner.js +56 -0
- package/dist/src/session/agy/adapter.js +159 -0
- package/dist/src/session/agy/cli.js +87 -0
- package/dist/src/session/agy/hook-installer.js +62 -0
- package/dist/src/session/agy/hook.js +167 -0
- package/dist/src/session/agy/index.js +5 -0
- package/dist/src/session/agy/project-resolver.js +27 -0
- package/dist/src/session/agy/recovery.js +112 -0
- package/dist/src/session/agy/transcript.js +191 -0
- package/dist/src/session/codex/adapter.js +138 -0
- package/dist/src/session/codex/cli.js +236 -0
- package/dist/src/session/codex/context-hook-installer.js +70 -0
- package/dist/src/session/codex/context-hook.js +69 -0
- package/dist/src/session/codex/discovery.js +138 -0
- package/dist/src/session/codex/index.js +6 -0
- package/dist/src/session/codex/notify-installer.js +135 -0
- package/dist/src/session/codex/project-resolver.js +25 -0
- package/dist/src/session/codex/recovery.js +27 -0
- package/dist/src/session/codex/rollout.js +186 -0
- package/dist/src/session/core.js +239 -0
- package/dist/src/session/identity.js +57 -0
- package/dist/src/session/index.js +9 -0
- package/dist/src/session/learner/cli.js +62 -0
- package/dist/src/session/learner/extractor.js +461 -0
- package/dist/src/session/learner/index.js +4 -0
- package/dist/src/session/learner/journal.js +159 -0
- package/dist/src/session/learner/learner.js +124 -0
- package/dist/src/session/learner/types.js +1 -0
- package/dist/src/session/opencode/adapter.js +553 -0
- package/dist/src/session/opencode/cli.js +121 -0
- package/dist/src/session/opencode/index.js +2 -0
- package/dist/src/session/opencode/plugin-installer.js +230 -0
- package/dist/src/session/store.js +242 -0
- package/dist/src/session/types.js +1 -0
- package/dist/src/session/utils.js +47 -0
- package/dist/src/session/wal.js +303 -0
- package/dist/src/snapshot/index.js +2 -0
- package/dist/src/snapshot/manager.js +130 -0
- package/dist/src/snapshot/test-snapshot.js +28 -0
- package/dist/src/snapshot/types.js +1 -0
- package/dist/src/storage/architecture-store.js +37 -0
- package/dist/src/storage/cleanup-health.js +20 -0
- package/dist/src/storage/code-analysis-store.js +35 -0
- package/dist/src/storage/code-chunk-store.js +25 -0
- package/dist/src/storage/code-graph-store.js +24 -0
- package/dist/src/storage/code-manifest-store.js +24 -0
- package/dist/src/storage/code-vector-store.js +25 -0
- package/dist/src/storage/github/client.js +1 -0
- package/dist/src/storage/github/index.js +1 -0
- package/dist/src/storage/google-drive/client.js +1 -0
- package/dist/src/storage/google-drive/index.js +1 -0
- package/dist/src/storage/huggingface/client.js +116 -0
- package/dist/src/storage/huggingface/index.js +1 -0
- package/dist/src/storage/index.js +16 -0
- package/dist/src/storage/list-bucket-raw.js +14 -0
- package/dist/src/storage/list-bucket-tree.js +38 -0
- package/dist/src/storage/local/client.js +99 -0
- package/dist/src/storage/local/index.js +1 -0
- package/dist/src/storage/memory-store.js +30 -0
- package/dist/src/storage/project/cleanup-layout-v1.js +72 -0
- package/dist/src/storage/project/folder.js +15 -0
- package/dist/src/storage/project/index.js +3 -0
- package/dist/src/storage/project/layout.js +74 -0
- package/dist/src/storage/project/migrate-layout-v2.js +101 -0
- package/dist/src/storage/project/migrate.js +73 -0
- package/dist/src/storage/project/scoped-provider.js +165 -0
- package/dist/src/storage/provider.js +26 -0
- package/dist/src/storage/retrying-provider.js +23 -0
- package/dist/src/storage/test-connection.js +32 -0
- package/dist/src/storage/type-resolution-store.js +26 -0
- package/dist/src/storage/types.js +1 -0
- package/dist/src/storage/vector-store.js +30 -0
- package/dist/src/storage/visualization-store.js +25 -0
- package/dist/src/sync/compressor.js +1 -0
- package/dist/src/sync/conflict-resolver.js +1 -0
- package/dist/src/sync/downloader.js +1 -0
- package/dist/src/sync/index.js +1 -0
- package/dist/src/sync/sync-engine.js +1 -0
- package/dist/src/sync/upload-queue.js +1 -0
- package/dist/src/utils/retry.js +29 -0
- package/dist/src/visualization/server.js +270 -0
- package/dist/src/work-continuity/brief-cache.js +107 -0
- package/dist/src/work-continuity/brief.js +349 -0
- package/dist/src/work-continuity/cli.js +96 -0
- package/dist/src/work-continuity/context-cli.js +106 -0
- package/dist/src/work-continuity/context-runtime-cli.js +88 -0
- package/dist/src/work-continuity/extractor.js +339 -0
- package/dist/src/work-continuity/handoff.js +185 -0
- package/dist/src/work-continuity/index.js +14 -0
- package/dist/src/work-continuity/injection.js +84 -0
- package/dist/src/work-continuity/journal.js +49 -0
- package/dist/src/work-continuity/learner.js +127 -0
- package/dist/src/work-continuity/reducer.js +316 -0
- package/dist/src/work-continuity/semantic-extractor.js +360 -0
- package/dist/src/work-continuity/semantic-journal.js +55 -0
- package/dist/src/work-continuity/semantic-learner.js +125 -0
- package/dist/src/work-continuity/semantic-reducer.js +268 -0
- package/dist/src/work-continuity/semantic-types.js +1 -0
- package/dist/src/work-continuity/types.js +1 -0
- package/dist/visualization/public/index.html +2896 -0
- package/dist/visualization/public/vendor/3d-force-graph.min.js +5 -0
- package/package.json +69 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { sanitizeProjectFolder, } from "./folder.js";
|
|
2
|
+
import { mapProjectStoragePath, } from "./layout.js";
|
|
3
|
+
export class ProjectScopedStorageProvider {
|
|
4
|
+
provider;
|
|
5
|
+
name;
|
|
6
|
+
/**
|
|
7
|
+
* Physical remote project folder.
|
|
8
|
+
*/
|
|
9
|
+
folder;
|
|
10
|
+
sourcePrefix;
|
|
11
|
+
targetPrefix;
|
|
12
|
+
projectId;
|
|
13
|
+
projectName;
|
|
14
|
+
registryPromise;
|
|
15
|
+
constructor(provider, projectId, projectName, projectRemote) {
|
|
16
|
+
this.provider = provider;
|
|
17
|
+
this.name =
|
|
18
|
+
provider.name;
|
|
19
|
+
this.projectId =
|
|
20
|
+
projectId;
|
|
21
|
+
this.projectName =
|
|
22
|
+
projectName;
|
|
23
|
+
this.folder =
|
|
24
|
+
sanitizeProjectFolder(projectRemote ??
|
|
25
|
+
projectName);
|
|
26
|
+
/*
|
|
27
|
+
* Internal modules may still use the immutable project ID
|
|
28
|
+
* in logical paths.
|
|
29
|
+
*/
|
|
30
|
+
this.sourcePrefix =
|
|
31
|
+
`projects/${projectId}`;
|
|
32
|
+
/*
|
|
33
|
+
* Human-readable physical namespace.
|
|
34
|
+
*/
|
|
35
|
+
this.targetPrefix =
|
|
36
|
+
`projects/${this.folder}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Every real project gets:
|
|
40
|
+
*
|
|
41
|
+
* projects/<remote>/project.json
|
|
42
|
+
*
|
|
43
|
+
* This also acts as the remote project registry.
|
|
44
|
+
*
|
|
45
|
+
* IMPORTANT:
|
|
46
|
+
* If another project ID already owns the same remote folder,
|
|
47
|
+
* we fail instead of mixing their memory/graphs.
|
|
48
|
+
*/
|
|
49
|
+
async registerProject() {
|
|
50
|
+
const key = `${this.targetPrefix}/project.json`;
|
|
51
|
+
const now = new Date()
|
|
52
|
+
.toISOString();
|
|
53
|
+
let createdAt = now;
|
|
54
|
+
const existingText = await this.provider.getText(key);
|
|
55
|
+
if (existingText) {
|
|
56
|
+
let existing;
|
|
57
|
+
try {
|
|
58
|
+
existing =
|
|
59
|
+
JSON.parse(existingText);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw new Error(`Invalid remote ToolNet project manifest at ${key}: ${error instanceof Error
|
|
63
|
+
? error.message
|
|
64
|
+
: String(error)}`);
|
|
65
|
+
}
|
|
66
|
+
if (typeof existing.id ===
|
|
67
|
+
"string" &&
|
|
68
|
+
existing.id !==
|
|
69
|
+
this.projectId) {
|
|
70
|
+
throw new Error([
|
|
71
|
+
"ToolNet remote project namespace collision.",
|
|
72
|
+
`Remote folder: ${this.targetPrefix}`,
|
|
73
|
+
`Existing project id: ${existing.id}`,
|
|
74
|
+
`Current project id: ${this.projectId}`,
|
|
75
|
+
"Refusing to mix data from two projects.",
|
|
76
|
+
].join(" "));
|
|
77
|
+
}
|
|
78
|
+
if (typeof existing.createdAt ===
|
|
79
|
+
"string") {
|
|
80
|
+
createdAt =
|
|
81
|
+
existing.createdAt;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const manifest = {
|
|
85
|
+
version: 1,
|
|
86
|
+
id: this.projectId,
|
|
87
|
+
name: this.projectName,
|
|
88
|
+
remote: this.folder,
|
|
89
|
+
createdAt,
|
|
90
|
+
updatedAt: now,
|
|
91
|
+
};
|
|
92
|
+
await this.provider.put(key, JSON.stringify(manifest, null, 2) + "\n", "application/json");
|
|
93
|
+
}
|
|
94
|
+
async ensureRegistered() {
|
|
95
|
+
if (!this.registryPromise) {
|
|
96
|
+
this.registryPromise =
|
|
97
|
+
this.registerProject();
|
|
98
|
+
}
|
|
99
|
+
return this.registryPromise;
|
|
100
|
+
}
|
|
101
|
+
key(value) {
|
|
102
|
+
value =
|
|
103
|
+
mapProjectStoragePath(value);
|
|
104
|
+
if (value ===
|
|
105
|
+
this.sourcePrefix) {
|
|
106
|
+
return this.targetPrefix;
|
|
107
|
+
}
|
|
108
|
+
if (value.startsWith(`${this.sourcePrefix}/`)) {
|
|
109
|
+
return (this.targetPrefix +
|
|
110
|
+
value.slice(this.sourcePrefix.length));
|
|
111
|
+
}
|
|
112
|
+
/*
|
|
113
|
+
* Accessing the already-resolved physical namespace
|
|
114
|
+
* of this same project is allowed.
|
|
115
|
+
*/
|
|
116
|
+
if (value ===
|
|
117
|
+
this.targetPrefix ||
|
|
118
|
+
value.startsWith(`${this.targetPrefix}/`)) {
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
/*
|
|
122
|
+
* Critical isolation rule.
|
|
123
|
+
*
|
|
124
|
+
* A scoped project storage provider must never access:
|
|
125
|
+
*
|
|
126
|
+
* projects/<another-project>/...
|
|
127
|
+
*/
|
|
128
|
+
if (value.startsWith("projects/")) {
|
|
129
|
+
throw new Error([
|
|
130
|
+
"Cross-project storage access denied.",
|
|
131
|
+
`Current project: ${this.targetPrefix}`,
|
|
132
|
+
`Requested key: ${value}`,
|
|
133
|
+
].join(" "));
|
|
134
|
+
}
|
|
135
|
+
/*
|
|
136
|
+
* Preserve support for logical relative paths used
|
|
137
|
+
* internally by stores/snapshot components.
|
|
138
|
+
*/
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
async put(key, data, contentType) {
|
|
142
|
+
await this.ensureRegistered();
|
|
143
|
+
return this.provider.put(this.key(key), data, contentType);
|
|
144
|
+
}
|
|
145
|
+
async get(key) {
|
|
146
|
+
await this.ensureRegistered();
|
|
147
|
+
return this.provider.get(this.key(key));
|
|
148
|
+
}
|
|
149
|
+
async getText(key) {
|
|
150
|
+
await this.ensureRegistered();
|
|
151
|
+
return this.provider.getText(this.key(key));
|
|
152
|
+
}
|
|
153
|
+
async delete(key) {
|
|
154
|
+
await this.ensureRegistered();
|
|
155
|
+
return this.provider.delete(this.key(key));
|
|
156
|
+
}
|
|
157
|
+
async exists(key) {
|
|
158
|
+
await this.ensureRegistered();
|
|
159
|
+
return this.provider.exists(this.key(key));
|
|
160
|
+
}
|
|
161
|
+
async list(prefix) {
|
|
162
|
+
await this.ensureRegistered();
|
|
163
|
+
return this.provider.list(this.key(prefix));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { homedir, } from "node:os";
|
|
2
|
+
import { join, } from "node:path";
|
|
3
|
+
import { HuggingFaceStorageProvider, } from "./huggingface/client.js";
|
|
4
|
+
import { LocalStorageProvider, } from "./local/client.js";
|
|
5
|
+
export function createStorageProvider(config) {
|
|
6
|
+
const localRoot = config.localRoot ??
|
|
7
|
+
join(homedir(), ".toolnet-memory", "storage");
|
|
8
|
+
if (config.provider ===
|
|
9
|
+
"huggingface") {
|
|
10
|
+
const hf = config.huggingface;
|
|
11
|
+
if (hf?.namespace &&
|
|
12
|
+
hf.bucket &&
|
|
13
|
+
hf.accessKeyId &&
|
|
14
|
+
hf.secretAccessKey) {
|
|
15
|
+
return new HuggingFaceStorageProvider({
|
|
16
|
+
namespace: hf.namespace,
|
|
17
|
+
bucket: hf.bucket,
|
|
18
|
+
accessKeyId: hf.accessKeyId,
|
|
19
|
+
secretAccessKey: hf.secretAccessKey,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
console.warn("[storage] Hugging Face credentials missing. Using local fallback.");
|
|
23
|
+
return new LocalStorageProvider(localRoot);
|
|
24
|
+
}
|
|
25
|
+
return new LocalStorageProvider(localRoot);
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { retry, } from "../utils/retry.js";
|
|
2
|
+
const RETRY_METHODS = new Set([
|
|
3
|
+
"put",
|
|
4
|
+
"get",
|
|
5
|
+
"getText",
|
|
6
|
+
"delete",
|
|
7
|
+
"list",
|
|
8
|
+
]);
|
|
9
|
+
export function withStorageRetry(provider, options = {}) {
|
|
10
|
+
return new Proxy(provider, {
|
|
11
|
+
get(target, property) {
|
|
12
|
+
const value = Reflect.get(target, property, target);
|
|
13
|
+
if (typeof value !==
|
|
14
|
+
"function") {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
if (!RETRY_METHODS.has(property)) {
|
|
18
|
+
return value.bind(target);
|
|
19
|
+
}
|
|
20
|
+
return (...args) => retry(() => Promise.resolve(value.apply(target, args)), options);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { loadConfig, } from "../core/config.js";
|
|
3
|
+
import { createStorageProvider, } from "./provider.js";
|
|
4
|
+
async function main() {
|
|
5
|
+
const config = loadConfig();
|
|
6
|
+
const storage = createStorageProvider({
|
|
7
|
+
provider: config.storage.provider,
|
|
8
|
+
huggingface: config.storage
|
|
9
|
+
.huggingface,
|
|
10
|
+
localRoot: config.storage
|
|
11
|
+
.localRoot,
|
|
12
|
+
});
|
|
13
|
+
const key = `_health/${Date.now()}.txt`;
|
|
14
|
+
const value = `toolnet-memory:${new Date().toISOString()}`;
|
|
15
|
+
await storage.put(key, value, "text/plain");
|
|
16
|
+
const result = await storage.getText(key);
|
|
17
|
+
if (result !== value) {
|
|
18
|
+
throw new Error("Storage read/write mismatch");
|
|
19
|
+
}
|
|
20
|
+
await storage.delete(key);
|
|
21
|
+
console.log({
|
|
22
|
+
ok: true,
|
|
23
|
+
provider: storage.name,
|
|
24
|
+
write: "PASS",
|
|
25
|
+
read: "PASS",
|
|
26
|
+
delete: "PASS",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
main().catch((error) => {
|
|
30
|
+
console.error(error);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class PersistentTypeResolutionStore {
|
|
2
|
+
storage;
|
|
3
|
+
constructor(storage) {
|
|
4
|
+
this.storage = storage;
|
|
5
|
+
}
|
|
6
|
+
key(projectId) {
|
|
7
|
+
return [
|
|
8
|
+
"projects",
|
|
9
|
+
projectId,
|
|
10
|
+
"graph",
|
|
11
|
+
"resolution",
|
|
12
|
+
"current.json",
|
|
13
|
+
].join("/");
|
|
14
|
+
}
|
|
15
|
+
async load(projectId) {
|
|
16
|
+
const text = await this.storage
|
|
17
|
+
.getText(this.key(projectId));
|
|
18
|
+
if (!text) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return JSON.parse(text);
|
|
22
|
+
}
|
|
23
|
+
async save(snapshot) {
|
|
24
|
+
await this.storage.put(this.key(snapshot.projectId), JSON.stringify(snapshot), "application/json");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class PersistentVectorStore {
|
|
2
|
+
storage;
|
|
3
|
+
constructor(storage) {
|
|
4
|
+
this.storage = storage;
|
|
5
|
+
}
|
|
6
|
+
key(projectId) {
|
|
7
|
+
return [
|
|
8
|
+
"projects",
|
|
9
|
+
projectId,
|
|
10
|
+
"vectors",
|
|
11
|
+
"current.json",
|
|
12
|
+
].join("/");
|
|
13
|
+
}
|
|
14
|
+
async load(projectId) {
|
|
15
|
+
const text = await this.storage.getText(this.key(projectId));
|
|
16
|
+
if (!text) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const parsed = JSON.parse(text);
|
|
20
|
+
if (parsed.version !== 1 ||
|
|
21
|
+
parsed.projectId !== projectId ||
|
|
22
|
+
!Array.isArray(parsed.records)) {
|
|
23
|
+
throw new Error("Invalid vector snapshot");
|
|
24
|
+
}
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
async save(snapshot) {
|
|
28
|
+
await this.storage.put(this.key(snapshot.projectId), JSON.stringify(snapshot), "application/json");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class PersistentVisualizationStore {
|
|
2
|
+
storage;
|
|
3
|
+
constructor(storage) {
|
|
4
|
+
this.storage = storage;
|
|
5
|
+
}
|
|
6
|
+
key(projectId) {
|
|
7
|
+
return [
|
|
8
|
+
"projects",
|
|
9
|
+
projectId,
|
|
10
|
+
"code",
|
|
11
|
+
"visualization",
|
|
12
|
+
"graph.json",
|
|
13
|
+
].join("/");
|
|
14
|
+
}
|
|
15
|
+
async load(projectId) {
|
|
16
|
+
const text = await this.storage.getText(this.key(projectId));
|
|
17
|
+
if (!text) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return JSON.parse(text);
|
|
21
|
+
}
|
|
22
|
+
async save(graph) {
|
|
23
|
+
await this.storage.put(this.key(graph.projectId), JSON.stringify(graph, null, 2), "application/json");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
function sleep(ms) {
|
|
2
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
|
+
}
|
|
4
|
+
export async function retry(operation, options = {}) {
|
|
5
|
+
const attempts = Math.max(1, options.attempts ?? 3);
|
|
6
|
+
const baseDelayMs = options.baseDelayMs ?? 150;
|
|
7
|
+
const maxDelayMs = options.maxDelayMs ?? 2000;
|
|
8
|
+
let lastError;
|
|
9
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
10
|
+
try {
|
|
11
|
+
return await operation();
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
lastError =
|
|
15
|
+
error;
|
|
16
|
+
if (attempt >= attempts) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
const exponential = Math.min(maxDelayMs, baseDelayMs *
|
|
20
|
+
2 **
|
|
21
|
+
(attempt - 1));
|
|
22
|
+
const jitter = Math.floor(Math.random() *
|
|
23
|
+
Math.max(1, exponential * 0.2));
|
|
24
|
+
await sleep(exponential +
|
|
25
|
+
jitter);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
throw lastError;
|
|
29
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { createReadStream, } from "node:fs";
|
|
3
|
+
import { createServer, } from "node:http";
|
|
4
|
+
import { dirname, resolve, } from "node:path";
|
|
5
|
+
import { fileURLToPath, } from "node:url";
|
|
6
|
+
import { loadConfig, ProjectManager, } from "../core/index.js";
|
|
7
|
+
import { CodeGraphStore, VisualizationBuilder, } from "../code-intelligence/index.js";
|
|
8
|
+
import { createStorageProvider, withStorageRetry, } from "../storage/index.js";
|
|
9
|
+
const PORT = Number(process.env
|
|
10
|
+
.TOOLNET_GRAPH_PORT ??
|
|
11
|
+
9749);
|
|
12
|
+
const HOST = process.env
|
|
13
|
+
.TOOLNET_GRAPH_HOST ??
|
|
14
|
+
"127.0.0.1";
|
|
15
|
+
function extractKey(item) {
|
|
16
|
+
if (typeof item ===
|
|
17
|
+
"string") {
|
|
18
|
+
return item;
|
|
19
|
+
}
|
|
20
|
+
if (!item ||
|
|
21
|
+
typeof item !==
|
|
22
|
+
"object") {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const value = item;
|
|
26
|
+
for (const field of [
|
|
27
|
+
"key",
|
|
28
|
+
"Key",
|
|
29
|
+
"path",
|
|
30
|
+
"name",
|
|
31
|
+
]) {
|
|
32
|
+
if (typeof value[field] ===
|
|
33
|
+
"string") {
|
|
34
|
+
return value[field];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
async function listKeys(storage, prefix) {
|
|
40
|
+
const items = await storage.list(prefix);
|
|
41
|
+
return items
|
|
42
|
+
.map(extractKey)
|
|
43
|
+
.filter((value) => Boolean(value));
|
|
44
|
+
}
|
|
45
|
+
async function readJson(storage, key) {
|
|
46
|
+
const text = await storage.getText(key);
|
|
47
|
+
if (!text) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(text);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function discoverProjects(storage) {
|
|
58
|
+
const keys = await listKeys(storage, "projects/");
|
|
59
|
+
/*
|
|
60
|
+
* Registry rule:
|
|
61
|
+
*
|
|
62
|
+
* Only folders containing project.json are real projects.
|
|
63
|
+
*
|
|
64
|
+
* Backups / random directories never enter dashboard.
|
|
65
|
+
*/
|
|
66
|
+
const remotes = [
|
|
67
|
+
...new Set(keys
|
|
68
|
+
.filter(key => /^projects\/[^/]+\/project\.json$/
|
|
69
|
+
.test(key))
|
|
70
|
+
.map(key => key.split("/")[1])),
|
|
71
|
+
];
|
|
72
|
+
const keySet = new Set(keys);
|
|
73
|
+
const projects = [];
|
|
74
|
+
for (const remote of remotes) {
|
|
75
|
+
const manifest = await readJson(storage, `projects/${remote}/project.json`);
|
|
76
|
+
if (!manifest ||
|
|
77
|
+
typeof manifest.id !==
|
|
78
|
+
"string") {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
projects.push({
|
|
82
|
+
id: manifest.id,
|
|
83
|
+
name: typeof manifest.name ===
|
|
84
|
+
"string"
|
|
85
|
+
? manifest.name
|
|
86
|
+
: remote,
|
|
87
|
+
remote,
|
|
88
|
+
hasGraph: keySet.has(`projects/${remote}/code/graph/current.json`),
|
|
89
|
+
hasArchitecture: keySet.has(`projects/${remote}/code/architecture/current.json`),
|
|
90
|
+
hasAnalysis: keySet.has(`projects/${remote}/code/analysis/current.json`),
|
|
91
|
+
hasVisualization: keySet.has(`projects/${remote}/code/visualization/graph.json`),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return projects.sort((a, b) => a.name.localeCompare(b.name));
|
|
95
|
+
}
|
|
96
|
+
async function main() {
|
|
97
|
+
const config = loadConfig();
|
|
98
|
+
const currentProject = new ProjectManager()
|
|
99
|
+
.detect();
|
|
100
|
+
const rawStorage = withStorageRetry(createStorageProvider({
|
|
101
|
+
provider: config.storage.provider,
|
|
102
|
+
huggingface: config.storage.huggingface,
|
|
103
|
+
localRoot: config.storage.localRoot,
|
|
104
|
+
}), {
|
|
105
|
+
attempts: 3,
|
|
106
|
+
});
|
|
107
|
+
if (rawStorage.name !==
|
|
108
|
+
"huggingface") {
|
|
109
|
+
console.warn("[graph-ui] Remote Hugging Face storage is not active.");
|
|
110
|
+
}
|
|
111
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
112
|
+
const publicDir = resolve(moduleDir, "public");
|
|
113
|
+
const vendorFile = resolve(publicDir, "vendor", "3d-force-graph.min.js");
|
|
114
|
+
async function getCatalog() {
|
|
115
|
+
const projects = await discoverProjects(rawStorage);
|
|
116
|
+
const currentRemote = currentProject.remote ??
|
|
117
|
+
currentProject.name;
|
|
118
|
+
const current = projects.find(project => project.remote ===
|
|
119
|
+
currentRemote &&
|
|
120
|
+
project.hasGraph);
|
|
121
|
+
const firstGraph = projects.find(project => project.hasGraph);
|
|
122
|
+
return {
|
|
123
|
+
projects,
|
|
124
|
+
defaultProject: current?.remote ??
|
|
125
|
+
firstGraph?.remote ??
|
|
126
|
+
null,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function buildProjectVisualization(project) {
|
|
130
|
+
const prefix = `projects/${project.remote}`;
|
|
131
|
+
const graphSnapshot = await readJson(rawStorage, `${prefix}/code/graph/current.json`);
|
|
132
|
+
if (!graphSnapshot ||
|
|
133
|
+
!Array.isArray(graphSnapshot.symbols) ||
|
|
134
|
+
!Array.isArray(graphSnapshot.edges)) {
|
|
135
|
+
throw new Error(`Code graph missing for project ${project.name}`);
|
|
136
|
+
}
|
|
137
|
+
const architecture = await readJson(rawStorage, `${prefix}/code/architecture/current.json`);
|
|
138
|
+
const analysis = await readJson(rawStorage, `${prefix}/code/analysis/current.json`);
|
|
139
|
+
const graph = new CodeGraphStore();
|
|
140
|
+
graph.import(graphSnapshot.symbols, graphSnapshot.edges);
|
|
141
|
+
const visualization = new VisualizationBuilder(graph).build(project.id, architecture, analysis);
|
|
142
|
+
/*
|
|
143
|
+
* Keep a cached visualization in that project's own
|
|
144
|
+
* namespace. It never crosses into another project.
|
|
145
|
+
*/
|
|
146
|
+
await rawStorage.put(`${prefix}/code/visualization/graph.json`, JSON.stringify(visualization, null, 2), "application/json");
|
|
147
|
+
return {
|
|
148
|
+
...visualization,
|
|
149
|
+
dashboard: {
|
|
150
|
+
id: project.id,
|
|
151
|
+
name: project.name,
|
|
152
|
+
remote: project.remote,
|
|
153
|
+
hasArchitecture: Boolean(architecture),
|
|
154
|
+
hasAnalysis: Boolean(analysis),
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const server = createServer(async (req, res) => {
|
|
159
|
+
try {
|
|
160
|
+
const url = new URL(req.url ??
|
|
161
|
+
"/", `http://${req.headers.host ??
|
|
162
|
+
"localhost"}`);
|
|
163
|
+
if (url.pathname ===
|
|
164
|
+
"/api/health") {
|
|
165
|
+
const catalog = await getCatalog();
|
|
166
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
167
|
+
res.setHeader("cache-control", "no-store");
|
|
168
|
+
res.end(JSON.stringify({
|
|
169
|
+
ok: true,
|
|
170
|
+
mode: "multi-project",
|
|
171
|
+
projects: catalog.projects
|
|
172
|
+
.length,
|
|
173
|
+
indexedProjects: catalog.projects
|
|
174
|
+
.filter(item => item.hasGraph)
|
|
175
|
+
.length,
|
|
176
|
+
defaultProject: catalog.defaultProject,
|
|
177
|
+
}));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (url.pathname ===
|
|
181
|
+
"/api/projects") {
|
|
182
|
+
const catalog = await getCatalog();
|
|
183
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
184
|
+
res.setHeader("cache-control", "no-store");
|
|
185
|
+
res.end(JSON.stringify(catalog));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (url.pathname ===
|
|
189
|
+
"/api/graph") {
|
|
190
|
+
const catalog = await getCatalog();
|
|
191
|
+
const requested = url.searchParams.get("project") ??
|
|
192
|
+
catalog.defaultProject;
|
|
193
|
+
if (!requested) {
|
|
194
|
+
res.statusCode =
|
|
195
|
+
404;
|
|
196
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
197
|
+
res.end(JSON.stringify({
|
|
198
|
+
error: "No indexed ToolNet project found.",
|
|
199
|
+
}));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const project = catalog.projects
|
|
203
|
+
.find(item => item.remote ===
|
|
204
|
+
requested);
|
|
205
|
+
if (!project) {
|
|
206
|
+
res.statusCode =
|
|
207
|
+
404;
|
|
208
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
209
|
+
res.end(JSON.stringify({
|
|
210
|
+
error: "Project not found.",
|
|
211
|
+
project: requested,
|
|
212
|
+
}));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (!project.hasGraph) {
|
|
216
|
+
res.statusCode =
|
|
217
|
+
409;
|
|
218
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
219
|
+
res.end(JSON.stringify({
|
|
220
|
+
error: "Project is registered but has not been indexed yet.",
|
|
221
|
+
project,
|
|
222
|
+
}));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const visualization = await buildProjectVisualization(project);
|
|
226
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
227
|
+
res.setHeader("cache-control", "no-store");
|
|
228
|
+
res.end(JSON.stringify(visualization));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (url.pathname ===
|
|
232
|
+
"/vendor/3d-force-graph.min.js") {
|
|
233
|
+
res.setHeader("content-type", "application/javascript; charset=utf-8");
|
|
234
|
+
createReadStream(vendorFile).pipe(res);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (url.pathname ===
|
|
238
|
+
"/" ||
|
|
239
|
+
url.pathname ===
|
|
240
|
+
"/index.html") {
|
|
241
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
242
|
+
res.setHeader("cache-control", "no-store");
|
|
243
|
+
createReadStream(resolve(publicDir, "index.html")).pipe(res);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
res.statusCode =
|
|
247
|
+
404;
|
|
248
|
+
res.end("Not found");
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
console.error(error);
|
|
252
|
+
res.statusCode =
|
|
253
|
+
500;
|
|
254
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
255
|
+
res.end(JSON.stringify({
|
|
256
|
+
error: error instanceof Error
|
|
257
|
+
? error.message
|
|
258
|
+
: String(error),
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
server.listen(PORT, HOST, () => {
|
|
263
|
+
console.log("ToolNet Multi-Project 3D Graph");
|
|
264
|
+
console.log(`Listening: http://${HOST}:${PORT}`);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
main().catch(error => {
|
|
268
|
+
console.error(error);
|
|
269
|
+
process.exit(1);
|
|
270
|
+
});
|