wicked-brain 0.7.2 → 0.8.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain",
3
- "version": "0.7.2",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "description": "Digital brain as skills for AI coding CLIs — no vector DB, no embeddings, no infrastructure",
6
6
  "keywords": [
@@ -16,28 +16,52 @@ function getArg(name) {
16
16
  return idx !== -1 && args[idx + 1] ? args[idx + 1] : null;
17
17
  }
18
18
 
19
+ // --version / -v: print root package.json version and exit.
20
+ // Resolves relative to this file so the published install reads its own manifest.
21
+ if (args.includes("--version") || args.includes("-v")) {
22
+ const pkgUrl = new URL("../../package.json", import.meta.url);
23
+ const pkg = JSON.parse(readFileSync(pkgUrl, "utf-8"));
24
+ console.log(pkg.version);
25
+ exit(0);
26
+ }
27
+
19
28
  const brainPath = resolve(getArg("brain") || ".");
20
29
  const preferredPort = parseInt(getArg("port") || "4242", 10);
30
+ // Explicit --port means "bind this exact port or fail" — no probe.
31
+ const portExplicit = args.includes("--port");
21
32
  const configPath = join(brainPath, "brain.json");
22
33
  // Source path for LSP workspace root — prefer --source flag, fall back to config, then brainPath
23
34
  const sourceArgRaw = getArg("source");
24
35
  const sourceArg = sourceArgRaw ? resolve(sourceArgRaw) : null;
25
36
 
26
- /** Find a free TCP port starting from `start`. */
27
- function findFreePort(start) {
37
+ /**
38
+ * Listen on `startPort`, probing upward on EADDRINUSE. Probes using the real
39
+ * server instance so the bind semantics (dual-stack IPv4+IPv6) match the
40
+ * eventual listener — a separate 127.0.0.1 probe would miss an IPv6-only
41
+ * conflict and produce a false "free" result.
42
+ */
43
+ function listenWithProbe(server, startPort, maxProbe) {
28
44
  return new Promise((resolve, reject) => {
29
- const tryPort = (p) => {
30
- const probe = createServer();
31
- probe.once("error", (err) => {
32
- if (err.code === "EADDRINUSE") tryPort(p + 1);
33
- else reject(err);
34
- });
35
- probe.once("listening", () => {
36
- probe.close(() => resolve(p));
37
- });
38
- probe.listen(p, "127.0.0.1");
45
+ let p = startPort;
46
+ const attempt = () => {
47
+ const onError = (err) => {
48
+ server.off("listening", onListen);
49
+ if (err.code === "EADDRINUSE" && p < startPort + maxProbe - 1) {
50
+ p += 1;
51
+ attempt();
52
+ return;
53
+ }
54
+ reject(err);
55
+ };
56
+ const onListen = () => {
57
+ server.off("error", onError);
58
+ resolve(p);
59
+ };
60
+ server.once("error", onError);
61
+ server.once("listening", onListen);
62
+ server.listen(p);
39
63
  };
40
- tryPort(start);
64
+ attempt();
41
65
  });
42
66
  }
43
67
 
@@ -230,9 +254,13 @@ watcher.onFileChange((relPath, absPath, content, eventType) => {
230
254
  lsp.handleFileChange(relPath, absPath, content, eventType);
231
255
  });
232
256
 
233
- const port = await findFreePort(preferredPort);
257
+ // Bind the real server. Probe upward on EADDRINUSE unless the user passed
258
+ // --port explicitly (in which case bind that port or fail loudly).
259
+ const port = await listenWithProbe(server, preferredPort, portExplicit ? 1 : 20);
234
260
 
235
- // Write actual port back to config so skills can always find the server
261
+ // Write actual bound port back to config so skills can always find the server.
262
+ // Must happen AFTER listen succeeds — a pre-listen write would leave a stale
263
+ // value if the bind failed.
236
264
  try {
237
265
  let metaConfig = {};
238
266
  try { metaConfig = JSON.parse(readFileSync(metaConfigPath, "utf-8")); } catch {}
@@ -242,18 +270,16 @@ try {
242
270
  console.error(`Warning: could not write port to config: ${err.message}`);
243
271
  }
244
272
 
245
- server.listen(port, async () => {
246
- console.log(`wicked-brain-server running on port ${port} (brain: ${brainId}, pid: ${pid})`);
247
- watcher.start();
248
- const busReady = await waitForBus();
249
- emitEvent("wicked.server.started", "brain.system", {
250
- brain_id: brainId, port, pid,
251
- });
252
- if (busReady) {
253
- try {
254
- memorySubscriber = await startMemorySubscriber({ brainPath, brainId, db });
255
- } catch (err) {
256
- console.error(`[memory-subscriber] failed to start: ${err.message}`);
257
- }
258
- }
273
+ console.log(`wicked-brain-server running on port ${port} (brain: ${brainId}, pid: ${pid})`);
274
+ watcher.start();
275
+ const busReady = await waitForBus();
276
+ emitEvent("wicked.server.started", "brain.system", {
277
+ brain_id: brainId, port, pid,
259
278
  });
279
+ if (busReady) {
280
+ try {
281
+ memorySubscriber = await startMemorySubscriber({ brainPath, brainId, db });
282
+ } catch (err) {
283
+ console.error(`[memory-subscriber] failed to start: ${err.message}`);
284
+ }
285
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain-server",
3
- "version": "0.7.2",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "description": "SQLite FTS5 search server for wicked-brain digital knowledge bases",
6
6
  "keywords": [
@@ -60,9 +60,12 @@ If the result is `not installed`, the package was never globally installed (the
60
60
  user may have been running via `npx` only). Treat this as "needs install" and
61
61
  proceed to Step 4.
62
62
 
63
- **Do NOT use `npx wicked-brain-server --version`** it may return the version
64
- of a stale cached npx copy, not the globally installed one that actually runs
65
- when brain servers start.
63
+ `wicked-brain-server --version` (v0.8.0+) prints the installed version directly.
64
+ It's safe to call the binary by its installed path e.g.
65
+ `$(npm config get prefix)/bin/wicked-brain-server --version`. **Avoid
66
+ `npx wicked-brain-server --version`** — npx may resolve to a cached copy that
67
+ isn't the globally installed one that actually runs when brain servers start,
68
+ so the number it reports can lie about what will serve requests.
66
69
 
67
70
  ### Step 2: Check latest version on npm
68
71