nexo-brain 7.13.7 → 7.13.8
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/.claude-plugin/plugin.json +1 -1
- package/README.md +3 -1
- package/bin/nexo-brain.js +129 -9
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.13.
|
|
3
|
+
"version": "7.13.8",
|
|
4
4
|
"description": "Local cognitive runtime for Claude Code \u2014 persistent memory, overnight learning, doctor diagnostics, personal scripts, recovery-aware jobs, startup preflight, and optional dashboard/power helper.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "NEXO Brain",
|
package/README.md
CHANGED
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
[Watch the overview video](https://nexo-brain.com/watch/) · [Watch on YouTube](https://www.youtube.com/watch?v=i2lkGhKyVqI) · [Open the infographic](https://nexo-brain.com/assets/nexo-brain-infographic-v5.png)
|
|
20
20
|
|
|
21
|
-
Version `7.13.
|
|
21
|
+
Version `7.13.8` is the current packaged-runtime line. Patch release over v7.13.7 — Brain now rejects Python <3.10 during Desktop-managed fresh installs, honors the Python interpreter prepared by Desktop, and fails clearly before dependency resolution if an unsupported Apple Python 3.9 reaches the installer.
|
|
22
|
+
|
|
23
|
+
Previously in `7.13.7`: patch release — Brain adds an authenticated official protocol-card client (`nexo_card_catalog`, `nexo_card_get`, `nexo_card_match`) so agents can ask the NEXO Desktop backend for the right task protocol at runtime. The protocol corpus stays private on the server; this open-source package ships only the client, tool map, and agent guidance.
|
|
22
24
|
|
|
23
25
|
Previously in `7.13.6`: patch release — Codex hook sync now renders the managed `PreToolUse` shell/exec_command guard with native Windows `cmd.exe` syntax while preserving the existing POSIX command on macOS/Linux. Result: coordinated Desktop bundles can ship the fixed Brain without changing the Mac/Windows installation contract.
|
|
24
26
|
|
package/bin/nexo-brain.js
CHANGED
|
@@ -36,6 +36,8 @@ if (process.platform === "win32") {
|
|
|
36
36
|
let NEXO_HOME = process.env.NEXO_HOME || path.join(require("os").homedir(), ".nexo");
|
|
37
37
|
const DEFAULT_ASSISTANT_NAME = "Nova";
|
|
38
38
|
const RESERVED_ASSISTANT_NAME_KEYS = new Set(["nexo", "nexobrain", "nexodesktop"]);
|
|
39
|
+
const MIN_INSTALLER_PYTHON_MAJOR = 3;
|
|
40
|
+
const MIN_INSTALLER_PYTHON_MINOR = 10;
|
|
39
41
|
|
|
40
42
|
function normalizeAssistantNameCandidate(value) {
|
|
41
43
|
return String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
@@ -225,6 +227,57 @@ function run(cmd, opts = {}) {
|
|
|
225
227
|
}
|
|
226
228
|
}
|
|
227
229
|
|
|
230
|
+
function shSingleQuote(value) {
|
|
231
|
+
return "'" + String(value || "").replace(/'/g, "'\\''") + "'";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function runPythonProbe(pythonBin, args, timeout = 15000) {
|
|
235
|
+
if (!pythonBin) return null;
|
|
236
|
+
try {
|
|
237
|
+
const result = spawnSync(pythonBin, args, {
|
|
238
|
+
encoding: "utf8",
|
|
239
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
240
|
+
timeout,
|
|
241
|
+
});
|
|
242
|
+
if (result.status !== 0) return null;
|
|
243
|
+
return String(result.stdout || result.stderr || "").trim();
|
|
244
|
+
} catch {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function pythonVersion(pythonBin) {
|
|
250
|
+
return runPythonProbe(pythonBin, ["-c", "import sys; print(sys.version.split()[0])"]);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function pythonVersionMeetsMinimum(versionText) {
|
|
254
|
+
const match = String(versionText || "").trim().match(/^(\d+)\.(\d+)(?:\.|$)/);
|
|
255
|
+
if (!match) return false;
|
|
256
|
+
const major = Number(match[1]);
|
|
257
|
+
const minor = Number(match[2]);
|
|
258
|
+
return major > MIN_INSTALLER_PYTHON_MAJOR
|
|
259
|
+
|| (major === MIN_INSTALLER_PYTHON_MAJOR && minor >= MIN_INSTALLER_PYTHON_MINOR);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function resolveInstallerPython() {
|
|
263
|
+
const candidates = [
|
|
264
|
+
process.env.NEXO_BOOTSTRAP_PYTHON,
|
|
265
|
+
process.env.NEXO_RUNTIME_PYTHON,
|
|
266
|
+
process.env.NEXO_PYTHON,
|
|
267
|
+
run("which python3"),
|
|
268
|
+
run("which python"),
|
|
269
|
+
].filter(Boolean);
|
|
270
|
+
const seen = new Set();
|
|
271
|
+
for (const candidate of candidates) {
|
|
272
|
+
const clean = String(candidate || "").trim();
|
|
273
|
+
if (!clean || seen.has(clean)) continue;
|
|
274
|
+
seen.add(clean);
|
|
275
|
+
const version = pythonVersion(clean);
|
|
276
|
+
if (version && pythonVersionMeetsMinimum(version)) return clean;
|
|
277
|
+
}
|
|
278
|
+
return "";
|
|
279
|
+
}
|
|
280
|
+
|
|
228
281
|
function findBundledWheel(wheelsDir, prefix) {
|
|
229
282
|
try {
|
|
230
283
|
const normalizedPrefix = String(prefix || "").toLowerCase() + "-";
|
|
@@ -1710,6 +1763,48 @@ function buildManagedCliEnv(extraEnv = {}) {
|
|
|
1710
1763
|
};
|
|
1711
1764
|
}
|
|
1712
1765
|
|
|
1766
|
+
function ensureDesktopNodeShim(desktopNode) {
|
|
1767
|
+
const clean = String(desktopNode || "").trim();
|
|
1768
|
+
if (!clean) return "";
|
|
1769
|
+
const shimDir = path.join(NEXO_HOME, "runtime", "bootstrap", "node-shim");
|
|
1770
|
+
fs.mkdirSync(shimDir, { recursive: true });
|
|
1771
|
+
if (process.platform === "win32") {
|
|
1772
|
+
const shimPath = path.join(shimDir, "node.cmd");
|
|
1773
|
+
fs.writeFileSync(
|
|
1774
|
+
shimPath,
|
|
1775
|
+
`@echo off\r\nset ELECTRON_RUN_AS_NODE=1\r\n"${clean}" %*\r\n`,
|
|
1776
|
+
);
|
|
1777
|
+
return shimDir;
|
|
1778
|
+
}
|
|
1779
|
+
const shimPath = path.join(shimDir, "node");
|
|
1780
|
+
fs.writeFileSync(
|
|
1781
|
+
shimPath,
|
|
1782
|
+
[
|
|
1783
|
+
"#!/bin/sh",
|
|
1784
|
+
"export ELECTRON_RUN_AS_NODE=1",
|
|
1785
|
+
`exec ${shSingleQuote(clean)} "$@"`,
|
|
1786
|
+
"",
|
|
1787
|
+
].join("\n"),
|
|
1788
|
+
);
|
|
1789
|
+
fs.chmodSync(shimPath, 0o755);
|
|
1790
|
+
return shimDir;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
function withDesktopNodeShim(env, desktopNode) {
|
|
1794
|
+
try {
|
|
1795
|
+
const shimDir = ensureDesktopNodeShim(desktopNode);
|
|
1796
|
+
if (!shimDir) return env;
|
|
1797
|
+
return {
|
|
1798
|
+
...env,
|
|
1799
|
+
ELECTRON_RUN_AS_NODE: "1",
|
|
1800
|
+
PATH: [shimDir, env.PATH || ""].filter(Boolean).join(path.delimiter),
|
|
1801
|
+
};
|
|
1802
|
+
} catch (err) {
|
|
1803
|
+
log(`Desktop Node shim could not be created: ${String(err && err.message || err)}`);
|
|
1804
|
+
return env;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1713
1808
|
function resolveManagedClaudeBinary() {
|
|
1714
1809
|
const prefix = managedClaudePrefix();
|
|
1715
1810
|
const candidates = process.platform === "win32"
|
|
@@ -1921,11 +2016,13 @@ function installClaudeCodeCli(platform) {
|
|
|
1921
2016
|
return { installed: true, path: claudeInstalled };
|
|
1922
2017
|
}
|
|
1923
2018
|
|
|
1924
|
-
const installEnv = buildManagedCliEnv();
|
|
1925
2019
|
const desktopNode = String(process.env.NEXO_DESKTOP_NODE || "").trim();
|
|
1926
2020
|
const bundledNpmCli = String(process.env.NEXO_DESKTOP_NPM_CLI || "").trim();
|
|
1927
2021
|
const managedPrefix = managedClaudePrefix();
|
|
1928
2022
|
const desktopManaged = isDesktopManagedInstall();
|
|
2023
|
+
const npmViaDesktop = desktopNode && bundledNpmCli;
|
|
2024
|
+
let installEnv = buildManagedCliEnv();
|
|
2025
|
+
if (desktopNode) installEnv = withDesktopNodeShim(installEnv, desktopNode);
|
|
1929
2026
|
|
|
1930
2027
|
// OFFLINE-FIRST v0.32.4: install claude-code wrapper + ALL its native packs
|
|
1931
2028
|
// from bundled tarballs. Path: resources/brain-bundle/claude-code/*.tgz.
|
|
@@ -1963,8 +2060,18 @@ function installClaudeCodeCli(platform) {
|
|
|
1963
2060
|
const tgzPaths = [path.join(bundledClaudeDir, wrapper), ...nativePacks.map((p) => path.join(bundledClaudeDir, p))];
|
|
1964
2061
|
log(" Installing claude-code from bundled tarballs (offline, " + (1 + nativePacks.length) + " packs)...");
|
|
1965
2062
|
spawnSync(
|
|
1966
|
-
"npm",
|
|
1967
|
-
[
|
|
2063
|
+
npmViaDesktop ? desktopNode : "npm",
|
|
2064
|
+
[
|
|
2065
|
+
...(npmViaDesktop ? [bundledNpmCli] : []),
|
|
2066
|
+
"install",
|
|
2067
|
+
"-g",
|
|
2068
|
+
"--prefix",
|
|
2069
|
+
managedPrefix,
|
|
2070
|
+
"--offline",
|
|
2071
|
+
"--no-audit",
|
|
2072
|
+
"--no-fund",
|
|
2073
|
+
...tgzPaths,
|
|
2074
|
+
],
|
|
1968
2075
|
{ stdio: "inherit", env: installEnv },
|
|
1969
2076
|
);
|
|
1970
2077
|
claudeInstalled = detectInstalledClients().claude_code.path || "";
|
|
@@ -1977,8 +2084,15 @@ function installClaudeCodeCli(platform) {
|
|
|
1977
2084
|
const tgzPath = path.join(bundledClaudeDir, wrapper);
|
|
1978
2085
|
log(" Installing claude-code from bundled wrapper only (legacy bundle, may need network for native pack)...");
|
|
1979
2086
|
spawnSync(
|
|
1980
|
-
"npm",
|
|
1981
|
-
[
|
|
2087
|
+
npmViaDesktop ? desktopNode : "npm",
|
|
2088
|
+
[
|
|
2089
|
+
...(npmViaDesktop ? [bundledNpmCli] : []),
|
|
2090
|
+
"install",
|
|
2091
|
+
"-g",
|
|
2092
|
+
"--prefix",
|
|
2093
|
+
managedPrefix,
|
|
2094
|
+
tgzPath,
|
|
2095
|
+
],
|
|
1982
2096
|
{ stdio: "inherit", env: installEnv },
|
|
1983
2097
|
);
|
|
1984
2098
|
claudeInstalled = detectInstalledClients().claude_code.path || "";
|
|
@@ -3081,7 +3195,7 @@ async function runSetup() {
|
|
|
3081
3195
|
}
|
|
3082
3196
|
|
|
3083
3197
|
// Find or install Python (platform-aware)
|
|
3084
|
-
let python =
|
|
3198
|
+
let python = resolveInstallerPython();
|
|
3085
3199
|
if (!python) {
|
|
3086
3200
|
if (platform === "darwin") {
|
|
3087
3201
|
// v0.32.5 — Mac vanilla NO trae python3. La auto-instalación de
|
|
@@ -3122,7 +3236,7 @@ async function runSetup() {
|
|
|
3122
3236
|
// fallan al import. Pinning a `python@3.12` evita el drift.
|
|
3123
3237
|
log("Python 3.12 not found. Installing via Homebrew...");
|
|
3124
3238
|
spawnSync("brew", ["install", "python@3.12"], { stdio: "inherit" });
|
|
3125
|
-
python = run("which python3.12") || run("which python3");
|
|
3239
|
+
python = resolveInstallerPython() || run("which python3.12") || run("which python3");
|
|
3126
3240
|
}
|
|
3127
3241
|
} else if (platform === "linux") {
|
|
3128
3242
|
// Linux: try apt or yum
|
|
@@ -3132,7 +3246,7 @@ async function runSetup() {
|
|
|
3132
3246
|
} else if (run("which yum")) {
|
|
3133
3247
|
spawnSync("sudo", ["yum", "install", "-y", "python3", "python3-pip"], { stdio: "inherit" });
|
|
3134
3248
|
}
|
|
3135
|
-
python =
|
|
3249
|
+
python = resolveInstallerPython();
|
|
3136
3250
|
}
|
|
3137
3251
|
if (!python) {
|
|
3138
3252
|
log("Python 3 not found and couldn't install automatically.");
|
|
@@ -3140,7 +3254,13 @@ async function runSetup() {
|
|
|
3140
3254
|
process.exit(1);
|
|
3141
3255
|
}
|
|
3142
3256
|
}
|
|
3143
|
-
const pyVersion =
|
|
3257
|
+
const pyVersion = pythonVersion(python);
|
|
3258
|
+
if (!pyVersion || !pythonVersionMeetsMinimum(pyVersion)) {
|
|
3259
|
+
log(pyVersion
|
|
3260
|
+
? `Python at ${python} is ${pyVersion}; NEXO Brain requires Python >=${MIN_INSTALLER_PYTHON_MAJOR}.${MIN_INSTALLER_PYTHON_MINOR}.`
|
|
3261
|
+
: `Python at ${python || "(not found)"} is not executable.`);
|
|
3262
|
+
process.exit(1);
|
|
3263
|
+
}
|
|
3144
3264
|
log(`Found ${pyVersion} at ${python}`);
|
|
3145
3265
|
logMacPermissionsNotice(NEXO_HOME, python);
|
|
3146
3266
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.13.
|
|
3
|
+
"version": "7.13.8",
|
|
4
4
|
"mcpName": "io.github.wazionapps/nexo",
|
|
5
5
|
"description": "NEXO Brain — Shared brain for AI agents. Persistent memory, semantic RAG, natural forgetting, metacognitive guard, trust scoring, 150+ MCP tools. Works with Claude Code, Codex, Claude Desktop & any MCP client. 100% local, free.",
|
|
6
6
|
"homepage": "https://nexo-brain.com",
|