@swarmvaultai/engine 0.7.24 → 0.7.25
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/dist/chunk-MB7HPUTR.js +1364 -0
- package/dist/chunk-NAIERP4C.js +65 -0
- package/dist/hooks/claude.js +0 -0
- package/dist/hooks/copilot.js +0 -0
- package/dist/hooks/gemini.js +0 -0
- package/dist/index.d.ts +79 -3
- package/dist/index.js +646 -15
- package/dist/registry-UA42LQUQ.js +12 -0
- package/dist/token-estimation-TTONKT4O.js +10 -0
- package/package.json +9 -8
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -22,7 +22,12 @@ import {
|
|
|
22
22
|
uniqueBy,
|
|
23
23
|
writeFileIfChanged,
|
|
24
24
|
writeJsonFile
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-MB7HPUTR.js";
|
|
26
|
+
import {
|
|
27
|
+
estimatePageTokens,
|
|
28
|
+
estimateTokens,
|
|
29
|
+
trimToTokenBudget
|
|
30
|
+
} from "./chunk-NAIERP4C.js";
|
|
26
31
|
|
|
27
32
|
// src/agents.ts
|
|
28
33
|
import crypto from "crypto";
|
|
@@ -450,6 +455,43 @@ async function installConfiguredAgents(rootDir) {
|
|
|
450
455
|
);
|
|
451
456
|
}
|
|
452
457
|
|
|
458
|
+
// src/auto-commit.ts
|
|
459
|
+
import { execFile } from "child_process";
|
|
460
|
+
import { promisify } from "util";
|
|
461
|
+
var execFileAsync = promisify(execFile);
|
|
462
|
+
async function git(rootDir, ...args) {
|
|
463
|
+
const { stdout } = await execFileAsync("git", args, { cwd: rootDir });
|
|
464
|
+
return stdout.trim();
|
|
465
|
+
}
|
|
466
|
+
async function isGitRepo(rootDir) {
|
|
467
|
+
try {
|
|
468
|
+
await git(rootDir, "rev-parse", "--is-inside-work-tree");
|
|
469
|
+
return true;
|
|
470
|
+
} catch {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
async function autoCommitWikiChanges(rootDir, operation, detail, options) {
|
|
475
|
+
const { config, paths } = await loadVaultConfig(rootDir);
|
|
476
|
+
if (!options?.force && !config.autoCommit) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
if (!await isGitRepo(rootDir)) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
const wikiRelative = paths.wikiDir.replace(`${rootDir}/`, "");
|
|
483
|
+
const stateRelative = paths.stateDir.replace(`${rootDir}/`, "");
|
|
484
|
+
await git(rootDir, "add", wikiRelative, stateRelative).catch(() => {
|
|
485
|
+
});
|
|
486
|
+
const status = await git(rootDir, "diff", "--cached", "--stat");
|
|
487
|
+
if (!status) {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
const message = detail ? `vault ${operation}: ${detail}` : `vault ${operation}`;
|
|
491
|
+
await git(rootDir, "commit", "-m", message);
|
|
492
|
+
return message;
|
|
493
|
+
}
|
|
494
|
+
|
|
453
495
|
// src/graph-export.ts
|
|
454
496
|
import fs2 from "fs/promises";
|
|
455
497
|
import path2 from "path";
|
|
@@ -587,6 +629,316 @@ function graphCounts(graph) {
|
|
|
587
629
|
};
|
|
588
630
|
}
|
|
589
631
|
|
|
632
|
+
// src/graph-report-html.ts
|
|
633
|
+
function htmlEscape(text) {
|
|
634
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
635
|
+
}
|
|
636
|
+
function nodeTypeColor(type) {
|
|
637
|
+
const colors = {
|
|
638
|
+
source: "#f59e0b",
|
|
639
|
+
module: "#fb7185",
|
|
640
|
+
symbol: "#8b5cf6",
|
|
641
|
+
rationale: "#14b8a6",
|
|
642
|
+
concept: "#0ea5e9",
|
|
643
|
+
entity: "#22c55e"
|
|
644
|
+
};
|
|
645
|
+
return colors[type] ?? "#94a3b8";
|
|
646
|
+
}
|
|
647
|
+
function renderGraphReportHtml(graph, report) {
|
|
648
|
+
const nodesByType = /* @__PURE__ */ new Map();
|
|
649
|
+
for (const node of graph.nodes) {
|
|
650
|
+
nodesByType.set(node.type, (nodesByType.get(node.type) ?? 0) + 1);
|
|
651
|
+
}
|
|
652
|
+
const edgesByRelation = /* @__PURE__ */ new Map();
|
|
653
|
+
for (const edge of graph.edges) {
|
|
654
|
+
edgesByRelation.set(edge.relation, (edgesByRelation.get(edge.relation) ?? 0) + 1);
|
|
655
|
+
}
|
|
656
|
+
const pagesByKind = /* @__PURE__ */ new Map();
|
|
657
|
+
for (const page of graph.pages) {
|
|
658
|
+
const list = pagesByKind.get(page.kind) ?? [];
|
|
659
|
+
list.push(page);
|
|
660
|
+
pagesByKind.set(page.kind, list);
|
|
661
|
+
}
|
|
662
|
+
const godNodes = (report?.godNodes ?? []).slice(0, 15);
|
|
663
|
+
const bridgeNodes = (report?.bridgeNodes ?? []).slice(0, 10);
|
|
664
|
+
const communities = graph.communities ?? [];
|
|
665
|
+
const warnings = report?.warnings ?? [];
|
|
666
|
+
const overview = report?.overview ?? {
|
|
667
|
+
nodes: graph.nodes.length,
|
|
668
|
+
edges: graph.edges.length,
|
|
669
|
+
pages: graph.pages.length,
|
|
670
|
+
communities: communities.length
|
|
671
|
+
};
|
|
672
|
+
const sortedEdgeRelations = [...edgesByRelation.entries()].sort((a, b) => b[1] - a[1]);
|
|
673
|
+
const sortedNodeTypes = [...nodesByType.entries()].sort((a, b) => b[1] - a[1]);
|
|
674
|
+
const sortedCommunities2 = [...communities].sort((a, b) => b.nodeIds.length - a.nodeIds.length).slice(0, 20);
|
|
675
|
+
return `<!DOCTYPE html>
|
|
676
|
+
<html lang="en">
|
|
677
|
+
<head>
|
|
678
|
+
<meta charset="UTF-8">
|
|
679
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
680
|
+
<title>SwarmVault Graph Report</title>
|
|
681
|
+
<style>
|
|
682
|
+
:root {
|
|
683
|
+
--bg: #0f172a;
|
|
684
|
+
--surface: #1e293b;
|
|
685
|
+
--surface2: #334155;
|
|
686
|
+
--text: #e2e8f0;
|
|
687
|
+
--muted: #94a3b8;
|
|
688
|
+
--accent: #0ea5e9;
|
|
689
|
+
--accent2: #8b5cf6;
|
|
690
|
+
--border: #475569;
|
|
691
|
+
--success: #22c55e;
|
|
692
|
+
--warning: #f59e0b;
|
|
693
|
+
--danger: #ef4444;
|
|
694
|
+
}
|
|
695
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
696
|
+
body {
|
|
697
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
698
|
+
background: var(--bg);
|
|
699
|
+
color: var(--text);
|
|
700
|
+
line-height: 1.6;
|
|
701
|
+
padding: 2rem;
|
|
702
|
+
max-width: 1200px;
|
|
703
|
+
margin: 0 auto;
|
|
704
|
+
}
|
|
705
|
+
h1 {
|
|
706
|
+
font-size: 1.75rem;
|
|
707
|
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
708
|
+
-webkit-background-clip: text;
|
|
709
|
+
-webkit-text-fill-color: transparent;
|
|
710
|
+
margin-bottom: 0.25rem;
|
|
711
|
+
}
|
|
712
|
+
.subtitle { color: var(--muted); font-size: 0.85rem; margin-bottom: 2rem; }
|
|
713
|
+
h2 {
|
|
714
|
+
font-size: 1.15rem;
|
|
715
|
+
color: var(--text);
|
|
716
|
+
border-bottom: 1px solid var(--border);
|
|
717
|
+
padding-bottom: 0.5rem;
|
|
718
|
+
margin: 2rem 0 1rem;
|
|
719
|
+
}
|
|
720
|
+
.stats-grid {
|
|
721
|
+
display: grid;
|
|
722
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
723
|
+
gap: 1rem;
|
|
724
|
+
margin-bottom: 1.5rem;
|
|
725
|
+
}
|
|
726
|
+
.stat-card {
|
|
727
|
+
background: var(--surface);
|
|
728
|
+
border: 1px solid var(--border);
|
|
729
|
+
border-radius: 8px;
|
|
730
|
+
padding: 1rem;
|
|
731
|
+
text-align: center;
|
|
732
|
+
}
|
|
733
|
+
.stat-card .value {
|
|
734
|
+
font-size: 1.75rem;
|
|
735
|
+
font-weight: 700;
|
|
736
|
+
color: var(--accent);
|
|
737
|
+
}
|
|
738
|
+
.stat-card .label {
|
|
739
|
+
font-size: 0.8rem;
|
|
740
|
+
color: var(--muted);
|
|
741
|
+
text-transform: uppercase;
|
|
742
|
+
letter-spacing: 0.05em;
|
|
743
|
+
}
|
|
744
|
+
.badge {
|
|
745
|
+
display: inline-block;
|
|
746
|
+
padding: 0.15rem 0.5rem;
|
|
747
|
+
border-radius: 9999px;
|
|
748
|
+
font-size: 0.7rem;
|
|
749
|
+
font-weight: 600;
|
|
750
|
+
color: #fff;
|
|
751
|
+
}
|
|
752
|
+
table {
|
|
753
|
+
width: 100%;
|
|
754
|
+
border-collapse: collapse;
|
|
755
|
+
margin-bottom: 1rem;
|
|
756
|
+
}
|
|
757
|
+
th, td {
|
|
758
|
+
text-align: left;
|
|
759
|
+
padding: 0.5rem 0.75rem;
|
|
760
|
+
border-bottom: 1px solid var(--border);
|
|
761
|
+
font-size: 0.85rem;
|
|
762
|
+
}
|
|
763
|
+
th {
|
|
764
|
+
color: var(--muted);
|
|
765
|
+
font-weight: 600;
|
|
766
|
+
font-size: 0.75rem;
|
|
767
|
+
text-transform: uppercase;
|
|
768
|
+
letter-spacing: 0.05em;
|
|
769
|
+
}
|
|
770
|
+
tr:hover { background: var(--surface); }
|
|
771
|
+
.bar-container {
|
|
772
|
+
display: flex;
|
|
773
|
+
align-items: center;
|
|
774
|
+
gap: 0.5rem;
|
|
775
|
+
}
|
|
776
|
+
.bar {
|
|
777
|
+
height: 8px;
|
|
778
|
+
border-radius: 4px;
|
|
779
|
+
background: var(--accent);
|
|
780
|
+
min-width: 2px;
|
|
781
|
+
}
|
|
782
|
+
.warning-list {
|
|
783
|
+
list-style: none;
|
|
784
|
+
padding: 0;
|
|
785
|
+
}
|
|
786
|
+
.warning-list li {
|
|
787
|
+
padding: 0.5rem 0.75rem;
|
|
788
|
+
margin-bottom: 0.5rem;
|
|
789
|
+
background: var(--surface);
|
|
790
|
+
border-left: 3px solid var(--warning);
|
|
791
|
+
border-radius: 0 4px 4px 0;
|
|
792
|
+
font-size: 0.85rem;
|
|
793
|
+
}
|
|
794
|
+
.page-group { margin-bottom: 1.5rem; }
|
|
795
|
+
.page-group-title {
|
|
796
|
+
font-size: 0.85rem;
|
|
797
|
+
font-weight: 600;
|
|
798
|
+
color: var(--accent);
|
|
799
|
+
text-transform: capitalize;
|
|
800
|
+
margin-bottom: 0.5rem;
|
|
801
|
+
}
|
|
802
|
+
.page-list {
|
|
803
|
+
display: grid;
|
|
804
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
805
|
+
gap: 0.5rem;
|
|
806
|
+
}
|
|
807
|
+
.page-item {
|
|
808
|
+
background: var(--surface);
|
|
809
|
+
border: 1px solid var(--border);
|
|
810
|
+
border-radius: 6px;
|
|
811
|
+
padding: 0.5rem 0.75rem;
|
|
812
|
+
font-size: 0.8rem;
|
|
813
|
+
overflow: hidden;
|
|
814
|
+
text-overflow: ellipsis;
|
|
815
|
+
white-space: nowrap;
|
|
816
|
+
}
|
|
817
|
+
.page-item .path { color: var(--muted); font-size: 0.7rem; }
|
|
818
|
+
.empty { color: var(--muted); font-style: italic; font-size: 0.85rem; }
|
|
819
|
+
input[type="text"] {
|
|
820
|
+
width: 100%;
|
|
821
|
+
padding: 0.5rem 0.75rem;
|
|
822
|
+
background: var(--surface);
|
|
823
|
+
border: 1px solid var(--border);
|
|
824
|
+
border-radius: 6px;
|
|
825
|
+
color: var(--text);
|
|
826
|
+
font-size: 0.85rem;
|
|
827
|
+
margin-bottom: 1rem;
|
|
828
|
+
outline: none;
|
|
829
|
+
}
|
|
830
|
+
input[type="text"]:focus { border-color: var(--accent); }
|
|
831
|
+
.section { margin-bottom: 1rem; }
|
|
832
|
+
footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border); color: var(--muted); font-size: 0.75rem; text-align: center; }
|
|
833
|
+
</style>
|
|
834
|
+
</head>
|
|
835
|
+
<body>
|
|
836
|
+
<h1>SwarmVault Graph Report</h1>
|
|
837
|
+
<p class="subtitle">Generated ${htmlEscape(report?.generatedAt ?? graph.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString())}</p>
|
|
838
|
+
|
|
839
|
+
<div class="stats-grid">
|
|
840
|
+
<div class="stat-card"><div class="value">${overview.nodes}</div><div class="label">Nodes</div></div>
|
|
841
|
+
<div class="stat-card"><div class="value">${overview.edges}</div><div class="label">Edges</div></div>
|
|
842
|
+
<div class="stat-card"><div class="value">${overview.pages}</div><div class="label">Pages</div></div>
|
|
843
|
+
<div class="stat-card"><div class="value">${overview.communities}</div><div class="label">Communities</div></div>
|
|
844
|
+
<div class="stat-card"><div class="value">${graph.sources.length}</div><div class="label">Sources</div></div>
|
|
845
|
+
<div class="stat-card"><div class="value">${(graph.hyperedges ?? []).length}</div><div class="label">Hyperedges</div></div>
|
|
846
|
+
</div>
|
|
847
|
+
|
|
848
|
+
<h2>Node Types</h2>
|
|
849
|
+
<table>
|
|
850
|
+
<thead><tr><th>Type</th><th>Count</th><th></th></tr></thead>
|
|
851
|
+
<tbody>
|
|
852
|
+
${sortedNodeTypes.map(([type, count]) => {
|
|
853
|
+
const maxCount = sortedNodeTypes[0]?.[1] ?? 1;
|
|
854
|
+
const pct = Math.round(count / maxCount * 100);
|
|
855
|
+
return `<tr><td><span class="badge" style="background:${nodeTypeColor(type)}">${htmlEscape(type)}</span></td><td>${count}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%;background:${nodeTypeColor(type)}"></div></div></td></tr>`;
|
|
856
|
+
}).join("\n")}
|
|
857
|
+
</tbody>
|
|
858
|
+
</table>
|
|
859
|
+
|
|
860
|
+
<h2>Edge Relations</h2>
|
|
861
|
+
<table>
|
|
862
|
+
<thead><tr><th>Relation</th><th>Count</th><th></th></tr></thead>
|
|
863
|
+
<tbody>
|
|
864
|
+
${sortedEdgeRelations.map(([relation, count]) => {
|
|
865
|
+
const maxCount = sortedEdgeRelations[0]?.[1] ?? 1;
|
|
866
|
+
const pct = Math.round(count / maxCount * 100);
|
|
867
|
+
return `<tr><td>${htmlEscape(relation)}</td><td>${count}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%"></div></div></td></tr>`;
|
|
868
|
+
}).join("\n")}
|
|
869
|
+
</tbody>
|
|
870
|
+
</table>
|
|
871
|
+
|
|
872
|
+
${godNodes.length ? `<h2>God Nodes (Highest Connectivity)</h2>
|
|
873
|
+
<table>
|
|
874
|
+
<thead><tr><th>Label</th><th>Degree</th><th>Bridge Score</th><th></th></tr></thead>
|
|
875
|
+
<tbody>
|
|
876
|
+
${godNodes.map((node) => {
|
|
877
|
+
const maxDegree = godNodes[0]?.degree ?? 1;
|
|
878
|
+
const pct = Math.round((node.degree ?? 0) / maxDegree * 100);
|
|
879
|
+
return `<tr><td>${htmlEscape(node.label)}</td><td>${node.degree ?? 0}</td><td>${(node.bridgeScore ?? 0).toFixed(2)}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%;background:var(--accent2)"></div></div></td></tr>`;
|
|
880
|
+
}).join("\n")}
|
|
881
|
+
</tbody>
|
|
882
|
+
</table>` : ""}
|
|
883
|
+
|
|
884
|
+
${bridgeNodes.length ? `<h2>Bridge Nodes</h2>
|
|
885
|
+
<table>
|
|
886
|
+
<thead><tr><th>Label</th><th>Degree</th><th>Bridge Score</th></tr></thead>
|
|
887
|
+
<tbody>
|
|
888
|
+
${bridgeNodes.map((node) => `<tr><td>${htmlEscape(node.label)}</td><td>${node.degree ?? 0}</td><td>${(node.bridgeScore ?? 0).toFixed(2)}</td></tr>`).join("\n")}
|
|
889
|
+
</tbody>
|
|
890
|
+
</table>` : ""}
|
|
891
|
+
|
|
892
|
+
${sortedCommunities2.length ? `<h2>Communities</h2>
|
|
893
|
+
<table>
|
|
894
|
+
<thead><tr><th>Label</th><th>Nodes</th><th></th></tr></thead>
|
|
895
|
+
<tbody>
|
|
896
|
+
${sortedCommunities2.map((c) => {
|
|
897
|
+
const maxSize = sortedCommunities2[0]?.nodeIds.length ?? 1;
|
|
898
|
+
const pct = Math.round(c.nodeIds.length / maxSize * 100);
|
|
899
|
+
return `<tr><td>${htmlEscape(c.label)}</td><td>${c.nodeIds.length}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%;background:var(--success)"></div></div></td></tr>`;
|
|
900
|
+
}).join("\n")}
|
|
901
|
+
</tbody>
|
|
902
|
+
</table>` : ""}
|
|
903
|
+
|
|
904
|
+
${warnings.length ? `<h2>Warnings</h2>
|
|
905
|
+
<ul class="warning-list">
|
|
906
|
+
${warnings.map((w) => `<li>${htmlEscape(w)}</li>`).join("\n")}
|
|
907
|
+
</ul>` : ""}
|
|
908
|
+
|
|
909
|
+
<h2>Pages</h2>
|
|
910
|
+
<input type="text" id="page-filter" placeholder="Filter pages..." />
|
|
911
|
+
<div id="pages-container">
|
|
912
|
+
${[...pagesByKind.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(
|
|
913
|
+
([kind, pages]) => `<div class="page-group" data-kind="${htmlEscape(kind)}">
|
|
914
|
+
<div class="page-group-title">${htmlEscape(kind)} (${pages.length})</div>
|
|
915
|
+
<div class="page-list">
|
|
916
|
+
${pages.sort((a, b) => a.title.localeCompare(b.title)).map(
|
|
917
|
+
(p) => `<div class="page-item" data-title="${htmlEscape(p.title.toLowerCase())}"><strong>${htmlEscape(p.title)}</strong><div class="path">${htmlEscape(p.path)}</div></div>`
|
|
918
|
+
).join("\n ")}
|
|
919
|
+
</div>
|
|
920
|
+
</div>`
|
|
921
|
+
).join("\n")}
|
|
922
|
+
</div>
|
|
923
|
+
|
|
924
|
+
<footer>Generated by SwarmVault · ${graph.nodes.length} nodes · ${graph.edges.length} edges · ${graph.pages.length} pages</footer>
|
|
925
|
+
|
|
926
|
+
<script>
|
|
927
|
+
document.getElementById("page-filter").addEventListener("input", function(e) {
|
|
928
|
+
var query = e.target.value.toLowerCase();
|
|
929
|
+
document.querySelectorAll(".page-item").forEach(function(el) {
|
|
930
|
+
el.style.display = el.getAttribute("data-title").includes(query) ? "" : "none";
|
|
931
|
+
});
|
|
932
|
+
document.querySelectorAll(".page-group").forEach(function(group) {
|
|
933
|
+
var visible = group.querySelectorAll('.page-item[style=""], .page-item:not([style])').length;
|
|
934
|
+
group.style.display = visible > 0 || !query ? "" : "none";
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
</script>
|
|
938
|
+
</body>
|
|
939
|
+
</html>`;
|
|
940
|
+
}
|
|
941
|
+
|
|
590
942
|
// src/graph-export.ts
|
|
591
943
|
var NODE_COLORS = {
|
|
592
944
|
source: "#f59e0b",
|
|
@@ -1220,6 +1572,14 @@ async function exportGraphFormat(rootDir, format, outputPath) {
|
|
|
1220
1572
|
const resolvedPath = await writeGraphExport(outputPath, rendered);
|
|
1221
1573
|
return { format, outputPath: resolvedPath };
|
|
1222
1574
|
}
|
|
1575
|
+
async function exportGraphReportHtml(rootDir, outputPath) {
|
|
1576
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
1577
|
+
const graph = await loadGraph(rootDir);
|
|
1578
|
+
const report = await readJsonFile(path2.join(paths.wikiDir, "graph", "report.json"));
|
|
1579
|
+
const html = renderGraphReportHtml(graph, report);
|
|
1580
|
+
const resolvedPath = await writeGraphExport(outputPath, html);
|
|
1581
|
+
return { format: "report", outputPath: resolvedPath };
|
|
1582
|
+
}
|
|
1223
1583
|
function safeFileName(label) {
|
|
1224
1584
|
return label.replace(/[\\/*?:"<>|#^[\]]/g, "").replace(/\s+/g, " ").trim().slice(0, 200) || "unnamed";
|
|
1225
1585
|
}
|
|
@@ -1463,7 +1823,7 @@ function nodeMap(graph) {
|
|
|
1463
1823
|
function pageMap(graph) {
|
|
1464
1824
|
return new Map(graph.pages.map((page) => [page.id, page]));
|
|
1465
1825
|
}
|
|
1466
|
-
function
|
|
1826
|
+
function estimateTokens2(text) {
|
|
1467
1827
|
return Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN));
|
|
1468
1828
|
}
|
|
1469
1829
|
function estimateCorpusWords(texts) {
|
|
@@ -1500,7 +1860,7 @@ function benchmarkQueryTokens(graph, queryResult, pageContentsById) {
|
|
|
1500
1860
|
const target = nodesById.get(edge.target)?.label ?? edge.target;
|
|
1501
1861
|
lines.push(`EDGE ${source} --${edge.relation}/${edge.evidenceClass}/${edge.confidence.toFixed(2)}--> ${target}`);
|
|
1502
1862
|
}
|
|
1503
|
-
const queryTokens =
|
|
1863
|
+
const queryTokens = estimateTokens2(lines.join("\n"));
|
|
1504
1864
|
return {
|
|
1505
1865
|
question: queryResult.question,
|
|
1506
1866
|
queryTokens,
|
|
@@ -2217,6 +2577,67 @@ function graphDiff(oldGraph, newGraph) {
|
|
|
2217
2577
|
const summary = parts.length ? parts.join("; ") : "No changes";
|
|
2218
2578
|
return { addedNodes, removedNodes, addedEdges, removedEdges, addedPages, removedPages, summary };
|
|
2219
2579
|
}
|
|
2580
|
+
function blastRadius(graph, target, options) {
|
|
2581
|
+
const maxDepth = Math.max(1, Math.min(options?.maxDepth ?? 3, 10));
|
|
2582
|
+
const resolved = resolveNode(graph, target);
|
|
2583
|
+
const moduleNode = resolved?.type === "module" ? resolved : resolved?.moduleId ? graph.nodes.find((n) => n.id === resolved.moduleId) : void 0;
|
|
2584
|
+
if (!moduleNode) {
|
|
2585
|
+
const normalizedTarget = normalizeTarget(target);
|
|
2586
|
+
const candidate = graph.nodes.filter((n) => n.type === "module").find((n) => normalizeTarget(n.label).includes(normalizedTarget) || normalizeTarget(n.id).includes(normalizedTarget));
|
|
2587
|
+
if (!candidate) {
|
|
2588
|
+
return {
|
|
2589
|
+
target,
|
|
2590
|
+
totalAffected: 0,
|
|
2591
|
+
maxDepth,
|
|
2592
|
+
affectedModules: [],
|
|
2593
|
+
summary: `No module found matching "${target}".`
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
return blastRadius(graph, candidate.id, options);
|
|
2597
|
+
}
|
|
2598
|
+
const reverseImports = /* @__PURE__ */ new Map();
|
|
2599
|
+
for (const edge of graph.edges) {
|
|
2600
|
+
if (edge.relation === "imports") {
|
|
2601
|
+
const dependents = reverseImports.get(edge.target) ?? [];
|
|
2602
|
+
dependents.push(edge.source);
|
|
2603
|
+
reverseImports.set(edge.target, dependents);
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
const affected = [];
|
|
2607
|
+
const seen = /* @__PURE__ */ new Set([moduleNode.id]);
|
|
2608
|
+
const frontier = [{ id: moduleNode.id, depth: 0 }];
|
|
2609
|
+
const nodes = nodeById(graph);
|
|
2610
|
+
while (frontier.length > 0) {
|
|
2611
|
+
const current = frontier.shift();
|
|
2612
|
+
if (current.depth >= maxDepth) {
|
|
2613
|
+
continue;
|
|
2614
|
+
}
|
|
2615
|
+
for (const dependentId of reverseImports.get(current.id) ?? []) {
|
|
2616
|
+
if (seen.has(dependentId)) {
|
|
2617
|
+
continue;
|
|
2618
|
+
}
|
|
2619
|
+
seen.add(dependentId);
|
|
2620
|
+
const dependentNode = nodes.get(dependentId);
|
|
2621
|
+
const nextDepth = current.depth + 1;
|
|
2622
|
+
affected.push({
|
|
2623
|
+
moduleId: dependentId,
|
|
2624
|
+
label: dependentNode?.label ?? dependentId,
|
|
2625
|
+
depth: nextDepth
|
|
2626
|
+
});
|
|
2627
|
+
frontier.push({ id: dependentId, depth: nextDepth });
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
affected.sort((a, b) => a.depth - b.depth || a.label.localeCompare(b.label));
|
|
2631
|
+
const summary = affected.length ? `Changing "${moduleNode.label}" affects ${affected.length} module${affected.length === 1 ? "" : "s"} (max depth ${maxDepth}).` : `No modules depend on "${moduleNode.label}".`;
|
|
2632
|
+
return {
|
|
2633
|
+
target,
|
|
2634
|
+
resolvedModuleId: moduleNode.id,
|
|
2635
|
+
affectedModules: affected,
|
|
2636
|
+
totalAffected: affected.length,
|
|
2637
|
+
maxDepth,
|
|
2638
|
+
summary
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2220
2641
|
|
|
2221
2642
|
// src/hooks.ts
|
|
2222
2643
|
import fs4 from "fs/promises";
|
|
@@ -14024,6 +14445,33 @@ async function semanticGraphMatches(rootDir, graph, question, limit = 12) {
|
|
|
14024
14445
|
score: Math.max(0, Number((cosineSimilarity(queryVector, vectors.get(`${item.kind}:${item.id}`) ?? []) * 100).toFixed(2)))
|
|
14025
14446
|
})).filter((match) => match.score >= 18).sort((left, right) => right.score - left.score || left.label.localeCompare(right.label)).slice(0, limit);
|
|
14026
14447
|
}
|
|
14448
|
+
async function semanticPageSearch(rootDir, graph, query, limit = 10) {
|
|
14449
|
+
const items = await buildEmbeddableItems(rootDir, graph);
|
|
14450
|
+
const pageItems = items.filter((item) => item.kind === "page");
|
|
14451
|
+
if (!pageItems.length) {
|
|
14452
|
+
return [];
|
|
14453
|
+
}
|
|
14454
|
+
const { provider, vectors } = await resolveVectorsForItems(rootDir, graph.generatedAt, items);
|
|
14455
|
+
if (!provider) {
|
|
14456
|
+
return [];
|
|
14457
|
+
}
|
|
14458
|
+
const [queryVector] = await provider.embedTexts([query]);
|
|
14459
|
+
if (!Array.isArray(queryVector) || queryVector.length === 0) {
|
|
14460
|
+
return [];
|
|
14461
|
+
}
|
|
14462
|
+
const pageMap2 = new Map(graph.pages.map((page) => [page.id, page]));
|
|
14463
|
+
return pageItems.map((item) => {
|
|
14464
|
+
const page = pageMap2.get(item.id);
|
|
14465
|
+
return {
|
|
14466
|
+
pageId: item.id,
|
|
14467
|
+
path: page?.path ?? "",
|
|
14468
|
+
title: item.label,
|
|
14469
|
+
kind: page?.kind ?? "",
|
|
14470
|
+
status: page?.status ?? "",
|
|
14471
|
+
score: cosineSimilarity(queryVector, vectors.get(`page:${item.id}`) ?? [])
|
|
14472
|
+
};
|
|
14473
|
+
}).filter((result) => result.score >= 0.25 && result.path).sort((left, right) => right.score - left.score).slice(0, limit);
|
|
14474
|
+
}
|
|
14027
14475
|
function distinctScope(left, right) {
|
|
14028
14476
|
const leftSources = new Set(left.sourceIds);
|
|
14029
14477
|
const rightSources = new Set(right.sourceIds);
|
|
@@ -16633,6 +17081,38 @@ ${excerpt.trim()}`.trim();
|
|
|
16633
17081
|
db.exec("INSERT INTO page_search (rowid, title, body) SELECT rowid, title, body FROM pages;");
|
|
16634
17082
|
db.close();
|
|
16635
17083
|
}
|
|
17084
|
+
function mergeSearchResults(ftsResults, semanticHits, limit) {
|
|
17085
|
+
const k = 60;
|
|
17086
|
+
const scores = /* @__PURE__ */ new Map();
|
|
17087
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
17088
|
+
for (let i = 0; i < ftsResults.length; i++) {
|
|
17089
|
+
const r = ftsResults[i];
|
|
17090
|
+
scores.set(r.pageId, (scores.get(r.pageId) ?? 0) + 1 / (k + i + 1));
|
|
17091
|
+
resultMap.set(r.pageId, r);
|
|
17092
|
+
}
|
|
17093
|
+
for (let i = 0; i < semanticHits.length; i++) {
|
|
17094
|
+
const hit = semanticHits[i];
|
|
17095
|
+
scores.set(hit.pageId, (scores.get(hit.pageId) ?? 0) + 1 / (k + i + 1));
|
|
17096
|
+
if (!resultMap.has(hit.pageId)) {
|
|
17097
|
+
resultMap.set(hit.pageId, {
|
|
17098
|
+
pageId: hit.pageId,
|
|
17099
|
+
path: hit.path,
|
|
17100
|
+
title: hit.title,
|
|
17101
|
+
snippet: "",
|
|
17102
|
+
rank: -hit.score,
|
|
17103
|
+
kind: hit.kind,
|
|
17104
|
+
status: hit.status,
|
|
17105
|
+
projectIds: [],
|
|
17106
|
+
sourceType: void 0,
|
|
17107
|
+
sourceClass: void 0
|
|
17108
|
+
});
|
|
17109
|
+
}
|
|
17110
|
+
}
|
|
17111
|
+
return [...scores.entries()].sort(([, a], [, b]) => b - a).slice(0, limit).map(([pageId, rrfScore]) => {
|
|
17112
|
+
const result = resultMap.get(pageId);
|
|
17113
|
+
return { ...result, rank: -rrfScore };
|
|
17114
|
+
});
|
|
17115
|
+
}
|
|
16636
17116
|
function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
16637
17117
|
const options = typeof limitOrOptions === "number" ? { limit: limitOrOptions } : limitOrOptions;
|
|
16638
17118
|
const ftsQuery = toFtsQuery(query);
|
|
@@ -16974,7 +17454,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
16974
17454
|
if (!providerConfig) {
|
|
16975
17455
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
16976
17456
|
}
|
|
16977
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
17457
|
+
const { createProvider: createProvider2 } = await import("./registry-UA42LQUQ.js");
|
|
16978
17458
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
16979
17459
|
}
|
|
16980
17460
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -20553,6 +21033,45 @@ async function compileVault(rootDir, options = {}) {
|
|
|
20553
21033
|
`benchmark=${benchmark.ok ? "ok" : `error:${benchmark.error}`}`
|
|
20554
21034
|
]
|
|
20555
21035
|
});
|
|
21036
|
+
let tokenStats;
|
|
21037
|
+
if (options.maxTokens && options.maxTokens > 0) {
|
|
21038
|
+
const { estimatePageTokens: estimatePageTokens2, trimToTokenBudget: trimToTokenBudget2 } = await import("./token-estimation-TTONKT4O.js");
|
|
21039
|
+
const nodeDegreeLookup = /* @__PURE__ */ new Map();
|
|
21040
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21041
|
+
if (graph) {
|
|
21042
|
+
for (const node of graph.nodes) {
|
|
21043
|
+
if (node.pageId && node.degree) {
|
|
21044
|
+
const existing = nodeDegreeLookup.get(node.pageId) ?? 0;
|
|
21045
|
+
nodeDegreeLookup.set(node.pageId, Math.max(existing, node.degree));
|
|
21046
|
+
}
|
|
21047
|
+
}
|
|
21048
|
+
}
|
|
21049
|
+
const estimates = await Promise.all(
|
|
21050
|
+
sync.allPages.map(async (page) => {
|
|
21051
|
+
const fullPath = path23.join(paths.wikiDir, page.path);
|
|
21052
|
+
let content = "";
|
|
21053
|
+
try {
|
|
21054
|
+
content = await fs19.readFile(fullPath, "utf8");
|
|
21055
|
+
} catch {
|
|
21056
|
+
}
|
|
21057
|
+
return estimatePageTokens2(page.id, page.path, page.kind, content, nodeDegreeLookup.get(page.id), page.confidence);
|
|
21058
|
+
})
|
|
21059
|
+
);
|
|
21060
|
+
const budgetResult = trimToTokenBudget2(estimates, options.maxTokens);
|
|
21061
|
+
for (const dropped of budgetResult.dropped) {
|
|
21062
|
+
const fullPath = path23.join(paths.wikiDir, dropped.path);
|
|
21063
|
+
try {
|
|
21064
|
+
await fs19.unlink(fullPath);
|
|
21065
|
+
} catch {
|
|
21066
|
+
}
|
|
21067
|
+
}
|
|
21068
|
+
tokenStats = {
|
|
21069
|
+
estimatedTokens: budgetResult.totalTokens,
|
|
21070
|
+
maxTokens: options.maxTokens,
|
|
21071
|
+
pagesKept: budgetResult.kept.length,
|
|
21072
|
+
pagesDropped: budgetResult.dropped.length
|
|
21073
|
+
};
|
|
21074
|
+
}
|
|
20556
21075
|
return {
|
|
20557
21076
|
graphPath: paths.graphPath,
|
|
20558
21077
|
pageCount: sync.allPages.length,
|
|
@@ -20564,7 +21083,8 @@ async function compileVault(rootDir, options = {}) {
|
|
|
20564
21083
|
postPassApprovalId,
|
|
20565
21084
|
postPassApprovalDir,
|
|
20566
21085
|
promotedPageIds: sync.promotedPageIds,
|
|
20567
|
-
candidatePageCount: sync.candidatePageCount
|
|
21086
|
+
candidatePageCount: sync.candidatePageCount,
|
|
21087
|
+
tokenStats
|
|
20568
21088
|
};
|
|
20569
21089
|
}
|
|
20570
21090
|
async function queryVault(rootDir, options) {
|
|
@@ -20921,11 +21441,59 @@ ${orchestrationNotes.join("\n")}
|
|
|
20921
21441
|
};
|
|
20922
21442
|
}
|
|
20923
21443
|
async function searchVault(rootDir, query, limit = 5) {
|
|
20924
|
-
const { paths } = await loadVaultConfig(rootDir);
|
|
21444
|
+
const { paths, config } = await loadVaultConfig(rootDir);
|
|
20925
21445
|
if (!await fileExists(paths.searchDbPath)) {
|
|
20926
21446
|
await compileVault(rootDir, {});
|
|
20927
21447
|
}
|
|
20928
|
-
|
|
21448
|
+
const hybrid = config.search?.hybrid !== false;
|
|
21449
|
+
const ftsResults = searchPages(paths.searchDbPath, query, hybrid ? limit * 3 : limit);
|
|
21450
|
+
if (!hybrid || !await fileExists(paths.graphPath)) {
|
|
21451
|
+
return ftsResults.slice(0, limit);
|
|
21452
|
+
}
|
|
21453
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21454
|
+
if (!graph) {
|
|
21455
|
+
return ftsResults.slice(0, limit);
|
|
21456
|
+
}
|
|
21457
|
+
const semanticHits = await semanticPageSearch(rootDir, graph, query, limit * 3).catch(() => []);
|
|
21458
|
+
if (!semanticHits.length) {
|
|
21459
|
+
return ftsResults.slice(0, limit);
|
|
21460
|
+
}
|
|
21461
|
+
const merged = mergeSearchResults(ftsResults, semanticHits, limit);
|
|
21462
|
+
if (config.search?.rerank && merged.length > 1) {
|
|
21463
|
+
return rerankSearchResults(rootDir, query, merged, limit);
|
|
21464
|
+
}
|
|
21465
|
+
return merged;
|
|
21466
|
+
}
|
|
21467
|
+
async function rerankSearchResults(rootDir, query, results, limit) {
|
|
21468
|
+
const provider = await getProviderForTask(rootDir, "queryProvider");
|
|
21469
|
+
const candidates = results.slice(0, Math.min(results.length, 20)).map((r, i) => `[${i}] ${r.title} \u2014 ${r.snippet || r.path}`).join("\n");
|
|
21470
|
+
const prompt = `Given the search query: "${query}"
|
|
21471
|
+
|
|
21472
|
+
Rank these results by relevance (most relevant first).
|
|
21473
|
+
|
|
21474
|
+
${candidates}`;
|
|
21475
|
+
try {
|
|
21476
|
+
const indices = await provider.generateStructured(
|
|
21477
|
+
{ prompt, system: "You are a search result ranker." },
|
|
21478
|
+
z7.array(z7.number().int().nonnegative())
|
|
21479
|
+
);
|
|
21480
|
+
const reranked = [];
|
|
21481
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21482
|
+
for (const idx of indices) {
|
|
21483
|
+
if (idx >= 0 && idx < results.length && !seen.has(idx)) {
|
|
21484
|
+
seen.add(idx);
|
|
21485
|
+
reranked.push(results[idx]);
|
|
21486
|
+
}
|
|
21487
|
+
}
|
|
21488
|
+
for (let i = 0; i < results.length && reranked.length < limit; i++) {
|
|
21489
|
+
if (!seen.has(i)) {
|
|
21490
|
+
reranked.push(results[i]);
|
|
21491
|
+
}
|
|
21492
|
+
}
|
|
21493
|
+
return reranked.slice(0, limit);
|
|
21494
|
+
} catch {
|
|
21495
|
+
return results.slice(0, limit);
|
|
21496
|
+
}
|
|
20929
21497
|
}
|
|
20930
21498
|
async function ensureCompiledGraph(rootDir) {
|
|
20931
21499
|
const { paths } = await loadVaultConfig(rootDir);
|
|
@@ -21029,6 +21597,10 @@ async function listGodNodes(rootDir, limit = 10) {
|
|
|
21029
21597
|
const graph = await ensureCompiledGraph(rootDir);
|
|
21030
21598
|
return topGodNodes(graph, limit);
|
|
21031
21599
|
}
|
|
21600
|
+
async function blastRadiusVault(rootDir, target, options) {
|
|
21601
|
+
const graph = await ensureCompiledGraph(rootDir);
|
|
21602
|
+
return blastRadius(graph, target, options);
|
|
21603
|
+
}
|
|
21032
21604
|
async function listPages(rootDir) {
|
|
21033
21605
|
const { paths } = await loadVaultConfig(rootDir);
|
|
21034
21606
|
const graph = await readJsonFile(paths.graphPath);
|
|
@@ -21263,7 +21835,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
21263
21835
|
}
|
|
21264
21836
|
|
|
21265
21837
|
// src/mcp.ts
|
|
21266
|
-
var SERVER_VERSION = "0.7.
|
|
21838
|
+
var SERVER_VERSION = "0.7.25";
|
|
21267
21839
|
async function createMcpServer(rootDir) {
|
|
21268
21840
|
const server = new McpServer({
|
|
21269
21841
|
name: "swarmvault",
|
|
@@ -21413,6 +21985,19 @@ async function createMcpServer(rootDir) {
|
|
|
21413
21985
|
return asToolText(await listGodNodes(rootDir, limit ?? 10));
|
|
21414
21986
|
})
|
|
21415
21987
|
);
|
|
21988
|
+
server.registerTool(
|
|
21989
|
+
"blast_radius",
|
|
21990
|
+
{
|
|
21991
|
+
description: "Analyze the impact of changing a file or module by tracing reverse import edges.",
|
|
21992
|
+
inputSchema: {
|
|
21993
|
+
target: z8.string().min(1).describe("File path, module label, or module id"),
|
|
21994
|
+
maxDepth: z8.number().int().min(1).max(10).optional().describe("Maximum traversal depth (default 3)")
|
|
21995
|
+
}
|
|
21996
|
+
},
|
|
21997
|
+
safeHandler(async ({ target, maxDepth }) => {
|
|
21998
|
+
return asToolText(await blastRadiusVault(rootDir, target, { maxDepth: maxDepth ?? 3 }));
|
|
21999
|
+
})
|
|
22000
|
+
);
|
|
21416
22001
|
server.registerTool(
|
|
21417
22002
|
"query_vault",
|
|
21418
22003
|
{
|
|
@@ -21450,11 +22035,12 @@ async function createMcpServer(rootDir) {
|
|
|
21450
22035
|
{
|
|
21451
22036
|
description: "Compile source manifests into wiki pages, graph data, and search index.",
|
|
21452
22037
|
inputSchema: {
|
|
21453
|
-
approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes")
|
|
22038
|
+
approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes"),
|
|
22039
|
+
maxTokens: z8.number().int().min(1e3).optional().describe("Maximum token budget for wiki output")
|
|
21454
22040
|
}
|
|
21455
22041
|
},
|
|
21456
|
-
safeHandler(async ({ approve }) => {
|
|
21457
|
-
const result = await compileVault(rootDir, { approve: approve ?? false });
|
|
22042
|
+
safeHandler(async ({ approve, maxTokens }) => {
|
|
22043
|
+
const result = await compileVault(rootDir, { approve: approve ?? false, maxTokens });
|
|
21458
22044
|
return asToolText(result);
|
|
21459
22045
|
})
|
|
21460
22046
|
);
|
|
@@ -23719,11 +24305,11 @@ async function deleteManagedSource(rootDir, id) {
|
|
|
23719
24305
|
}
|
|
23720
24306
|
|
|
23721
24307
|
// src/viewer.ts
|
|
23722
|
-
import { execFile } from "child_process";
|
|
24308
|
+
import { execFile as execFile2 } from "child_process";
|
|
23723
24309
|
import fs23 from "fs/promises";
|
|
23724
24310
|
import http from "http";
|
|
23725
24311
|
import path28 from "path";
|
|
23726
|
-
import { promisify } from "util";
|
|
24312
|
+
import { promisify as promisify2 } from "util";
|
|
23727
24313
|
import matter11 from "gray-matter";
|
|
23728
24314
|
import mime2 from "mime-types";
|
|
23729
24315
|
|
|
@@ -24337,7 +24923,7 @@ async function getWatchStatus(rootDir) {
|
|
|
24337
24923
|
}
|
|
24338
24924
|
|
|
24339
24925
|
// src/viewer.ts
|
|
24340
|
-
var
|
|
24926
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
24341
24927
|
async function isReadableFile(absolutePath) {
|
|
24342
24928
|
try {
|
|
24343
24929
|
const stats = await fs23.stat(absolutePath);
|
|
@@ -24404,7 +24990,7 @@ async function ensureViewerDist(viewerDistDir) {
|
|
|
24404
24990
|
}
|
|
24405
24991
|
const viewerProjectDir = path28.dirname(viewerDistDir);
|
|
24406
24992
|
if (await fileExists(path28.join(viewerProjectDir, "package.json"))) {
|
|
24407
|
-
await
|
|
24993
|
+
await execFileAsync2("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
24408
24994
|
}
|
|
24409
24995
|
}
|
|
24410
24996
|
async function startGraphServer(rootDir, port, options = {}) {
|
|
@@ -24584,6 +25170,44 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
24584
25170
|
response.end(JSON.stringify(result));
|
|
24585
25171
|
return;
|
|
24586
25172
|
}
|
|
25173
|
+
if (url.pathname === "/api/clip" && request.method === "POST") {
|
|
25174
|
+
const body = await readJsonBody(request);
|
|
25175
|
+
const clipUrl = typeof body.url === "string" ? body.url.trim() : "";
|
|
25176
|
+
if (!clipUrl) {
|
|
25177
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
25178
|
+
response.end(JSON.stringify({ error: "Missing url field." }));
|
|
25179
|
+
return;
|
|
25180
|
+
}
|
|
25181
|
+
const manifest = await ingestInput(rootDir, clipUrl);
|
|
25182
|
+
response.writeHead(200, { "content-type": "application/json", "access-control-allow-origin": "*" });
|
|
25183
|
+
response.end(JSON.stringify({ ok: true, sourceId: manifest.sourceId, title: manifest.title }));
|
|
25184
|
+
return;
|
|
25185
|
+
}
|
|
25186
|
+
if (url.pathname === "/api/clip" && request.method === "OPTIONS") {
|
|
25187
|
+
response.writeHead(204, {
|
|
25188
|
+
"access-control-allow-origin": "*",
|
|
25189
|
+
"access-control-allow-methods": "POST, OPTIONS",
|
|
25190
|
+
"access-control-allow-headers": "content-type"
|
|
25191
|
+
});
|
|
25192
|
+
response.end();
|
|
25193
|
+
return;
|
|
25194
|
+
}
|
|
25195
|
+
if (url.pathname === "/api/bookmarklet") {
|
|
25196
|
+
const script = `javascript:void(fetch('http://localhost:${effectivePort}/api/clip',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url:location.href})}).then(r=>r.json()).then(d=>alert('Clipped: '+(d.title||d.sourceId))).catch(e=>alert('Clip failed: '+e.message)))`;
|
|
25197
|
+
response.writeHead(200, { "content-type": "text/html" });
|
|
25198
|
+
response.end(
|
|
25199
|
+
[
|
|
25200
|
+
"<!doctype html><html><head><title>SwarmVault Clipper</title></head><body>",
|
|
25201
|
+
"<h1>SwarmVault Clipper</h1>",
|
|
25202
|
+
`<p>Drag this link to your bookmarks bar:</p>`,
|
|
25203
|
+
`<p style="font-size:1.5em"><a href="${script.replace(/&/g, "&").replace(/"/g, """)}">Clip to SwarmVault</a></p>`,
|
|
25204
|
+
`<p>When clicked on any page, it sends the URL to your running SwarmVault instance for ingestion.</p>`,
|
|
25205
|
+
`<p>Server: <code>http://localhost:${effectivePort}</code></p>`,
|
|
25206
|
+
"</body></html>"
|
|
25207
|
+
].join("\n")
|
|
25208
|
+
);
|
|
25209
|
+
return;
|
|
25210
|
+
}
|
|
24587
25211
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
24588
25212
|
const target = path28.join(paths.viewerDistDir, relativePath);
|
|
24589
25213
|
const fallback = path28.join(paths.viewerDistDir, "index.html");
|
|
@@ -24702,7 +25326,10 @@ export {
|
|
|
24702
25326
|
addManagedSource,
|
|
24703
25327
|
archiveCandidate,
|
|
24704
25328
|
assertProviderCapability,
|
|
25329
|
+
autoCommitWikiChanges,
|
|
24705
25330
|
benchmarkVault,
|
|
25331
|
+
blastRadius,
|
|
25332
|
+
blastRadiusVault,
|
|
24706
25333
|
bootstrapDemo,
|
|
24707
25334
|
compileVault,
|
|
24708
25335
|
createMcpServer,
|
|
@@ -24711,10 +25338,13 @@ export {
|
|
|
24711
25338
|
defaultVaultConfig,
|
|
24712
25339
|
defaultVaultSchema,
|
|
24713
25340
|
deleteManagedSource,
|
|
25341
|
+
estimatePageTokens,
|
|
25342
|
+
estimateTokens,
|
|
24714
25343
|
explainGraphVault,
|
|
24715
25344
|
exploreVault,
|
|
24716
25345
|
exportGraphFormat,
|
|
24717
25346
|
exportGraphHtml,
|
|
25347
|
+
exportGraphReportHtml,
|
|
24718
25348
|
exportObsidianCanvas,
|
|
24719
25349
|
exportObsidianVault,
|
|
24720
25350
|
getGitHookStatus,
|
|
@@ -24771,6 +25401,7 @@ export {
|
|
|
24771
25401
|
startMcpServer,
|
|
24772
25402
|
syncTrackedRepos,
|
|
24773
25403
|
syncTrackedReposForWatch,
|
|
25404
|
+
trimToTokenBudget,
|
|
24774
25405
|
uninstallGitHooks,
|
|
24775
25406
|
watchVault
|
|
24776
25407
|
};
|