ocerebro 0.4.7 → 0.4.9

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.
@@ -101,10 +101,11 @@ def find_claude_desktop_config() -> Path | None:
101
101
  def get_ocerebro_path() -> Path:
102
102
  """Retorna o caminho absoluto do OCerebro instalado"""
103
103
  # Tenta encontrar o package instalado via pip
104
+ # NOTA: O package se chama 'ocerebro' mas o modulo interno e 'cerebro'
104
105
  try:
105
- import cerebro
106
- cerebro_path = Path(cerebro.__file__).parent
107
- return cerebro_path.resolve()
106
+ import ocerebro
107
+ ocerebro_path = Path(ocerebro.__file__).parent
108
+ return ocerebro_path.resolve()
108
109
  except ImportError:
109
110
  pass
110
111
 
@@ -132,16 +133,19 @@ def generate_mcp_config(ocerebro_path: Path) -> dict:
132
133
  SECURITY: Não salva API keys no config file.
133
134
  As variáveis de ambiente são herdadas do sistema.
134
135
  Configure no seu shell: ~/.bashrc ou ~/.zshrc
136
+
137
+ WINDOWS FIX: Paths com espaços são tratados corretamente.
135
138
  """
136
139
 
137
- # Determina o comando Python
140
+ # Determina o comando Python (path completo para evitar issues no Windows)
138
141
  python_cmd = sys.executable
139
142
 
140
- # Estratégia 1: usa python -m ocerebro.mcp (robusto para pip install)
143
+ # Estratégia 1: usa python -m src.mcp.server (robusto para pip install)
144
+ # NOTA: O cwd precisa ser path absoluto para evitar issues com paths relativos
141
145
  mcp_config = {
142
146
  "command": python_cmd,
143
147
  "args": ["-m", "src.mcp.server"],
144
- "cwd": str(ocerebro_path.parent),
148
+ "cwd": str(ocerebro_path.parent.resolve()), # resolve() para path absoluto
145
149
  }
146
150
 
147
151
  # Estratégia 2: path direto se arquivo existe
@@ -150,7 +154,8 @@ def generate_mcp_config(ocerebro_path: Path) -> dict:
150
154
  mcp_server = ocerebro_path.parent / "src" / "mcp" / "server.py"
151
155
 
152
156
  if mcp_server.exists():
153
- mcp_config["args"] = [str(mcp_server)]
157
+ # Usa path absoluto e resolved para spaces no Windows
158
+ mcp_config["args"] = [str(mcp_server.resolve())]
154
159
 
155
160
  # SECURITY: NÃO salvar API keys no config
156
161
  mcp_config["env"] = {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ocerebro",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "OCerebro - Sistema de Memoria para Agentes (Claude Code/MCP)",
5
5
  "main": "bin/ocerebro.js",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ocerebro"
7
- version = "0.4.7"
7
+ version = "0.4.9"
8
8
  description = "OCerebro - Sistema de Memoria para Agentes (Claude Code/MCP)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -72,6 +72,9 @@ ocerebro-setup = "cerebro.cerebro_setup:main"
72
72
  where = ["."]
73
73
  include = ["src*", "cerebro*"]
74
74
 
75
+ [tool.setuptools.package-data]
76
+ "src.dashboard" = ["static/*", "static/**/*"]
77
+
75
78
  [tool.pytest.ini_options]
76
79
  testpaths = ["tests"]
77
80
  python_files = "test_*.py"
@@ -5,6 +5,7 @@ import threading
5
5
  import webbrowser
6
6
  from pathlib import Path
7
7
  from typing import Any, Optional
8
+ import importlib.resources
8
9
 
9
10
  from fastapi import FastAPI
10
11
  from fastapi.middleware.cors import CORSMiddleware
@@ -44,6 +45,16 @@ class DashboardServer:
44
45
  self.embeddings_db = embeddings_db
45
46
  self.entities_db = entities_db
46
47
 
48
+ # Usa importlib.resources para encontrar static files corretamente
49
+ # Funciona tanto em desenvolvimento quanto em pacote instalado
50
+ try:
51
+ # Python 3.9+
52
+ static_path = importlib.resources.files("src.dashboard") / "static"
53
+ self._static_path = Path(str(static_path))
54
+ except AttributeError:
55
+ # Fallback para Python < 3.9
56
+ self._static_path = Path(__file__).parent / "static"
57
+
47
58
  self.app = self._create_app()
48
59
  self._server_thread: Optional[threading.Thread] = None
49
60
  self._port: Optional[int] = None
@@ -66,9 +77,8 @@ class DashboardServer:
66
77
  )
67
78
 
68
79
  # Monta static files
69
- static_path = Path(__file__).parent / "static"
70
- if static_path.exists():
71
- app.mount("/static", StaticFiles(directory=str(static_path)), name="static")
80
+ if self._static_path.exists():
81
+ app.mount("/static", StaticFiles(directory=str(self._static_path)), name="static")
72
82
 
73
83
  # Monta API router
74
84
  from src.dashboard.api import create_router
@@ -84,10 +94,10 @@ class DashboardServer:
84
94
  @app.get("/")
85
95
  async def root():
86
96
  from fastapi.responses import FileResponse
87
- index_path = static_path / "index.html"
97
+ index_path = self._static_path / "index.html"
88
98
  if index_path.exists():
89
99
  return FileResponse(str(index_path))
90
- return {"error": "index.html not found"}
100
+ return {"error": "index.html not found", "path": str(index_path)}
91
101
 
92
102
  return app
93
103
 
@@ -79,6 +79,7 @@ class HooksLoader:
79
79
  Carrega módulo Python dinamicamente.
80
80
 
81
81
  SECURITY FIX: Valida path para evitar path traversal
82
+ WINDOWS FIX: Suporte a paths com espaços
82
83
 
83
84
  Args:
84
85
  module_path: Path do módulo relativo ao projeto
@@ -93,6 +94,7 @@ class HooksLoader:
93
94
  if module_path in self._loaded_modules:
94
95
  return self._loaded_modules[module_path]
95
96
 
97
+ # WINDOWS FIX: Usa resolve() para paths absolutos com espaços
96
98
  # SECURITY: Resolve path absoluto e verifica se está dentro do diretório permitido
97
99
  path = Path(module_path).resolve()
98
100
  allowed_root = self.config_path.parent.resolve()
@@ -113,9 +115,10 @@ class HooksLoader:
113
115
  if path.suffix != ".py":
114
116
  raise ValueError(f"Hook deve ser arquivo .py: {path}")
115
117
 
118
+ # WINDOWS FIX: Usa str(path) em vez de path direto para evitar issues
116
119
  spec = importlib.util.spec_from_file_location(
117
120
  f"hook_{path.stem}",
118
- path
121
+ str(path) # Converte para string para evitar issues no Windows
119
122
  )
120
123
 
121
124
  if spec is None or spec.loader is None:
package/src/mcp/server.py CHANGED
@@ -105,12 +105,17 @@ class CerebroMCP:
105
105
  self.raw_storage
106
106
  )
107
107
 
108
- # Inicializa hooks com tratamento de erro
109
- # WARN-04 FIX: hooks.yaml com erro não derruba o servidor
108
+ # Inicializa hooks APENAS se .ocerebro existir e hooks.yaml estiver presente
109
+ # FIX: Não carrega hooks em projetos sem .ocerebro
110
110
  hooks_config = self.cerebro_path.parent / "hooks.yaml"
111
111
  try:
112
- self.hooks_loader = HooksLoader(hooks_config) if hooks_config.exists() else None
113
- self.hooks_runner = HookRunner(self.hooks_loader) if self.hooks_loader else None
112
+ # Verifica se hooks.yaml existe antes de carregar
113
+ if hooks_config.exists():
114
+ self.hooks_loader = HooksLoader(hooks_config)
115
+ self.hooks_runner = HookRunner(self.hooks_loader)
116
+ else:
117
+ self.hooks_loader = None
118
+ self.hooks_runner = None
114
119
  except Exception as e:
115
120
  # WINDOWS FIX: Usa _safe_print_error para evitar UnicodeEncodeError
116
121
  _safe_print_error(f"[CEREBRO] Aviso: hooks.yaml com erro ({e}). Hooks desativados.")