stellavault 0.8.5 → 0.8.6

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.
@@ -13,7 +13,7 @@
13
13
  html, body, #root { width: 100%; height: 100%; overflow: hidden; }
14
14
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0a0a0f; color: #e0e0e0; }
15
15
  </style>
16
- <script type="module" crossorigin src="/assets/index-KgyFgRwK.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-nyIICe1Z.js"></script>
17
17
  </head>
18
18
  <body>
19
19
  <div id="root"></div>
@@ -997,9 +997,13 @@ var init_indexer = __esm({
997
997
  var graph_data_exports = {};
998
998
  __export(graph_data_exports, {
999
999
  buildClusteredGraph: () => buildClusteredGraph,
1000
- buildGraphData: () => buildGraphData
1000
+ buildGraphData: () => buildGraphData,
1001
+ flattenClusterLevel: () => flattenClusterLevel
1001
1002
  });
1002
1003
  import { createHash as createHash2 } from "node:crypto";
1004
+ function paletteHex(clusterId) {
1005
+ return PALETTE_HEX[(clusterId % PALETTE_HEX.length + PALETTE_HEX.length) % PALETTE_HEX.length];
1006
+ }
1003
1007
  async function buildGraphData(store, options = {}) {
1004
1008
  const {
1005
1009
  // 0.35: 384-dim 임베딩에서 의미있는 코사인 임계. 0.15는 사실상 모든 페어가 통과 →
@@ -1153,6 +1157,43 @@ async function buildGraphData(store, options = {}) {
1153
1157
  }
1154
1158
  };
1155
1159
  }
1160
+ function flattenClusterLevel(level) {
1161
+ const nodes = level.superNodes.map((sn) => ({
1162
+ id: `cluster:${sn.clusterId}`,
1163
+ label: sn.label,
1164
+ filePath: "",
1165
+ tags: [],
1166
+ clusterId: sn.clusterId,
1167
+ position: sn.position,
1168
+ size: sn.size,
1169
+ source: "cluster",
1170
+ type: "cluster",
1171
+ isCluster: true,
1172
+ memberCount: sn.memberCount,
1173
+ representativeId: sn.representativeId
1174
+ }));
1175
+ const edges = level.metaEdges.map((me) => ({
1176
+ source: `cluster:${me.sourceCluster}`,
1177
+ target: `cluster:${me.targetCluster}`,
1178
+ weight: me.weight
1179
+ }));
1180
+ const clusters = level.superNodes.map((sn) => ({
1181
+ id: sn.clusterId,
1182
+ label: sn.label,
1183
+ color: paletteHex(sn.clusterId),
1184
+ nodeCount: sn.memberCount
1185
+ }));
1186
+ return {
1187
+ nodes,
1188
+ edges,
1189
+ clusters,
1190
+ stats: {
1191
+ nodeCount: nodes.length,
1192
+ edgeCount: edges.length,
1193
+ clusterCount: clusters.length
1194
+ }
1195
+ };
1196
+ }
1156
1197
  async function buildClusteredGraph(store, options = {}) {
1157
1198
  const mode = options.mode ?? "semantic";
1158
1199
  const clusterCap = Math.max(200, Math.floor(options.clusterCap ?? (Number(process.env.GRAPH_CLUSTER_CAP) || 3e3)));
@@ -1456,7 +1497,7 @@ function kMeans(flat, n, dims, k, maxIter = 50) {
1456
1497
  }
1457
1498
  return assignments;
1458
1499
  }
1459
- var CLUSTER_COLORS;
1500
+ var CLUSTER_COLORS, PALETTE_HEX;
1460
1501
  var init_graph_data = __esm({
1461
1502
  "packages/core/dist/api/graph-data.js"() {
1462
1503
  "use strict";
@@ -1477,6 +1518,23 @@ var init_graph_data = __esm({
1477
1518
  "#a3e635",
1478
1519
  "#fb923c"
1479
1520
  ];
1521
+ PALETTE_HEX = [
1522
+ "#7c3aed",
1523
+ "#ec4899",
1524
+ "#f59e0b",
1525
+ "#10b981",
1526
+ "#3b82f6",
1527
+ "#ef4444",
1528
+ "#06b6d4",
1529
+ "#84cc16",
1530
+ "#f97316",
1531
+ "#8b5cf6",
1532
+ "#14b8a6",
1533
+ "#e879f9",
1534
+ "#eab308",
1535
+ "#22d3ee",
1536
+ "#fb7185"
1537
+ ];
1480
1538
  }
1481
1539
  });
1482
1540
 
@@ -7548,17 +7606,40 @@ function createApiServer(options) {
7548
7606
  }
7549
7607
  const GRAPH_CACHE_TTL = 5 * 60 * 1e3;
7550
7608
  const graphCaches = /* @__PURE__ */ new Map();
7609
+ function parseView(q) {
7610
+ return q === "raw" ? "raw" : "cluster";
7611
+ }
7612
+ function parseMode(q) {
7613
+ return q === "folder" ? "folder" : "semantic";
7614
+ }
7615
+ function clampCap(view, raw) {
7616
+ if (!Number.isFinite(raw) || raw <= 0)
7617
+ return void 0;
7618
+ const max = view === "raw" ? 4e3 : 6e3;
7619
+ const rounded = Math.round(raw / 1e3) * 1e3;
7620
+ return Math.max(1e3, Math.min(max, rounded));
7621
+ }
7622
+ async function buildGraphEntry(view, mode, cap) {
7623
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7624
+ if (view === "cluster") {
7625
+ const g = await buildClusteredGraph(store, { mode, clusterCap: cap });
7626
+ return { data: flattenClusterLevel(g.clusterLevel), clustered: g, generatedAt: now, cachedAt: Date.now() };
7627
+ }
7628
+ const data = await buildGraphData(store, { mode, nodeCap: cap });
7629
+ return { data, generatedAt: now, cachedAt: Date.now() };
7630
+ }
7551
7631
  app.get("/api/graph", async (req, res) => {
7552
7632
  try {
7553
- const mode = req.query.mode === "folder" ? "folder" : "semantic";
7554
- const cached = graphCaches.get(mode);
7633
+ const view = parseView(req.query.view);
7634
+ const mode = parseMode(req.query.mode);
7635
+ const cap = clampCap(view, Number(req.query.cap));
7636
+ const cacheKey = `${view}:${mode}:${cap ?? ""}`;
7637
+ const cached = graphCaches.get(cacheKey);
7555
7638
  if (!cached || Date.now() - cached.cachedAt > GRAPH_CACHE_TTL) {
7556
- const data = await buildGraphData(store, { mode });
7557
- const now = (/* @__PURE__ */ new Date()).toISOString();
7558
- graphCaches.set(mode, { data, generatedAt: now, cachedAt: Date.now() });
7639
+ graphCaches.set(cacheKey, await buildGraphEntry(view, mode, cap));
7559
7640
  }
7560
- const entry = graphCaches.get(mode);
7561
- res.json({ data: entry.data, generatedAt: entry.generatedAt, mode });
7641
+ const entry = graphCaches.get(cacheKey);
7642
+ res.json({ data: entry.data, generatedAt: entry.generatedAt, mode, view });
7562
7643
  } catch (err) {
7563
7644
  console.error(err);
7564
7645
  res.status(500).json({ error: "Internal server error" });
@@ -7566,11 +7647,34 @@ function createApiServer(options) {
7566
7647
  });
7567
7648
  app.get("/api/graph/refresh", async (req, res) => {
7568
7649
  try {
7569
- const mode = req.query.mode === "folder" ? "folder" : "semantic";
7570
- const data = await buildGraphData(store, { mode });
7571
- const now = (/* @__PURE__ */ new Date()).toISOString();
7572
- graphCaches.set(mode, { data, generatedAt: now, cachedAt: Date.now() });
7573
- res.json({ data, generatedAt: now, mode });
7650
+ const view = parseView(req.query.view);
7651
+ const mode = parseMode(req.query.mode);
7652
+ const cap = clampCap(view, Number(req.query.cap));
7653
+ const cacheKey = `${view}:${mode}:${cap ?? ""}`;
7654
+ const entry = await buildGraphEntry(view, mode, cap);
7655
+ graphCaches.set(cacheKey, entry);
7656
+ res.json({ data: entry.data, generatedAt: entry.generatedAt, mode, view });
7657
+ } catch (err) {
7658
+ console.error(err);
7659
+ res.status(500).json({ error: "Internal server error" });
7660
+ }
7661
+ });
7662
+ app.get("/api/graph/cluster/:id", async (req, res) => {
7663
+ try {
7664
+ const mode = parseMode(req.query.mode);
7665
+ const cap = clampCap("cluster", Number(req.query.cap));
7666
+ const cacheKey = `cluster:${mode}:${cap ?? ""}`;
7667
+ let cached = graphCaches.get(cacheKey);
7668
+ if (!cached || !cached.clustered || Date.now() - cached.cachedAt > GRAPH_CACHE_TTL) {
7669
+ cached = await buildGraphEntry("cluster", mode, cap);
7670
+ graphCaches.set(cacheKey, cached);
7671
+ }
7672
+ const members = cached.clustered.members.get(Number(req.params.id));
7673
+ if (!members) {
7674
+ res.status(404).json({ error: "Cluster not found" });
7675
+ return;
7676
+ }
7677
+ res.json({ data: members });
7574
7678
  } catch (err) {
7575
7679
  console.error(err);
7576
7680
  res.status(500).json({ error: "Internal server error" });
@@ -11550,7 +11654,7 @@ if (nodeVersion < 20) {
11550
11654
  process.exit(1);
11551
11655
  }
11552
11656
  var program = new Command();
11553
- var SV_VERSION = true ? "0.8.5" : "0.0.0-dev";
11657
+ var SV_VERSION = true ? "0.8.6" : "0.0.0-dev";
11554
11658
  program.name("stellavault").description("Stellavault \u2014 Self-compiling knowledge base for your Obsidian vault").version(SV_VERSION).option("--json", "Output in JSON format (for scripting)").option("--quiet", "Suppress non-essential output");
11555
11659
  program.command("init").description("Interactive setup wizard \u2014 get started in 3 minutes").action(initCommand);
11556
11660
  program.command("doctor").description("Diagnose setup issues (config, vault, DB, model, Node version)").action(doctorCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stellavault",
3
- "version": "0.8.5",
3
+ "version": "0.8.6",
4
4
  "description": "Drop anything. It compiles itself into knowledge. Claude remembers everything you know. Local-first MCP server, vault files never modified.",
5
5
  "repository": {
6
6
  "type": "git",