nexo-brain 7.1.0 → 7.1.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/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/auto_update.py +73 -14
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.1",
|
|
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,7 @@
|
|
|
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.1.
|
|
21
|
+
Version `7.1.1` is the current packaged-runtime line. It hotfixes the post-F0.6 updater path after `7.1.0`: packaged installs no longer confuse `~/.nexo/core` with a mutable source repo, and source-sync/update flows now replace compatibility shims safely instead of failing on `db` / `cognitive` / `skills-core` / root-file conflicts. The companion NEXO Desktop client (v0.22.1, closed-source distributed separately) embeds the same hotfix so fresh installs and in-app repairs start from the corrected updater baseline.
|
|
22
22
|
|
|
23
23
|
Previously in `7.0.1`: hotfix over v7.0.0 (db._core.DB_PATH was only caller still hardcoded to legacy ~/.nexo/data/nexo.db; every shared-DB command silently returned empty results post-migration). Previously in `7.0.0`: **BREAKING — Plan Consolidado fase F0.6**: physical separation of the runtime tree into `~/.nexo/{core,personal,runtime}/`. The flat layout (`~/.nexo/scripts/`, `brain/`, `data/`, `operations/`, ...) is gone. Operators on v6.x are auto-migrated on first `nexo update`; fresh installs land directly in the new tree. New `paths.py` helpers are transition-aware.
|
|
24
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.1",
|
|
4
4
|
"mcpName": "io.github.wazionapps/nexo",
|
|
5
5
|
"description": "NEXO Brain \u2014 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",
|
package/src/auto_update.py
CHANGED
|
@@ -3612,6 +3612,23 @@ def _resolve_sync_source() -> tuple[Path | None, Path | None]:
|
|
|
3612
3612
|
return candidate
|
|
3613
3613
|
return None
|
|
3614
3614
|
|
|
3615
|
+
try:
|
|
3616
|
+
runtime_core = (dest / "core").resolve()
|
|
3617
|
+
code_resolved = NEXO_CODE.resolve()
|
|
3618
|
+
except Exception:
|
|
3619
|
+
runtime_core = dest / "core"
|
|
3620
|
+
code_resolved = NEXO_CODE
|
|
3621
|
+
|
|
3622
|
+
# Packaged/runtime-only installs resolve the launcher to ``~/.nexo/core``.
|
|
3623
|
+
# Those must use the packaged updater path instead of treating the managed
|
|
3624
|
+
# runtime itself as a mutable source repository. Only a recorded external
|
|
3625
|
+
# source repo in ``version.json`` should reactivate source-sync mode.
|
|
3626
|
+
if code_resolved == runtime_core:
|
|
3627
|
+
version_source = _runtime_version_source()
|
|
3628
|
+
if version_source:
|
|
3629
|
+
return version_source / "src", version_source
|
|
3630
|
+
return None, None
|
|
3631
|
+
|
|
3615
3632
|
try:
|
|
3616
3633
|
same_as_runtime = NEXO_CODE.resolve() == dest.resolve()
|
|
3617
3634
|
except Exception:
|
|
@@ -3798,6 +3815,31 @@ def _backup_runtime_tree(dest: Path = NEXO_HOME) -> str:
|
|
|
3798
3815
|
return str(backup_dir)
|
|
3799
3816
|
|
|
3800
3817
|
|
|
3818
|
+
def _remove_runtime_copy_target(target: Path) -> None:
|
|
3819
|
+
"""Remove a runtime copy destination, handling symlinks explicitly.
|
|
3820
|
+
|
|
3821
|
+
F0.6 installs keep compatibility shims such as ``~/.nexo/db ->
|
|
3822
|
+
~/.nexo/core/db``. ``shutil.rmtree(..., ignore_errors=True)`` does not
|
|
3823
|
+
remove those symlinks, which makes subsequent ``copytree(...)`` calls fail
|
|
3824
|
+
with ``FileExistsError`` during ``nexo update``. This helper treats
|
|
3825
|
+
symlink/file targets differently from real directories so runtime sync and
|
|
3826
|
+
rollback can replace shim paths safely.
|
|
3827
|
+
"""
|
|
3828
|
+
import shutil
|
|
3829
|
+
|
|
3830
|
+
try:
|
|
3831
|
+
if target.is_symlink() or target.is_file():
|
|
3832
|
+
target.unlink()
|
|
3833
|
+
return
|
|
3834
|
+
if target.is_dir():
|
|
3835
|
+
shutil.rmtree(str(target), ignore_errors=True)
|
|
3836
|
+
return
|
|
3837
|
+
if target.exists():
|
|
3838
|
+
target.unlink()
|
|
3839
|
+
except FileNotFoundError:
|
|
3840
|
+
return
|
|
3841
|
+
|
|
3842
|
+
|
|
3801
3843
|
def _restore_runtime_tree(backup_dir: str, dest: Path = NEXO_HOME) -> None:
|
|
3802
3844
|
import shutil
|
|
3803
3845
|
|
|
@@ -3807,11 +3849,11 @@ def _restore_runtime_tree(backup_dir: str, dest: Path = NEXO_HOME) -> None:
|
|
|
3807
3849
|
for item in bdir.iterdir():
|
|
3808
3850
|
target = dest / item.name
|
|
3809
3851
|
if item.is_dir():
|
|
3810
|
-
|
|
3811
|
-
shutil.rmtree(target, ignore_errors=True)
|
|
3852
|
+
_remove_runtime_copy_target(target)
|
|
3812
3853
|
shutil.copytree(str(item), str(target))
|
|
3813
3854
|
else:
|
|
3814
3855
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
3856
|
+
_remove_runtime_copy_target(target)
|
|
3815
3857
|
shutil.copy2(str(item), str(target))
|
|
3816
3858
|
|
|
3817
3859
|
|
|
@@ -3834,8 +3876,7 @@ def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_H
|
|
|
3834
3876
|
pkg_src = src_dir / pkg
|
|
3835
3877
|
pkg_dest = dest / pkg
|
|
3836
3878
|
if pkg_src.is_dir():
|
|
3837
|
-
|
|
3838
|
-
shutil.rmtree(str(pkg_dest), ignore_errors=True)
|
|
3879
|
+
_remove_runtime_copy_target(pkg_dest)
|
|
3839
3880
|
shutil.copytree(
|
|
3840
3881
|
str(pkg_src),
|
|
3841
3882
|
str(pkg_dest),
|
|
@@ -3847,30 +3888,37 @@ def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_H
|
|
|
3847
3888
|
for name in flat_files:
|
|
3848
3889
|
src_file = src_dir / name
|
|
3849
3890
|
if src_file.is_file():
|
|
3850
|
-
|
|
3891
|
+
target = dest / name
|
|
3892
|
+
_remove_runtime_copy_target(target)
|
|
3893
|
+
shutil.copy2(str(src_file), str(target))
|
|
3851
3894
|
copied_files += 1
|
|
3852
3895
|
|
|
3853
3896
|
_emit_progress(progress_fn, "Copying plugin modules...")
|
|
3854
3897
|
plugins_src = src_dir / "plugins"
|
|
3855
3898
|
plugins_dest = dest / "plugins"
|
|
3856
3899
|
if plugins_src.is_dir():
|
|
3900
|
+
if plugins_dest.is_symlink():
|
|
3901
|
+
_remove_runtime_copy_target(plugins_dest)
|
|
3857
3902
|
plugins_dest.mkdir(parents=True, exist_ok=True)
|
|
3858
3903
|
for item in plugins_src.iterdir():
|
|
3859
3904
|
if item.is_file() and item.suffix == ".py" and not is_duplicate_artifact_name(item):
|
|
3860
|
-
|
|
3905
|
+
target = plugins_dest / item.name
|
|
3906
|
+
_remove_runtime_copy_target(target)
|
|
3907
|
+
shutil.copy2(str(item), str(target))
|
|
3861
3908
|
|
|
3862
3909
|
_emit_progress(progress_fn, "Copying scripts...")
|
|
3863
3910
|
scripts_src = src_dir / "scripts"
|
|
3864
3911
|
scripts_dest = dest / "scripts"
|
|
3865
3912
|
if scripts_src.is_dir():
|
|
3913
|
+
if scripts_dest.is_symlink():
|
|
3914
|
+
_remove_runtime_copy_target(scripts_dest)
|
|
3866
3915
|
scripts_dest.mkdir(parents=True, exist_ok=True)
|
|
3867
3916
|
for item in scripts_src.iterdir():
|
|
3868
3917
|
if item.name == "__pycache__" or item.name.startswith(".") or is_duplicate_artifact_name(item):
|
|
3869
3918
|
continue
|
|
3870
3919
|
dst = scripts_dest / item.name
|
|
3871
3920
|
if item.is_dir():
|
|
3872
|
-
|
|
3873
|
-
shutil.rmtree(str(dst), ignore_errors=True)
|
|
3921
|
+
_remove_runtime_copy_target(dst)
|
|
3874
3922
|
shutil.copytree(str(item), str(dst), ignore=_runtime_copy_ignore())
|
|
3875
3923
|
elif item.is_file():
|
|
3876
3924
|
existing_class = installed_script_classes.get(item.name, "")
|
|
@@ -3899,25 +3947,37 @@ def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_H
|
|
|
3899
3947
|
templates_src = repo_dir / "templates"
|
|
3900
3948
|
templates_dest = dest / "templates"
|
|
3901
3949
|
if templates_src.is_dir():
|
|
3950
|
+
if templates_dest.is_symlink():
|
|
3951
|
+
_remove_runtime_copy_target(templates_dest)
|
|
3902
3952
|
templates_dest.mkdir(parents=True, exist_ok=True)
|
|
3903
3953
|
for item in templates_src.iterdir():
|
|
3904
3954
|
if item.name == "__pycache__" or is_duplicate_artifact_name(item):
|
|
3905
3955
|
continue
|
|
3906
3956
|
if item.is_file():
|
|
3907
|
-
|
|
3957
|
+
target = templates_dest / item.name
|
|
3958
|
+
_remove_runtime_copy_target(target)
|
|
3959
|
+
shutil.copy2(str(item), str(target))
|
|
3908
3960
|
elif item.is_dir():
|
|
3909
3961
|
sub_dest = templates_dest / item.name
|
|
3962
|
+
if sub_dest.is_symlink():
|
|
3963
|
+
_remove_runtime_copy_target(sub_dest)
|
|
3910
3964
|
sub_dest.mkdir(parents=True, exist_ok=True)
|
|
3911
3965
|
for sub in item.iterdir():
|
|
3912
3966
|
if sub.is_file() and not is_duplicate_artifact_name(sub):
|
|
3913
|
-
|
|
3967
|
+
target = sub_dest / sub.name
|
|
3968
|
+
_remove_runtime_copy_target(target)
|
|
3969
|
+
shutil.copy2(str(sub), str(target))
|
|
3914
3970
|
|
|
3915
3971
|
package_json = repo_dir / "package.json"
|
|
3916
3972
|
if package_json.is_file():
|
|
3917
|
-
|
|
3973
|
+
package_target = dest / "package.json"
|
|
3974
|
+
_remove_runtime_copy_target(package_target)
|
|
3975
|
+
shutil.copy2(str(package_json), str(package_target))
|
|
3918
3976
|
try:
|
|
3919
3977
|
pkg = json.loads(package_json.read_text())
|
|
3920
|
-
|
|
3978
|
+
version_target = dest / "version.json"
|
|
3979
|
+
_remove_runtime_copy_target(version_target)
|
|
3980
|
+
version_target.write_text(json.dumps({
|
|
3921
3981
|
"version": pkg.get("version", "?"),
|
|
3922
3982
|
"source": str(repo_dir),
|
|
3923
3983
|
}, indent=2))
|
|
@@ -3928,8 +3988,7 @@ def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_H
|
|
|
3928
3988
|
skills_src = src_dir / "skills"
|
|
3929
3989
|
skills_dest = dest / "skills-core"
|
|
3930
3990
|
if skills_src.is_dir():
|
|
3931
|
-
|
|
3932
|
-
shutil.rmtree(str(skills_dest), ignore_errors=True)
|
|
3991
|
+
_remove_runtime_copy_target(skills_dest)
|
|
3933
3992
|
shutil.copytree(str(skills_src), str(skills_dest), ignore=_runtime_copy_ignore())
|
|
3934
3993
|
|
|
3935
3994
|
bin_dir = dest / "bin"
|