@andespindola/brainlink 0.1.0-beta.35 → 0.1.0-beta.37
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/CHANGELOG.md +3 -0
- package/COPYRIGHT.md +1 -1
- package/README.md +3 -1
- package/dist/application/frontend/client-html.js +2 -2
- package/dist/application/frontend/client-js.js +41 -2
- package/dist/cli/commands/write-commands.js +111 -26
- package/docs/AGENT_USAGE.md +2 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
- Improved graph UI auto-fit and viewport recovery so loaded nodes are re-centered when zoom/pan drifts to empty canvas.
|
|
28
28
|
- Added cross-platform native desktop GUI auto-open for `blink server` (macOS Swift/WebKit, Windows PowerShell WinForms, Linux Python GTK/WebKit2), with app-window/browser fallback.
|
|
29
29
|
- Changed Linux default UI launch to app-window/browser for lighter startup; Linux native GUI is now opt-in via `BRAINLINK_LINUX_NATIVE_GUI=1`.
|
|
30
|
+
- Added native GUI parent-process monitoring so GUI windows close automatically when `blink server` stops.
|
|
31
|
+
- Improved non-mac browser detection fallback to try installed Edge/Chrome/Firefox/Chromium candidates before system default open.
|
|
32
|
+
- Improved graph filter rendering to keep hub anchor nodes visible (`Memory Hub`/`MOC`/high-degree fallback) for coherent relationship context.
|
|
30
33
|
|
|
31
34
|
## 0.1.0-beta.3
|
|
32
35
|
|
package/COPYRIGHT.md
CHANGED
package/README.md
CHANGED
|
@@ -561,6 +561,7 @@ By default, `blink server` tries to open the graph in a native desktop GUI windo
|
|
|
561
561
|
On Linux, native GUI is disabled by default for better startup performance. Enable it with `BRAINLINK_LINUX_NATIVE_GUI=1`.
|
|
562
562
|
If native GUI launch is unavailable on your system, it falls back to dedicated app-window mode and then to the default browser.
|
|
563
563
|
Use `--no-open` to keep it headless.
|
|
564
|
+
When native GUI is used, the GUI window automatically closes when the `blink server` process stops.
|
|
564
565
|
|
|
565
566
|
The graph UI shows:
|
|
566
567
|
|
|
@@ -570,6 +571,7 @@ The graph UI shows:
|
|
|
570
571
|
- neutral graph nodes with segment/group metadata
|
|
571
572
|
- agent selector (id-only labels) for isolated views
|
|
572
573
|
- graph filter matches title, path, tags and note content
|
|
574
|
+
- graph filter keeps hub context nodes visible (`Memory Hub`/`MOC`/high-degree fallback) to preserve relationship readability
|
|
573
575
|
- realtime refresh while `--watch` is enabled
|
|
574
576
|
- graph controls for zoom in, zoom out, fit visible nodes and reset-to-fit-all
|
|
575
577
|
- wheel zoom (including `cmd+scroll` and `ctrl+scroll`) anchored to cursor position for faster navigation in large graphs
|
|
@@ -1054,7 +1056,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
1054
1056
|
## License
|
|
1055
1057
|
|
|
1056
1058
|
MIT. See [LICENSE](LICENSE).
|
|
1057
|
-
Copyright (c) 2026
|
|
1059
|
+
Copyright (c) 2026 Substructa. See [COPYRIGHT.md](COPYRIGHT.md).
|
|
1058
1060
|
|
|
1059
1061
|
### Memory Optimization Loop (1-7)
|
|
1060
1062
|
|
|
@@ -46,8 +46,8 @@ export const createClientHtml = () => `<!doctype html>
|
|
|
46
46
|
<canvas id="graph" aria-label="Brainlink knowledge graph"></canvas>
|
|
47
47
|
</section>
|
|
48
48
|
</main>
|
|
49
|
-
<footer class="app-footer" aria-label="
|
|
50
|
-
<small>
|
|
49
|
+
<footer class="app-footer" aria-label="Copyright notice">
|
|
50
|
+
<small>Copyright © 2026 Substructa</small>
|
|
51
51
|
</footer>
|
|
52
52
|
<dialog id="contentDialog" class="content-dialog" aria-labelledby="contentTitle">
|
|
53
53
|
<article>
|
|
@@ -99,6 +99,8 @@ const resize = () => {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const normalizeQuery = value => value.trim().toLowerCase()
|
|
102
|
+
const hubNodeRetentionLimit = 2
|
|
103
|
+
const hubNodePattern = /\b(memory\s*hub|knowledge\s*hub|hub|moc|map|memory\s*map|mapa)\b/i
|
|
102
104
|
|
|
103
105
|
const localFilteredNodes = query =>
|
|
104
106
|
state.nodes.filter(node =>
|
|
@@ -107,14 +109,51 @@ const localFilteredNodes = query =>
|
|
|
107
109
|
node.tags.some(tag => tag.toLowerCase().includes(query))
|
|
108
110
|
)
|
|
109
111
|
|
|
112
|
+
const rankedHubNodes = () => {
|
|
113
|
+
if (state.nodes.length === 0) {
|
|
114
|
+
return []
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const byTitleAndDegree = [...state.nodes]
|
|
118
|
+
.filter(node => hubNodePattern.test(node.title) || hubNodePattern.test(node.path) || node.tags.some(tag => hubNodePattern.test(tag)))
|
|
119
|
+
.sort((left, right) => {
|
|
120
|
+
const byDegree = (state.nodeDegrees.get(right.id) ?? 0) - (state.nodeDegrees.get(left.id) ?? 0)
|
|
121
|
+
if (byDegree !== 0) return byDegree
|
|
122
|
+
return left.title.localeCompare(right.title)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (byTitleAndDegree.length > 0) {
|
|
126
|
+
return byTitleAndDegree.slice(0, hubNodeRetentionLimit)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return [...state.nodes]
|
|
130
|
+
.sort((left, right) => {
|
|
131
|
+
const byDegree = (state.nodeDegrees.get(right.id) ?? 0) - (state.nodeDegrees.get(left.id) ?? 0)
|
|
132
|
+
if (byDegree !== 0) return byDegree
|
|
133
|
+
return left.title.localeCompare(right.title)
|
|
134
|
+
})
|
|
135
|
+
.slice(0, 1)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const withPersistentHubNodes = nodes => {
|
|
139
|
+
if (nodes.length === 0) {
|
|
140
|
+
return rankedHubNodes()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const ids = new Set(nodes.map(node => node.id))
|
|
144
|
+
const hubsToKeep = rankedHubNodes().filter(node => !ids.has(node.id))
|
|
145
|
+
return nodes.concat(hubsToKeep)
|
|
146
|
+
}
|
|
147
|
+
|
|
110
148
|
const filteredNodes = () => {
|
|
111
149
|
const query = normalizeQuery(state.query)
|
|
112
150
|
if (!query) return state.nodes
|
|
113
151
|
if (state.contentFilter.query === query && state.contentFilter.ids instanceof Set) {
|
|
114
|
-
|
|
152
|
+
const matched = state.nodes.filter(node => state.contentFilter.ids.has(node.id))
|
|
153
|
+
return withPersistentHubNodes(matched)
|
|
115
154
|
}
|
|
116
155
|
|
|
117
|
-
return localFilteredNodes(query)
|
|
156
|
+
return withPersistentHubNodes(localFilteredNodes(query))
|
|
118
157
|
}
|
|
119
158
|
|
|
120
159
|
const recomputeVisibility = () => {
|
|
@@ -42,14 +42,18 @@ const nativeGuiLinuxScriptPath = join(tmpdir(), 'brainlink-native-gui-linux.py')
|
|
|
42
42
|
const nativeGuiSwiftScript = `import Foundation
|
|
43
43
|
import AppKit
|
|
44
44
|
import WebKit
|
|
45
|
+
import Darwin
|
|
45
46
|
|
|
46
47
|
final class BrainlinkAppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
|
47
48
|
private let targetUrl: URL
|
|
49
|
+
private let parentPid: Int32
|
|
48
50
|
private var window: NSWindow?
|
|
49
51
|
private var webView: WKWebView?
|
|
52
|
+
private var monitorTimer: Timer?
|
|
50
53
|
|
|
51
|
-
init(targetUrl: URL) {
|
|
54
|
+
init(targetUrl: URL, parentPid: Int32) {
|
|
52
55
|
self.targetUrl = targetUrl
|
|
56
|
+
self.parentPid = parentPid
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
@@ -73,16 +77,27 @@ final class BrainlinkAppDelegate: NSObject, NSApplicationDelegate, NSWindowDeleg
|
|
|
73
77
|
self.window = window
|
|
74
78
|
self.webView = webView
|
|
75
79
|
|
|
80
|
+
if parentPid > 0 {
|
|
81
|
+
monitorTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
|
|
82
|
+
if kill(self.parentPid, 0) != 0 {
|
|
83
|
+
NSApp.terminate(nil)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
76
88
|
window.makeKeyAndOrderFront(nil)
|
|
77
89
|
NSApp.activate(ignoringOtherApps: true)
|
|
78
90
|
}
|
|
79
91
|
|
|
80
92
|
func windowWillClose(_ notification: Notification) {
|
|
93
|
+
monitorTimer?.invalidate()
|
|
81
94
|
NSApp.terminate(nil)
|
|
82
95
|
}
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
let
|
|
98
|
+
let args = Array(CommandLine.arguments.dropFirst())
|
|
99
|
+
let rawTarget = args.indices.contains(0) ? args[0] : "http://127.0.0.1:4321"
|
|
100
|
+
let parentPid: Int32 = args.indices.contains(1) ? (Int32(args[1]) ?? 0) : 0
|
|
86
101
|
|
|
87
102
|
guard let targetUrl = URL(string: rawTarget) else {
|
|
88
103
|
fputs("Invalid URL for Brainlink GUI: \\(rawTarget)\\n", stderr)
|
|
@@ -91,12 +106,13 @@ guard let targetUrl = URL(string: rawTarget) else {
|
|
|
91
106
|
|
|
92
107
|
let app = NSApplication.shared
|
|
93
108
|
app.setActivationPolicy(.regular)
|
|
94
|
-
let delegate = BrainlinkAppDelegate(targetUrl: targetUrl)
|
|
109
|
+
let delegate = BrainlinkAppDelegate(targetUrl: targetUrl, parentPid: parentPid)
|
|
95
110
|
app.delegate = delegate
|
|
96
111
|
app.run()
|
|
97
112
|
`;
|
|
98
113
|
const nativeGuiPowershellScript = `param(
|
|
99
|
-
[string]$TargetUrl = "http://127.0.0.1:4321"
|
|
114
|
+
[string]$TargetUrl = "http://127.0.0.1:4321",
|
|
115
|
+
[int]$ParentPid = 0
|
|
100
116
|
)
|
|
101
117
|
|
|
102
118
|
Add-Type -AssemblyName System.Windows.Forms
|
|
@@ -115,6 +131,23 @@ $browser.ScriptErrorsSuppressed = $true
|
|
|
115
131
|
$browser.Navigate($TargetUrl)
|
|
116
132
|
|
|
117
133
|
$form.Controls.Add($browser)
|
|
134
|
+
$timer = New-Object System.Windows.Forms.Timer
|
|
135
|
+
$timer.Interval = 1000
|
|
136
|
+
$timer.Add_Tick({
|
|
137
|
+
if ($ParentPid -le 0) {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
Get-Process -Id $ParentPid -ErrorAction Stop | Out-Null
|
|
142
|
+
} catch {
|
|
143
|
+
$timer.Stop()
|
|
144
|
+
$form.Close()
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
$form.Add_FormClosed({
|
|
148
|
+
$timer.Stop()
|
|
149
|
+
})
|
|
150
|
+
$timer.Start()
|
|
118
151
|
[void]$form.ShowDialog()
|
|
119
152
|
`;
|
|
120
153
|
const nativeGuiLinuxPythonScript = `#!/usr/bin/env python3
|
|
@@ -128,11 +161,12 @@ def run() -> int:
|
|
|
128
161
|
gi.require_version("WebKit2", "4.1")
|
|
129
162
|
except ValueError:
|
|
130
163
|
gi.require_version("WebKit2", "4.0")
|
|
131
|
-
from gi.repository import Gtk, WebKit2
|
|
164
|
+
from gi.repository import Gtk, WebKit2, GLib
|
|
132
165
|
except Exception:
|
|
133
166
|
return 1
|
|
134
167
|
|
|
135
168
|
target_url = sys.argv[1] if len(sys.argv) > 1 else "http://127.0.0.1:4321"
|
|
169
|
+
parent_pid = int(sys.argv[2]) if len(sys.argv) > 2 else 0
|
|
136
170
|
|
|
137
171
|
window = Gtk.Window(title="Brainlink Graph")
|
|
138
172
|
window.set_default_size(1320, 860)
|
|
@@ -142,6 +176,19 @@ def run() -> int:
|
|
|
142
176
|
webview.load_uri(target_url)
|
|
143
177
|
window.add(webview)
|
|
144
178
|
window.show_all()
|
|
179
|
+
|
|
180
|
+
if parent_pid > 0:
|
|
181
|
+
def _watch_parent() -> bool:
|
|
182
|
+
try:
|
|
183
|
+
import os
|
|
184
|
+
os.kill(parent_pid, 0)
|
|
185
|
+
except Exception:
|
|
186
|
+
Gtk.main_quit()
|
|
187
|
+
return False
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
GLib.timeout_add(1000, _watch_parent)
|
|
191
|
+
|
|
145
192
|
Gtk.main()
|
|
146
193
|
return 0
|
|
147
194
|
|
|
@@ -160,6 +207,10 @@ const commandExists = (command) => {
|
|
|
160
207
|
}
|
|
161
208
|
};
|
|
162
209
|
const envFlagEnabled = (name) => process.env[name] === '1' || process.env[name] === 'true';
|
|
210
|
+
const spawnAnyDetached = (candidates) => candidates.some(([command, args]) => spawnDetached(command, args));
|
|
211
|
+
const windowsStartCandidates = (program, args = []) => [
|
|
212
|
+
['cmd', ['/c', 'start', '', program, ...args]]
|
|
213
|
+
];
|
|
163
214
|
const resolveSwiftExecutable = () => {
|
|
164
215
|
const directSwift = '/usr/bin/swift';
|
|
165
216
|
if (existsSync(directSwift)) {
|
|
@@ -174,7 +225,7 @@ const resolveSwiftExecutable = () => {
|
|
|
174
225
|
return null;
|
|
175
226
|
}
|
|
176
227
|
};
|
|
177
|
-
const openGraphInMacNativeGui = (url) => {
|
|
228
|
+
const openGraphInMacNativeGui = (url, parentPid) => {
|
|
178
229
|
const swiftBinary = resolveSwiftExecutable();
|
|
179
230
|
if (!swiftBinary) {
|
|
180
231
|
return false;
|
|
@@ -185,7 +236,7 @@ const openGraphInMacNativeGui = (url) => {
|
|
|
185
236
|
catch {
|
|
186
237
|
return false;
|
|
187
238
|
}
|
|
188
|
-
return spawnDetached(swiftBinary, [nativeGuiSwiftScriptPath, url]);
|
|
239
|
+
return spawnDetached(swiftBinary, [nativeGuiSwiftScriptPath, url, String(parentPid)]);
|
|
189
240
|
};
|
|
190
241
|
const resolveWindowsPowershellExecutable = () => {
|
|
191
242
|
if (commandExists('powershell')) {
|
|
@@ -196,7 +247,7 @@ const resolveWindowsPowershellExecutable = () => {
|
|
|
196
247
|
}
|
|
197
248
|
return null;
|
|
198
249
|
};
|
|
199
|
-
const openGraphInWindowsNativeGui = (url) => {
|
|
250
|
+
const openGraphInWindowsNativeGui = (url, parentPid) => {
|
|
200
251
|
const powershell = resolveWindowsPowershellExecutable();
|
|
201
252
|
if (!powershell) {
|
|
202
253
|
return false;
|
|
@@ -207,9 +258,9 @@ const openGraphInWindowsNativeGui = (url) => {
|
|
|
207
258
|
catch {
|
|
208
259
|
return false;
|
|
209
260
|
}
|
|
210
|
-
return spawnDetached(powershell, ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-STA', '-File', nativeGuiPowershellScriptPath, url]);
|
|
261
|
+
return spawnDetached(powershell, ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-STA', '-File', nativeGuiPowershellScriptPath, url, String(parentPid)]);
|
|
211
262
|
};
|
|
212
|
-
const openGraphInLinuxNativeGui = (url) => {
|
|
263
|
+
const openGraphInLinuxNativeGui = (url, parentPid) => {
|
|
213
264
|
if (!commandExists('python3')) {
|
|
214
265
|
return false;
|
|
215
266
|
}
|
|
@@ -219,16 +270,16 @@ const openGraphInLinuxNativeGui = (url) => {
|
|
|
219
270
|
catch {
|
|
220
271
|
return false;
|
|
221
272
|
}
|
|
222
|
-
return spawnDetached('python3', [nativeGuiLinuxScriptPath, url]);
|
|
273
|
+
return spawnDetached('python3', [nativeGuiLinuxScriptPath, url, String(parentPid)]);
|
|
223
274
|
};
|
|
224
|
-
const openGraphInNativeGui = (url) => {
|
|
275
|
+
const openGraphInNativeGui = (url, parentPid) => {
|
|
225
276
|
if (platform() === 'darwin') {
|
|
226
|
-
return openGraphInMacNativeGui(url);
|
|
277
|
+
return openGraphInMacNativeGui(url, parentPid);
|
|
227
278
|
}
|
|
228
279
|
if (platform() === 'win32') {
|
|
229
|
-
return openGraphInWindowsNativeGui(url);
|
|
280
|
+
return openGraphInWindowsNativeGui(url, parentPid);
|
|
230
281
|
}
|
|
231
|
-
return openGraphInLinuxNativeGui(url);
|
|
282
|
+
return openGraphInLinuxNativeGui(url, parentPid);
|
|
232
283
|
};
|
|
233
284
|
const openGraphInAppWindow = (url) => {
|
|
234
285
|
if (platform() === 'darwin') {
|
|
@@ -248,18 +299,49 @@ const openGraphInAppWindow = (url) => {
|
|
|
248
299
|
}
|
|
249
300
|
if (platform() === 'win32') {
|
|
250
301
|
const appArgument = `--app=${url}`;
|
|
251
|
-
return (
|
|
252
|
-
|
|
253
|
-
|
|
302
|
+
return spawnAnyDetached([
|
|
303
|
+
...windowsStartCandidates('msedge', [appArgument, '--new-window']),
|
|
304
|
+
...windowsStartCandidates('chrome', [appArgument, '--new-window']),
|
|
305
|
+
...windowsStartCandidates('chromium', [appArgument, '--new-window']),
|
|
306
|
+
...windowsStartCandidates('brave', [appArgument, '--new-window'])
|
|
307
|
+
]);
|
|
254
308
|
}
|
|
255
309
|
const appArgument = `--app=${url}`;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
310
|
+
const linuxAppWindowCandidates = [
|
|
311
|
+
'microsoft-edge',
|
|
312
|
+
'microsoft-edge-stable',
|
|
313
|
+
'google-chrome',
|
|
314
|
+
'google-chrome-stable',
|
|
315
|
+
'chromium',
|
|
316
|
+
'chromium-browser',
|
|
317
|
+
'brave-browser'
|
|
318
|
+
].filter((candidate) => commandExists(candidate));
|
|
319
|
+
return spawnAnyDetached(linuxAppWindowCandidates.map((command) => [command, [appArgument, '--new-window']]));
|
|
320
|
+
};
|
|
321
|
+
const openGraphInDetectedBrowser = (url) => {
|
|
322
|
+
if (platform() === 'win32') {
|
|
323
|
+
return spawnAnyDetached([
|
|
324
|
+
...windowsStartCandidates('msedge', [url]),
|
|
325
|
+
...windowsStartCandidates('chrome', [url]),
|
|
326
|
+
...windowsStartCandidates('firefox', ['-new-window', url]),
|
|
327
|
+
...windowsStartCandidates('chromium', [url]),
|
|
328
|
+
...windowsStartCandidates('brave', [url])
|
|
329
|
+
]);
|
|
330
|
+
}
|
|
331
|
+
const linuxBrowserCandidates = [
|
|
332
|
+
['microsoft-edge', [url]],
|
|
333
|
+
['microsoft-edge-stable', [url]],
|
|
334
|
+
['google-chrome', [url]],
|
|
335
|
+
['google-chrome-stable', [url]],
|
|
336
|
+
['chromium', [url]],
|
|
337
|
+
['chromium-browser', [url]],
|
|
338
|
+
['brave-browser', [url]],
|
|
339
|
+
['firefox', ['-new-window', url]]
|
|
340
|
+
];
|
|
341
|
+
const available = linuxBrowserCandidates.filter(([command]) => commandExists(command));
|
|
342
|
+
return spawnAnyDetached(available);
|
|
261
343
|
};
|
|
262
|
-
const openUrlInUi = (url) => {
|
|
344
|
+
const openUrlInUi = (url, parentPid) => {
|
|
263
345
|
const openDisabled = process.env.BRAINLINK_NO_BROWSER === '1' ||
|
|
264
346
|
process.env.BRAINLINK_NO_BROWSER === 'true' ||
|
|
265
347
|
process.env.CI === 'true';
|
|
@@ -269,7 +351,7 @@ const openUrlInUi = (url) => {
|
|
|
269
351
|
const currentPlatform = platform();
|
|
270
352
|
const nativeGuiEnabled = !envFlagEnabled('BRAINLINK_NO_NATIVE_GUI') &&
|
|
271
353
|
(currentPlatform !== 'linux' || envFlagEnabled('BRAINLINK_LINUX_NATIVE_GUI') || envFlagEnabled('BRAINLINK_FORCE_NATIVE_GUI'));
|
|
272
|
-
if (nativeGuiEnabled && openGraphInNativeGui(url)) {
|
|
354
|
+
if (nativeGuiEnabled && openGraphInNativeGui(url, parentPid)) {
|
|
273
355
|
return { opened: true, mode: 'native-gui' };
|
|
274
356
|
}
|
|
275
357
|
if (openGraphInAppWindow(url)) {
|
|
@@ -279,6 +361,9 @@ const openUrlInUi = (url) => {
|
|
|
279
361
|
if (platform() === 'darwin') {
|
|
280
362
|
return { opened: spawnDetached('open', [url]), mode: 'browser' };
|
|
281
363
|
}
|
|
364
|
+
if (openGraphInDetectedBrowser(url)) {
|
|
365
|
+
return { opened: true, mode: 'browser' };
|
|
366
|
+
}
|
|
282
367
|
if (platform() === 'win32') {
|
|
283
368
|
return { opened: spawnDetached('cmd', ['/c', 'start', '', url]), mode: 'browser' };
|
|
284
369
|
}
|
|
@@ -490,7 +575,7 @@ export const registerWriteCommands = (program) => {
|
|
|
490
575
|
shouldIndex: options.index,
|
|
491
576
|
shouldWatch: Boolean(options.watch)
|
|
492
577
|
});
|
|
493
|
-
const openResult = options.open !== false ? openUrlInUi(server.url) : { opened: false, mode: 'none' };
|
|
578
|
+
const openResult = options.open !== false ? openUrlInUi(server.url, process.pid) : { opened: false, mode: 'none' };
|
|
494
579
|
print(options.json, {
|
|
495
580
|
url: server.url,
|
|
496
581
|
watch: Boolean(options.watch),
|
package/docs/AGENT_USAGE.md
CHANGED
|
@@ -548,12 +548,14 @@ By default it tries to open the graph in a native desktop GUI window:
|
|
|
548
548
|
On Linux, native GUI is disabled by default for better startup performance. Enable it with `BRAINLINK_LINUX_NATIVE_GUI=1`.
|
|
549
549
|
If native GUI launch is unavailable, it falls back to dedicated app-window mode and then to the default browser.
|
|
550
550
|
Use `--no-open` to keep the server headless.
|
|
551
|
+
When native GUI is active, the GUI window closes automatically when the `blink server` process stops.
|
|
551
552
|
|
|
552
553
|
Without `--vault`, the graph UI serves `$HOME/.brainlink/vault`.
|
|
553
554
|
|
|
554
555
|
The frontend includes an agent selector that shows only the agent id. Selecting an agent calls the same read APIs with `agent=<agent-id>` and renders that namespace instead of merging every agent into one graph.
|
|
555
556
|
|
|
556
557
|
Graph navigation controls include zoom in, zoom out, fit visible nodes and reset-to-fit-all nodes. Mouse wheel zoom (including `cmd+scroll` and `ctrl+scroll`) is anchored to the cursor. Keyboard shortcuts are `+` (zoom in), `-` (zoom out) and `0` (reset fit). Double-click on canvas zooms in at cursor position. Totals for notes, links and tags stay visible as floating metrics under the Brainlink title, and node details open on click in a modal (tags, outgoing links, backlinks and Markdown content).
|
|
558
|
+
During graph filtering, Brainlink keeps hub context nodes visible (`Memory Hub`/`MOC`/high-degree fallback) so filtered views still show relationship anchors.
|
|
557
559
|
|
|
558
560
|
The command reindexes by default, then serves:
|
|
559
561
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andespindola/brainlink",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.37",
|
|
4
4
|
"description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"author": "
|
|
7
|
+
"author": "Substructa",
|
|
8
8
|
"homepage": "https://github.com/andersonflima/brainlink#readme",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|