nano-brain 2026.8.4 → 2026.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.
@@ -33,7 +33,7 @@ services:
33
33
  - nano-brain-node-modules:/app/node_modules
34
34
  - ${NANO_BRAIN_HOME:-~/.nano-brain}:/root/.nano-brain
35
35
  - ${NANO_BRAIN_WORKSPACE:-/Users/tamlh/workspaces/NUSTechnology/Projects/zengamingx}:${NANO_BRAIN_WORKSPACE:-/Users/tamlh/workspaces/NUSTechnology/Projects/zengamingx}:ro
36
- - ${OPENCODE_DATA_DIR:-~/.ai-sandbox/tools/opencode/home/.local/share/opencode}:/root/.local/share/opencode:ro
36
+ - ${OPENCODE_DATA_DIR:-~/.local/share/opencode}:/root/.local/share/opencode:ro
37
37
  environment:
38
38
  - NODE_ENV=production
39
39
  - OLLAMA_HOST=http://host.docker.internal:11434
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nano-brain",
3
- "version": "2026.8.4",
3
+ "version": "2026.8.6",
4
4
  "description": "Persistent memory and code intelligence for AI coding agents. Local MCP server with self-learning hybrid search (BM25 + vector + knowledge graph + LLM reranking), automatic session ingestion, codebase indexing, and 22 tools. Learns your preferences over time. Works with OpenCode, Claude, Cursor, Windsurf, and any MCP client.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,10 +26,25 @@ function formatBytes(bytes: number): string {
26
26
  return `${mb.toFixed(1)} MB`;
27
27
  }
28
28
 
29
- async function getVectorStoreHealth(config: ReturnType<typeof loadCollectionConfig>): Promise<VectorStoreHealth | null> {
29
+ async function getVectorStoreHealth(
30
+ config: ReturnType<typeof loadCollectionConfig>,
31
+ serverRunning: boolean,
32
+ port: number
33
+ ): Promise<VectorStoreHealth | null> {
30
34
  const vectorConfig = config?.vector;
31
35
  if (!vectorConfig || vectorConfig.provider !== 'qdrant') return null;
32
36
 
37
+ // If server is running, proxy the request through it
38
+ if (serverRunning) {
39
+ try {
40
+ const health = await proxyGet(port, '/api/vector-health');
41
+ return health as VectorStoreHealth;
42
+ } catch (err) {
43
+ log('cli', 'Vector health proxy failed: ' + (err instanceof Error ? err.message : String(err)));
44
+ // Fall through to direct check
45
+ }
46
+ }
47
+
33
48
  try {
34
49
  const vectorStore = createVectorStore(vectorConfig);
35
50
  const health = await Promise.race([
@@ -110,11 +125,18 @@ async function printEmbeddingServerStatus(config: ReturnType<typeof loadCollecti
110
125
  export async function handleStatus(globalOpts: GlobalOptions, commandArgs: string[]): Promise<void> {
111
126
  log('cli', 'status command invoked');
112
127
  const serverRunning = await detectRunningServer(DEFAULT_HTTP_PORT);
113
- let serverInfo: { uptime: number; ready: boolean } | null = null;
128
+ let serverInfo: { uptime: number; ready: boolean; index?: { documentCount: number; embeddedCount: number; pendingEmbeddings: number } } | null = null;
114
129
  if (serverRunning) {
115
130
  try {
116
- const data = await proxyGet(DEFAULT_HTTP_PORT, '/api/status') as { uptime?: number; ready?: boolean };
131
+ const data = await proxyGet(DEFAULT_HTTP_PORT, '/api/status') as { uptime?: number; ready?: boolean; index?: any };
117
132
  serverInfo = { uptime: data.uptime ?? 0, ready: data.ready ?? false };
133
+ if (data.index) {
134
+ serverInfo.index = {
135
+ documentCount: data.index.documentCount ?? 0,
136
+ embeddedCount: data.index.embeddedCount ?? 0,
137
+ pendingEmbeddings: data.index.pendingEmbeddings ?? 0
138
+ };
139
+ }
118
140
  } catch (err) {
119
141
  log('cli', 'HTTP proxy failed for server info: ' + (err instanceof Error ? err.message : String(err)));
120
142
  }
@@ -207,7 +229,7 @@ export async function handleStatus(globalOpts: GlobalOptions, commandArgs: strin
207
229
  await printEmbeddingServerStatus(config);
208
230
  cliOutput('');
209
231
 
210
- const vectorHealth = await getVectorStoreHealth(config);
232
+ const vectorHealth = await getVectorStoreHealth(config, serverRunning, DEFAULT_HTTP_PORT);
211
233
  printVectorStoreSection(vectorHealth);
212
234
 
213
235
  const allTokenUsage = new Map<string, { totalTokens: number; requestCount: number; lastUpdated: string }>();
@@ -272,9 +294,10 @@ export async function handleStatus(globalOpts: GlobalOptions, commandArgs: strin
272
294
  cliOutput('');
273
295
 
274
296
  cliOutput('Index:');
275
- cliOutput(` Documents: ${health.documentCount.toLocaleString()}`);
276
- cliOutput(` Embedded: ${health.embeddedCount.toLocaleString()}`);
277
- cliOutput(` Pending embeddings: ${health.pendingEmbeddings.toLocaleString()}`);
297
+ const indexData = serverInfo?.index ?? health;
298
+ cliOutput(` Documents: ${indexData.documentCount.toLocaleString()}`);
299
+ cliOutput(` Embedded: ${indexData.embeddedCount.toLocaleString()}`);
300
+ cliOutput(` Pending embeddings: ${indexData.pendingEmbeddings.toLocaleString()}`);
278
301
  cliOutput('');
279
302
 
280
303
  if (health.collections.length > 0) {
@@ -319,7 +342,7 @@ export async function handleStatus(globalOpts: GlobalOptions, commandArgs: strin
319
342
  await printEmbeddingServerStatus(config);
320
343
  cliOutput('');
321
344
 
322
- const vectorHealth = await getVectorStoreHealth(config);
345
+ const vectorHealth = await getVectorStoreHealth(config, serverRunning, DEFAULT_HTTP_PORT);
323
346
  printVectorStoreSection(vectorHealth, vectorHealth ? undefined : store.getSqliteVecCount());
324
347
 
325
348
  printTokenUsageSection(store.getTokenUsage());
@@ -140,6 +140,27 @@ export async function handleRequest(
140
140
  return;
141
141
  }
142
142
 
143
+ if (req.method === 'GET' && pathname === '/api/vector-health') {
144
+ const vectorStore = store.getVectorStore();
145
+ if (!vectorStore) {
146
+ res.writeHead(200, { 'Content-Type': 'application/json' });
147
+ res.end(JSON.stringify({ provider: 'sqlite-vec', ok: true, vectorCount: store.getSqliteVecCount() }));
148
+ return;
149
+ }
150
+ try {
151
+ const health = await Promise.race([
152
+ vectorStore.health(),
153
+ new Promise<never>((_, reject) => setTimeout(() => reject(new Error('timeout')), 5000))
154
+ ]);
155
+ res.writeHead(200, { 'Content-Type': 'application/json' });
156
+ res.end(JSON.stringify(health));
157
+ } catch (err) {
158
+ res.writeHead(200, { 'Content-Type': 'application/json' });
159
+ res.end(JSON.stringify({ ok: false, provider: 'qdrant', vectorCount: 0, error: err instanceof Error ? err.message : String(err) }));
160
+ }
161
+ return;
162
+ }
163
+
143
164
  if (req.method === 'POST' && pathname === '/api/query') {
144
165
  const body = await readBody(req);
145
166
  try {