nexo-brain 7.25.6 → 7.27.0
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 +5 -1
- package/bin/nexo-brain.js +235 -31
- package/codex/openai-codex-0.133.0.tgz +0 -0
- package/package.json +7 -1
- package/src/agent_runner.py +99 -32
- package/src/call_model_raw.py +1 -1
- package/src/cli.py +117 -4
- package/src/client_preferences.py +296 -1
- package/src/client_sync.py +343 -6
- package/src/db/_schema.py +23 -0
- package/src/db/_sessions.py +75 -24
- package/src/enforcement_classifier.py +1 -1
- package/src/local_context/api.py +58 -45
- package/src/model_defaults.json +4 -4
- package/src/model_defaults.py +4 -4
- package/src/provider_runtime.py +39 -0
- package/src/resonance_tiers.json +5 -5
- package/src/scripts/deep-sleep/extract.py +2 -0
- package/src/scripts/deep-sleep/synthesize.py +1 -0
- package/src/scripts/nexo-cron-wrapper.sh +108 -25
- package/src/scripts/nexo-morning-agent.py +1 -1
- package/src/server.py +3 -1
- package/src/tools_automation_sessions.py +2 -1
- package/src/tools_sessions.py +13 -8
- package/templates/launchagents/README.md +2 -2
- package/templates/launchagents/com.nexo.auto-close-sessions.plist +1 -1
- package/templates/launchagents/com.nexo.catchup.plist +3 -3
- package/templates/launchagents/com.nexo.cognitive-decay.plist +3 -3
- package/templates/launchagents/com.nexo.deep-sleep.plist +3 -3
- package/templates/launchagents/com.nexo.evolution.plist +3 -3
- package/templates/launchagents/com.nexo.followup-hygiene.plist +3 -3
- package/templates/launchagents/com.nexo.immune.plist +1 -1
- package/templates/launchagents/com.nexo.postmortem.plist +3 -3
- package/templates/launchagents/com.nexo.self-audit.plist +3 -3
- package/templates/launchagents/com.nexo.synthesis.plist +1 -1
- package/templates/launchagents/com.nexo.watchdog.plist +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.27.0",
|
|
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,11 @@
|
|
|
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.
|
|
21
|
+
Version `7.27.0` is the current packaged-runtime line. Minor release over v7.26.0 - Codex-side defaults move to verified `gpt-5.5` resonance tiers, managed config healing follows that model family, and Local Memory file-type actions tolerate transient SQLite locks.
|
|
22
|
+
|
|
23
|
+
Previously in `7.26.0`: minor release over v7.25.6 - provider runtime parity lets Desktop-managed Brain choose Anthropic or OpenAI, keep provider metadata on sessions/automation/crons, and provision the managed OpenAI runtime from bundled Desktop resources.
|
|
24
|
+
|
|
25
|
+
Previously in `7.25.6`: patch release over v7.25.5 - existing Local Memory sidecar databases repair legacy root/exclusion columns before source-dependent indexes are created, and core background crons prefer the NEXO-managed Python runtime.
|
|
22
26
|
|
|
23
27
|
Previously in `7.25.4`: patch release over v7.25.3 - Local Memory starts from safe user-content and email roots, adds configurable included/excluded file types, and cleans legacy whole-disk index state with backup or archive-rebuild safety.
|
|
24
28
|
|
package/bin/nexo-brain.js
CHANGED
|
@@ -18,6 +18,7 @@ const { execSync, spawnSync } = require("child_process");
|
|
|
18
18
|
const crypto = require("crypto");
|
|
19
19
|
const fs = require("fs");
|
|
20
20
|
const { createRequire } = require("module");
|
|
21
|
+
const os = require("os");
|
|
21
22
|
const path = require("path");
|
|
22
23
|
const readline = require("readline");
|
|
23
24
|
// Force relative launcher helpers to resolve from bin/ even under test harnesses.
|
|
@@ -114,8 +115,8 @@ const PUBLIC_CONTRIBUTION_UPSTREAM = "wazionapps/nexo";
|
|
|
114
115
|
const MODEL_DEFAULTS_PATH = path.join(__dirname, "..", "src", "model_defaults.json");
|
|
115
116
|
function _loadModelDefaults() {
|
|
116
117
|
const fallback = {
|
|
117
|
-
claude_code: { model: "claude-opus-4-
|
|
118
|
-
codex: { model: "gpt-5.
|
|
118
|
+
claude_code: { model: "claude-opus-4-7[1m]", reasoning_effort: "max", display_name: "Opus 4.7 with 1M context" },
|
|
119
|
+
codex: { model: "gpt-5.5", reasoning_effort: "xhigh", display_name: "GPT-5.5 with max reasoning" },
|
|
119
120
|
};
|
|
120
121
|
try {
|
|
121
122
|
const raw = JSON.parse(fs.readFileSync(MODEL_DEFAULTS_PATH, "utf8"));
|
|
@@ -1672,6 +1673,7 @@ function getDefaultSchedule(timezone) {
|
|
|
1672
1673
|
default_terminal_client: "claude_code",
|
|
1673
1674
|
automation_enabled: true,
|
|
1674
1675
|
automation_backend: "claude_code",
|
|
1676
|
+
provider_runtime: defaultProviderRuntime("anthropic", "anthropic"),
|
|
1675
1677
|
// v6.0.0 — model/reasoning_effort have moved to src/resonance_tiers.json
|
|
1676
1678
|
// keyed by the operator's preferences.default_resonance. The shape
|
|
1677
1679
|
// below stays so that downstream readers that iterate the profile
|
|
@@ -1813,9 +1815,31 @@ function detectInstalledClients() {
|
|
|
1813
1815
|
: [];
|
|
1814
1816
|
const desktopAppPath = desktopApps.find((candidate) => fs.existsSync(candidate)) || "";
|
|
1815
1817
|
const managedClaudeBin = resolveManagedClaudeBinary();
|
|
1818
|
+
const managedCodexBin = resolveManagedCodexBinary();
|
|
1819
|
+
const desktopManaged = isDesktopManagedInstall();
|
|
1820
|
+
if (desktopManaged) {
|
|
1821
|
+
const managedCodexReady = managedCodexBin && codexVendorPresent(managedClaudePrefix());
|
|
1822
|
+
return {
|
|
1823
|
+
claude_code: {
|
|
1824
|
+
installed: Boolean(managedClaudeBin),
|
|
1825
|
+
path: managedClaudeBin,
|
|
1826
|
+
detectedBy: managedClaudeBin ? "managed_binary" : "missing",
|
|
1827
|
+
},
|
|
1828
|
+
codex: {
|
|
1829
|
+
installed: Boolean(managedCodexReady),
|
|
1830
|
+
path: managedCodexReady ? managedCodexBin : "",
|
|
1831
|
+
detectedBy: managedCodexReady ? "managed_binary" : "missing",
|
|
1832
|
+
},
|
|
1833
|
+
claude_desktop: {
|
|
1834
|
+
installed: Boolean(desktopAppPath || fs.existsSync(desktopConfig)),
|
|
1835
|
+
path: desktopAppPath || desktopConfig,
|
|
1836
|
+
detectedBy: desktopAppPath ? "app" : (fs.existsSync(desktopConfig) ? "config" : "missing"),
|
|
1837
|
+
},
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1816
1840
|
const persistedClaudeBin = readPersistedClaudeCliPath();
|
|
1817
1841
|
const claudeBin = managedClaudeBin || persistedClaudeBin || run("which claude", { env: buildManagedCliEnv() }) || run("which claude") || "";
|
|
1818
|
-
const codexBin = run("which codex") || "";
|
|
1842
|
+
const codexBin = managedCodexBin || run("which codex", { env: buildManagedCliEnv() }) || run("which codex") || "";
|
|
1819
1843
|
return {
|
|
1820
1844
|
claude_code: {
|
|
1821
1845
|
installed: Boolean(claudeBin),
|
|
@@ -1905,6 +1929,14 @@ function resolveManagedClaudeBinary() {
|
|
|
1905
1929
|
return candidates.find((candidate) => candidate && fs.existsSync(candidate)) || "";
|
|
1906
1930
|
}
|
|
1907
1931
|
|
|
1932
|
+
function resolveManagedCodexBinary() {
|
|
1933
|
+
const prefix = managedClaudePrefix();
|
|
1934
|
+
const candidates = process.platform === "win32"
|
|
1935
|
+
? [path.join(prefix, "codex.cmd"), path.join(prefix, "bin", "codex.cmd")]
|
|
1936
|
+
: [path.join(prefix, "bin", "codex"), path.join(prefix, "codex")];
|
|
1937
|
+
return candidates.find((candidate) => candidate && fs.existsSync(candidate)) || "";
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1908
1940
|
function readPersistedClaudeCliPath() {
|
|
1909
1941
|
const candidates = [
|
|
1910
1942
|
path.join(NEXO_HOME, "config", "claude-cli-path"),
|
|
@@ -2000,6 +2032,60 @@ function defaultClientRuntimeProfiles() {
|
|
|
2000
2032
|
};
|
|
2001
2033
|
}
|
|
2002
2034
|
|
|
2035
|
+
function defaultProviderRuntime(selectedProvider = "anthropic", automationProvider = selectedProvider) {
|
|
2036
|
+
const automationBackend = automationProvider === "openai" ? "codex" : (automationProvider === "anthropic" ? "claude_code" : "none");
|
|
2037
|
+
return {
|
|
2038
|
+
schema_version: 1,
|
|
2039
|
+
selected_chat_provider: selectedProvider,
|
|
2040
|
+
automation_provider: automationProvider,
|
|
2041
|
+
automation_backend: automationBackend,
|
|
2042
|
+
providers: {
|
|
2043
|
+
anthropic: {
|
|
2044
|
+
client: "claude_code",
|
|
2045
|
+
runtime_account_status: {
|
|
2046
|
+
surface: "desktop_login",
|
|
2047
|
+
status: "unknown",
|
|
2048
|
+
plan: null,
|
|
2049
|
+
last_checked_at: null,
|
|
2050
|
+
detail: null,
|
|
2051
|
+
},
|
|
2052
|
+
install_status: {
|
|
2053
|
+
installed: false,
|
|
2054
|
+
managed: true,
|
|
2055
|
+
binary_path: null,
|
|
2056
|
+
version: null,
|
|
2057
|
+
},
|
|
2058
|
+
},
|
|
2059
|
+
openai: {
|
|
2060
|
+
client: "codex",
|
|
2061
|
+
runtime_account_status: {
|
|
2062
|
+
surface: "desktop_login",
|
|
2063
|
+
status: "unknown",
|
|
2064
|
+
plan: null,
|
|
2065
|
+
last_checked_at: null,
|
|
2066
|
+
detail: null,
|
|
2067
|
+
},
|
|
2068
|
+
install_status: {
|
|
2069
|
+
installed: false,
|
|
2070
|
+
managed: true,
|
|
2071
|
+
binary_path: null,
|
|
2072
|
+
version: null,
|
|
2073
|
+
},
|
|
2074
|
+
},
|
|
2075
|
+
},
|
|
2076
|
+
fallback_policy: {
|
|
2077
|
+
chat: "ask",
|
|
2078
|
+
automation: "fail_closed",
|
|
2079
|
+
},
|
|
2080
|
+
last_provider_change: {
|
|
2081
|
+
changed_at: null,
|
|
2082
|
+
from_provider: null,
|
|
2083
|
+
to_provider: null,
|
|
2084
|
+
source: null,
|
|
2085
|
+
},
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2003
2089
|
function runtimeClientLabel(client) {
|
|
2004
2090
|
if (client === "claude_code") return "Claude Code";
|
|
2005
2091
|
if (client === "codex") return "Codex";
|
|
@@ -2039,6 +2125,7 @@ function defaultClientSetup(detected) {
|
|
|
2039
2125
|
default_terminal_client: "claude_code",
|
|
2040
2126
|
automation_enabled: true,
|
|
2041
2127
|
automation_backend: "claude_code",
|
|
2128
|
+
provider_runtime: defaultProviderRuntime("anthropic", "anthropic"),
|
|
2042
2129
|
client_runtime_profiles: defaultClientRuntimeProfiles(),
|
|
2043
2130
|
client_install_preferences: {
|
|
2044
2131
|
claude_code: "ask",
|
|
@@ -2057,6 +2144,17 @@ function applyClientSetupToSchedule(schedule, setup) {
|
|
|
2057
2144
|
schedule.default_terminal_client = setup.default_terminal_client;
|
|
2058
2145
|
schedule.automation_enabled = Boolean(setup.automation_enabled);
|
|
2059
2146
|
schedule.automation_backend = schedule.automation_enabled ? setup.automation_backend : "none";
|
|
2147
|
+
const selectedProvider = setup.default_terminal_client === "codex" ? "openai" : "anthropic";
|
|
2148
|
+
const automationProvider = schedule.automation_backend === "codex"
|
|
2149
|
+
? "openai"
|
|
2150
|
+
: (schedule.automation_backend === "claude_code" ? "anthropic" : "none");
|
|
2151
|
+
schedule.provider_runtime = {
|
|
2152
|
+
...defaultProviderRuntime(selectedProvider, automationProvider),
|
|
2153
|
+
...(setup.provider_runtime || {}),
|
|
2154
|
+
selected_chat_provider: selectedProvider,
|
|
2155
|
+
automation_provider: automationProvider,
|
|
2156
|
+
automation_backend: schedule.automation_backend,
|
|
2157
|
+
};
|
|
2060
2158
|
schedule.client_runtime_profiles = {
|
|
2061
2159
|
...defaultClientRuntimeProfiles(),
|
|
2062
2160
|
...(setup.client_runtime_profiles || {}),
|
|
@@ -2090,6 +2188,9 @@ function installClaudeCodeCli(platform) {
|
|
|
2090
2188
|
const npmViaDesktop = desktopNode && bundledNpmCli;
|
|
2091
2189
|
let installEnv = buildManagedCliEnv();
|
|
2092
2190
|
if (desktopNode) installEnv = withDesktopNodeShim(installEnv, desktopNode);
|
|
2191
|
+
if (desktopManaged && !npmViaDesktop) {
|
|
2192
|
+
return { installed: false, path: "" };
|
|
2193
|
+
}
|
|
2093
2194
|
|
|
2094
2195
|
// OFFLINE-FIRST v0.32.4: install claude-code wrapper + ALL its native packs
|
|
2095
2196
|
// from bundled tarballs. Path: resources/brain-bundle/claude-code/*.tgz.
|
|
@@ -2186,22 +2287,7 @@ function installClaudeCodeCli(platform) {
|
|
|
2186
2287
|
}
|
|
2187
2288
|
}
|
|
2188
2289
|
|
|
2189
|
-
if (desktopManaged) {
|
|
2190
|
-
spawnSync(
|
|
2191
|
-
"npm",
|
|
2192
|
-
["install", "-g", "--prefix", managedPrefix, "@anthropic-ai/claude-code"],
|
|
2193
|
-
{
|
|
2194
|
-
stdio: "inherit",
|
|
2195
|
-
env: installEnv,
|
|
2196
|
-
},
|
|
2197
|
-
);
|
|
2198
|
-
claudeInstalled = detectInstalledClients().claude_code.path || "";
|
|
2199
|
-
if (claudeInstalled) {
|
|
2200
|
-
persistClaudeCliPath(claudeInstalled);
|
|
2201
|
-
return { installed: true, path: claudeInstalled };
|
|
2202
|
-
}
|
|
2203
|
-
return { installed: false, path: "" };
|
|
2204
|
-
}
|
|
2290
|
+
if (desktopManaged) return { installed: false, path: "" };
|
|
2205
2291
|
|
|
2206
2292
|
spawnSync("npx", ["-y", "@anthropic-ai/claude-code", "--version"], {
|
|
2207
2293
|
stdio: "pipe",
|
|
@@ -2221,11 +2307,128 @@ function installClaudeCodeCli(platform) {
|
|
|
2221
2307
|
return { installed: Boolean(claudeInstalled), path: claudeInstalled || "" };
|
|
2222
2308
|
}
|
|
2223
2309
|
|
|
2310
|
+
function installBundledCodexVendor(bundleDir, managedPrefix) {
|
|
2311
|
+
const packageRoots = [
|
|
2312
|
+
path.join(managedPrefix, "lib", "node_modules", "@openai", "codex"),
|
|
2313
|
+
path.join(managedPrefix, "node_modules", "@openai", "codex"),
|
|
2314
|
+
];
|
|
2315
|
+
const packageRoot = packageRoots.find((candidate) => fs.existsSync(candidate)) || packageRoots[0];
|
|
2316
|
+
if (!fs.existsSync(packageRoot)) return false;
|
|
2317
|
+
const allTgz = fs.readdirSync(bundleDir).filter((f) => f.endsWith(".tgz"));
|
|
2318
|
+
const platformSlug = `${process.platform}-${process.arch}`;
|
|
2319
|
+
const nativePacks = allTgz.filter((f) => f.includes(`-${platformSlug}.tgz`) || f.includes(`-${platformSlug}-`));
|
|
2320
|
+
if (!nativePacks.length) return false;
|
|
2321
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nexo-codex-vendor-"));
|
|
2322
|
+
try {
|
|
2323
|
+
for (const pack of nativePacks) {
|
|
2324
|
+
const tgzPath = path.join(bundleDir, pack);
|
|
2325
|
+
const extract = spawnSync("tar", ["-xzf", tgzPath, "-C", tmpRoot], { stdio: "pipe" });
|
|
2326
|
+
if (extract.status !== 0) continue;
|
|
2327
|
+
const vendorRoot = path.join(tmpRoot, "package", "vendor");
|
|
2328
|
+
if (!fs.existsSync(vendorRoot)) continue;
|
|
2329
|
+
fs.mkdirSync(path.join(packageRoot, "vendor"), { recursive: true });
|
|
2330
|
+
for (const entry of fs.readdirSync(vendorRoot)) {
|
|
2331
|
+
fs.cpSync(path.join(vendorRoot, entry), path.join(packageRoot, "vendor", entry), { recursive: true, force: true });
|
|
2332
|
+
}
|
|
2333
|
+
return true;
|
|
2334
|
+
}
|
|
2335
|
+
} finally {
|
|
2336
|
+
try { fs.rmSync(tmpRoot, { recursive: true, force: true }); } catch {}
|
|
2337
|
+
}
|
|
2338
|
+
return false;
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
function codexVendorPresent(managedPrefix) {
|
|
2342
|
+
const packageRoots = [
|
|
2343
|
+
path.join(managedPrefix, "lib", "node_modules", "@openai", "codex"),
|
|
2344
|
+
path.join(managedPrefix, "node_modules", "@openai", "codex"),
|
|
2345
|
+
];
|
|
2346
|
+
for (const packageRoot of packageRoots) {
|
|
2347
|
+
const vendorRoot = path.join(packageRoot, "vendor");
|
|
2348
|
+
if (!fs.existsSync(vendorRoot)) continue;
|
|
2349
|
+
try {
|
|
2350
|
+
const stack = [vendorRoot];
|
|
2351
|
+
while (stack.length) {
|
|
2352
|
+
const current = stack.pop();
|
|
2353
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
2354
|
+
const target = path.join(current, entry.name);
|
|
2355
|
+
if (entry.isDirectory()) {
|
|
2356
|
+
stack.push(target);
|
|
2357
|
+
} else if (entry.isFile() && path.basename(path.dirname(target)) === "bin" && entry.name.startsWith("codex")) {
|
|
2358
|
+
return true;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
} catch {}
|
|
2363
|
+
}
|
|
2364
|
+
return false;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2224
2367
|
function installCodexCli() {
|
|
2225
|
-
const
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
const
|
|
2368
|
+
const desktopNode = String(process.env.NEXO_DESKTOP_NODE || "").trim();
|
|
2369
|
+
const bundledNpmCli = String(process.env.NEXO_DESKTOP_NPM_CLI || "").trim();
|
|
2370
|
+
const managedPrefix = managedClaudePrefix();
|
|
2371
|
+
const desktopManaged = isDesktopManagedInstall();
|
|
2372
|
+
let before = detectInstalledClients().codex.path || "";
|
|
2373
|
+
if (before && (!desktopManaged || codexVendorPresent(managedPrefix))) return { installed: true, path: before };
|
|
2374
|
+
if (before && desktopManaged) {
|
|
2375
|
+
log(" Managed Codex wrapper exists, but native vendor is missing; repairing bundled vendor.");
|
|
2376
|
+
}
|
|
2377
|
+
const npmViaDesktop = desktopNode && bundledNpmCli;
|
|
2378
|
+
let installEnv = buildManagedCliEnv();
|
|
2379
|
+
if (desktopNode) installEnv = withDesktopNodeShim(installEnv, desktopNode);
|
|
2380
|
+
if (desktopManaged && !npmViaDesktop) {
|
|
2381
|
+
return { installed: false, path: "" };
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
const bundledCodexDir = path.join(__dirname, "..", "codex");
|
|
2385
|
+
if (fs.existsSync(bundledCodexDir)) {
|
|
2386
|
+
const allTgz = fs.readdirSync(bundledCodexDir).filter((f) => f.endsWith(".tgz")).sort();
|
|
2387
|
+
const wrapper = allTgz.find((f) => /^openai-codex-\d+\.\d+\.\d+\.tgz$/.test(f));
|
|
2388
|
+
if (wrapper) {
|
|
2389
|
+
const tgzPath = path.join(bundledCodexDir, wrapper);
|
|
2390
|
+
log(" Installing Codex from bundled tarball (offline wrapper + native vendor)...");
|
|
2391
|
+
spawnSync(
|
|
2392
|
+
npmViaDesktop ? desktopNode : "npm",
|
|
2393
|
+
[
|
|
2394
|
+
...(npmViaDesktop ? [bundledNpmCli] : []),
|
|
2395
|
+
"install",
|
|
2396
|
+
"-g",
|
|
2397
|
+
"--prefix",
|
|
2398
|
+
managedPrefix,
|
|
2399
|
+
"--offline",
|
|
2400
|
+
"--no-audit",
|
|
2401
|
+
"--no-fund",
|
|
2402
|
+
tgzPath,
|
|
2403
|
+
],
|
|
2404
|
+
{ stdio: "inherit", env: installEnv },
|
|
2405
|
+
);
|
|
2406
|
+
const bundledVendorInstalled = installBundledCodexVendor(bundledCodexDir, managedPrefix);
|
|
2407
|
+
before = detectInstalledClients().codex.path || "";
|
|
2408
|
+
if (before && bundledVendorInstalled) return { installed: true, path: before };
|
|
2409
|
+
if (before && !bundledVendorInstalled) {
|
|
2410
|
+
log(" Bundled Codex wrapper installed, but native vendor extraction failed; falling back to online install.");
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
if (desktopNode && bundledNpmCli) {
|
|
2416
|
+
spawnSync(
|
|
2417
|
+
desktopNode,
|
|
2418
|
+
[bundledNpmCli, "install", "-g", "--prefix", managedPrefix, "@openai/codex"],
|
|
2419
|
+
{
|
|
2420
|
+
stdio: "inherit",
|
|
2421
|
+
env: { ...installEnv, ELECTRON_RUN_AS_NODE: "1" },
|
|
2422
|
+
},
|
|
2423
|
+
);
|
|
2424
|
+
before = detectInstalledClients().codex.path || "";
|
|
2425
|
+
if (before && (!desktopManaged || codexVendorPresent(managedPrefix))) return { installed: true, path: before };
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
if (desktopManaged) return { installed: false, path: "" };
|
|
2429
|
+
|
|
2430
|
+
spawnSync("npm", ["install", "-g", "--prefix", managedPrefix, "@openai/codex"], { stdio: "inherit", env: installEnv });
|
|
2431
|
+
const codexInstalled = detectInstalledClients().codex.path || "";
|
|
2229
2432
|
return { installed: Boolean(codexInstalled), path: codexInstalled };
|
|
2230
2433
|
}
|
|
2231
2434
|
|
|
@@ -2284,19 +2487,19 @@ async function configureClientSetup({ lang, useDefaults, autoInstall, detected }
|
|
|
2284
2487
|
const required = requiredCliClients(setup);
|
|
2285
2488
|
for (const client of required) {
|
|
2286
2489
|
if (detected[client] && detected[client].installed) continue;
|
|
2287
|
-
if (desktopManaged && client === "claude_code") {
|
|
2288
|
-
const
|
|
2490
|
+
if (desktopManaged && (client === "claude_code" || client === "codex")) {
|
|
2491
|
+
const bundledClientDir = path.join(__dirname, "..", client === "claude_code" ? "claude-code" : "codex");
|
|
2289
2492
|
let hasBundle = false;
|
|
2290
2493
|
try {
|
|
2291
|
-
if (fs.existsSync(
|
|
2292
|
-
hasBundle = fs.readdirSync(
|
|
2494
|
+
if (fs.existsSync(bundledClientDir)) {
|
|
2495
|
+
hasBundle = fs.readdirSync(bundledClientDir).some((f) => f.endsWith(".tgz"));
|
|
2293
2496
|
}
|
|
2294
2497
|
} catch (_) {}
|
|
2295
2498
|
if (!hasBundle) {
|
|
2296
|
-
log(
|
|
2499
|
+
log(`${runtimeClientLabel(client)} install deferred to Desktop final sync.`);
|
|
2297
2500
|
continue;
|
|
2298
2501
|
}
|
|
2299
|
-
log(
|
|
2502
|
+
log(`Bundled ${runtimeClientLabel(client)} tarball detected — installing offline now.`);
|
|
2300
2503
|
}
|
|
2301
2504
|
let shouldInstall = useDefaults || autoInstall === "auto";
|
|
2302
2505
|
if (!shouldInstall && process.stdin.isTTY && process.stdout.isTTY) {
|
|
@@ -2326,8 +2529,9 @@ async function configureClientSetup({ lang, useDefaults, autoInstall, detected }
|
|
|
2326
2529
|
}
|
|
2327
2530
|
|
|
2328
2531
|
if (setup.automation_enabled && setup.automation_backend !== "none" && !detected[setup.automation_backend]?.installed) {
|
|
2329
|
-
if (desktopManaged && setup.automation_backend === "claude_code") {
|
|
2330
|
-
|
|
2532
|
+
if (desktopManaged && (setup.automation_backend === "claude_code" || setup.automation_backend === "codex")) {
|
|
2533
|
+
const label = setup.automation_backend === "claude_code" ? "Claude Code" : "Codex";
|
|
2534
|
+
log(`${label} will be provisioned by Desktop after the core runtime is ready.`);
|
|
2331
2535
|
return { setup, detected };
|
|
2332
2536
|
}
|
|
2333
2537
|
const label = setup.automation_backend === "claude_code" ? "Claude Code" : "Codex";
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.27.0",
|
|
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",
|
|
@@ -65,6 +65,11 @@
|
|
|
65
65
|
"name": "@anthropic-ai/claude-code",
|
|
66
66
|
"type": "npm-global",
|
|
67
67
|
"optional": false
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "@openai/codex",
|
|
71
|
+
"type": "npm-global",
|
|
72
|
+
"optional": true
|
|
68
73
|
}
|
|
69
74
|
],
|
|
70
75
|
"engines": {
|
|
@@ -87,6 +92,7 @@
|
|
|
87
92
|
"!templates/**/__pycache__",
|
|
88
93
|
"!templates/**/*.pyc",
|
|
89
94
|
"!templates/**/*.pyo",
|
|
95
|
+
"codex/openai-codex-0.133.0.tgz",
|
|
90
96
|
".claude-plugin/",
|
|
91
97
|
".mcp.json",
|
|
92
98
|
"hooks/hooks.json",
|