@andespindola/brainlink 1.6.5 → 1.6.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.
@@ -71,6 +71,7 @@ const state = {
71
71
  workerReady: false,
72
72
  rendererMode: 'worker',
73
73
  renderWorker: null,
74
+ graphVersion: null,
74
75
  agentId: '',
75
76
  contextId: '',
76
77
  graphSignature: '',
@@ -0,0 +1,42 @@
1
+ export const createLiveRefreshJs = () => `
2
+ // Poll a cheap generation counter so the graph reflects vault changes live:
3
+ // when an agent writes notes via MCP, the watcher bumps the version and the
4
+ // client re-fetches the current viewport and refreshes the context list, so new
5
+ // nodes and contexts surface without a manual reload.
6
+ const liveRefreshIntervalMs = 3000
7
+
8
+ const pollGraphVersion = async () => {
9
+ try {
10
+ const response = await fetch('/api/graph-version', { headers: { accept: 'application/json' } })
11
+ if (!response.ok) {
12
+ return
13
+ }
14
+ const payload = await response.json()
15
+ const version = typeof payload?.version === 'number' ? payload.version : null
16
+ if (version === null) {
17
+ return
18
+ }
19
+ if (state.graphVersion === null) {
20
+ state.graphVersion = version
21
+ return
22
+ }
23
+ if (version !== state.graphVersion) {
24
+ state.graphVersion = version
25
+ await loadContexts().catch((error) => console.error(error))
26
+ scheduleChunkFetch()
27
+ }
28
+ } catch (error) {
29
+ // Transient poll failures (offline, redeploy) are non-fatal; the next tick retries.
30
+ }
31
+ }
32
+
33
+ const startLiveRefresh = () => {
34
+ if (typeof fetch !== 'function') {
35
+ return
36
+ }
37
+ pollGraphVersion().catch(() => {})
38
+ setInterval(() => {
39
+ pollGraphVersion().catch(() => {})
40
+ }, liveRefreshIntervalMs)
41
+ }
42
+ `;
@@ -211,6 +211,7 @@ const bootstrap = async () => {
211
211
  updateTotals()
212
212
 
213
213
  scheduleChunkFetch({ fit: true })
214
+ startLiveRefresh()
214
215
  }
215
216
 
216
217
  bootstrap().catch((error) => {
@@ -8,6 +8,7 @@ import { createChunkFetchJs } from './client/chunk-fetch.js';
8
8
  import { createInputJs } from './client/input.js';
9
9
  import { createUploadJs } from './client/upload.js';
10
10
  import { createControlsJs } from './client/controls.js';
11
+ import { createLiveRefreshJs } from './client/live-refresh.js';
11
12
  import { createGraphRendererJs } from './client/graph-renderer.js';
12
13
  import { createWorkerBootstrapJs } from './client/worker-bootstrap.js';
13
14
  export const createClientJs = () => [
@@ -21,6 +22,7 @@ export const createClientJs = () => [
21
22
  createInputJs(),
22
23
  createUploadJs(),
23
24
  createControlsJs(),
25
+ createLiveRefreshJs(),
24
26
  createGraphRendererJs(),
25
27
  createWorkerBootstrapJs()
26
28
  ].join('');
@@ -0,0 +1,11 @@
1
+ // Monotonic generation counter for the graph. The vault watcher bumps it after
2
+ // every reindex, and the web client polls /api/graph-version to know when to
3
+ // re-fetch — so nodes and contexts written by an agent (via MCP) surface live
4
+ // in an open graph without a manual reload. Process-local by design: a restart
5
+ // resets it, which the client simply treats as one more change.
6
+ let version = 0;
7
+ export const bumpGraphVersion = () => {
8
+ version += 1;
9
+ return version;
10
+ };
11
+ export const getGraphVersion = () => version;
@@ -24,6 +24,7 @@ import { createClientWorkerJs } from '../frontend/client-worker-js.js';
24
24
  import { createClientRenderWorkerJs } from '../frontend/client-render-worker-js.js';
25
25
  import { createLoginPageHtml, createChangePasswordPageHtml } from '../frontend/login-page.js';
26
26
  import { buildClearSessionCookie, buildSessionSetCookie, changeWebPassword, createSessionToken, parseCookies, resolveWebAuth, SESSION_COOKIE_NAME, verifySessionToken, verifyWebPassword } from './web-auth.js';
27
+ import { getGraphVersion } from './graph-version.js';
27
28
  import { contentTypes, createJsonResponse, isReadMethod, parsePositiveInteger } from './http.js';
28
29
  import { parseMultipartForm } from './multipart.js';
29
30
  const readRuntimeDefaults = async (url) => {
@@ -519,6 +520,11 @@ export const route = async (request, url, vaultPath, options = {}) => {
519
520
  if (isReadMethod(request) && url.pathname === '/api/agents') {
520
521
  return createResponse(createJsonResponse({ agents: await listAgents(vaultPath) }), 200, contentTypes['.json']);
521
522
  }
523
+ // Cheap generation counter the client polls to auto-refresh when the vault
524
+ // changes (new nodes/contexts written via MCP surface live).
525
+ if (isReadMethod(request) && url.pathname === '/api/graph-version') {
526
+ return createResponse(createJsonResponse({ version: getGraphVersion() }), 200, contentTypes['.json']);
527
+ }
522
528
  if (isReadMethod(request) && url.pathname === '/api/graph-contexts') {
523
529
  return createResponse(createJsonResponse({ contexts: await getGraphContexts(vaultPath, readAgentQuery(url)) }), 200, contentTypes['.json']);
524
530
  }
@@ -2,6 +2,7 @@ import { createServer } from 'node:http';
2
2
  import { brotliCompressSync, constants, gzipSync } from 'node:zlib';
3
3
  import { indexVault } from './index-vault.js';
4
4
  import { startVaultWatcher } from './watch-vault.js';
5
+ import { bumpGraphVersion } from './server/graph-version.js';
5
6
  import { assertLoopbackHost } from './server/host-security.js';
6
7
  import { contentTypes, createJsonResponse, isHttpError } from './server/http.js';
7
8
  import { route } from './server/routes.js';
@@ -82,10 +83,16 @@ export const startServer = async (input) => {
82
83
  }
83
84
  if (input.shouldIndex) {
84
85
  await indexVault(input.vaultPath);
86
+ bumpGraphVersion();
85
87
  }
86
88
  const watcher = input.shouldWatch
87
89
  ? startVaultWatcher({
88
90
  vaultPath: input.vaultPath,
91
+ // Signal the web client (via /api/graph-version) that the graph changed
92
+ // so it can re-fetch and surface new nodes/contexts live.
93
+ onIndex: () => {
94
+ bumpGraphVersion();
95
+ },
89
96
  onError: (error) => console.error(error)
90
97
  })
91
98
  : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "1.6.5",
3
+ "version": "1.6.6",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",