zerochat 7.3.0__tar.gz → 7.5.0__tar.gz

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.
Files changed (138) hide show
  1. {zerochat-7.3.0/zerochat.egg-info → zerochat-7.5.0}/PKG-INFO +6 -4
  2. {zerochat-7.3.0 → zerochat-7.5.0}/README.md +5 -3
  3. {zerochat-7.3.0 → zerochat-7.5.0}/pyproject.toml +1 -2
  4. {zerochat-7.3.0 → zerochat-7.5.0/zerochat.egg-info}/PKG-INFO +6 -4
  5. zerochat-7.5.0/zerochat.egg-info/SOURCES.txt +9 -0
  6. zerochat-7.5.0/zerochat.egg-info/top_level.txt +1 -0
  7. {zerochat-7.3.0 → zerochat-7.5.0}/zerochat.py +74 -100
  8. zerochat-7.3.0/MANIFEST.in +0 -15
  9. zerochat-7.3.0/css/base.css +0 -199
  10. zerochat-7.3.0/css/components/composer.css +0 -1258
  11. zerochat-7.3.0/css/components/debug.css +0 -777
  12. zerochat-7.3.0/css/components/header.css +0 -97
  13. zerochat-7.3.0/css/components/icons.css +0 -39
  14. zerochat-7.3.0/css/components/markdown.css +0 -685
  15. zerochat-7.3.0/css/components/messages.css +0 -473
  16. zerochat-7.3.0/css/components/modals.css +0 -1319
  17. zerochat-7.3.0/css/components/sidebar.css +0 -622
  18. zerochat-7.3.0/css/components/tools.css +0 -1223
  19. zerochat-7.3.0/css/layout.css +0 -171
  20. zerochat-7.3.0/css/print.css +0 -120
  21. zerochat-7.3.0/css/styles.css +0 -18
  22. zerochat-7.3.0/css/theme-overrides.css +0 -555
  23. zerochat-7.3.0/css/tokens.css +0 -165
  24. zerochat-7.3.0/help/architecture.html +0 -247
  25. zerochat-7.3.0/help/composer.html +0 -134
  26. zerochat-7.3.0/help/conversations.html +0 -105
  27. zerochat-7.3.0/help/debug.html +0 -108
  28. zerochat-7.3.0/help/en/architecture.html +0 -247
  29. zerochat-7.3.0/help/en/composer.html +0 -134
  30. zerochat-7.3.0/help/en/conversations.html +0 -104
  31. zerochat-7.3.0/help/en/debug.html +0 -105
  32. zerochat-7.3.0/help/en/gemini-free.html +0 -39
  33. zerochat-7.3.0/help/en/index.html +0 -191
  34. zerochat-7.3.0/help/en/learning.html +0 -71
  35. zerochat-7.3.0/help/en/mcp.html +0 -143
  36. zerochat-7.3.0/help/en/openrouter-free.html +0 -39
  37. zerochat-7.3.0/help/en/profiles.html +0 -173
  38. zerochat-7.3.0/help/en/rag.html +0 -130
  39. zerochat-7.3.0/help/en/reasoning-telemetry.html +0 -108
  40. zerochat-7.3.0/help/en/tools-agent.html +0 -133
  41. zerochat-7.3.0/help/en/webllm.html +0 -355
  42. zerochat-7.3.0/help/gemini-free.html +0 -39
  43. zerochat-7.3.0/help/help.css +0 -604
  44. zerochat-7.3.0/help/help.js +0 -51
  45. zerochat-7.3.0/help/index.html +0 -194
  46. zerochat-7.3.0/help/learning.html +0 -71
  47. zerochat-7.3.0/help/mcp.html +0 -143
  48. zerochat-7.3.0/help/openrouter-free.html +0 -39
  49. zerochat-7.3.0/help/profiles.html +0 -174
  50. zerochat-7.3.0/help/rag.html +0 -130
  51. zerochat-7.3.0/help/reasoning-telemetry.html +0 -111
  52. zerochat-7.3.0/help/tools-agent.html +0 -133
  53. zerochat-7.3.0/help/webllm.html +0 -359
  54. zerochat-7.3.0/js/agent-core.js +0 -1553
  55. zerochat-7.3.0/js/api.js +0 -844
  56. zerochat-7.3.0/js/app.js +0 -2558
  57. zerochat-7.3.0/js/attachments.js +0 -220
  58. zerochat-7.3.0/js/charts.js +0 -338
  59. zerochat-7.3.0/js/chat-engine.js +0 -566
  60. zerochat-7.3.0/js/config-store.js +0 -207
  61. zerochat-7.3.0/js/context-manager.js +0 -584
  62. zerochat-7.3.0/js/conversation-service.js +0 -484
  63. zerochat-7.3.0/js/cookies.js +0 -688
  64. zerochat-7.3.0/js/data-reset-service.js +0 -136
  65. zerochat-7.3.0/js/debug.js +0 -508
  66. zerochat-7.3.0/js/defaults.js +0 -16
  67. zerochat-7.3.0/js/export.js +0 -179
  68. zerochat-7.3.0/js/file-parser.js +0 -2088
  69. zerochat-7.3.0/js/generation-controller.js +0 -417
  70. zerochat-7.3.0/js/i18n.js +0 -1483
  71. zerochat-7.3.0/js/icons.js +0 -156
  72. zerochat-7.3.0/js/ingestionEngine.js +0 -436
  73. zerochat-7.3.0/js/markdown.js +0 -588
  74. zerochat-7.3.0/js/mcp.js +0 -1271
  75. zerochat-7.3.0/js/message-turns.js +0 -62
  76. zerochat-7.3.0/js/profile-backup.js +0 -118
  77. zerochat-7.3.0/js/profile-export-bundle.js +0 -481
  78. zerochat-7.3.0/js/profile-repository.js +0 -213
  79. zerochat-7.3.0/js/providers-webllm.js +0 -486
  80. zerochat-7.3.0/js/providers.js +0 -1560
  81. zerochat-7.3.0/js/rag-index.js +0 -295
  82. zerochat-7.3.0/js/rag-service.js +0 -530
  83. zerochat-7.3.0/js/rag-ui.js +0 -997
  84. zerochat-7.3.0/js/ragStorage.js +0 -676
  85. zerochat-7.3.0/js/sandbox.js +0 -449
  86. zerochat-7.3.0/js/state.js +0 -704
  87. zerochat-7.3.0/js/storage-db.js +0 -144
  88. zerochat-7.3.0/js/tool-cards.js +0 -330
  89. zerochat-7.3.0/js/tool-security.js +0 -653
  90. zerochat-7.3.0/js/tools/README.md +0 -50
  91. zerochat-7.3.0/js/tools/builtin/agent-checkpoint.tool.js +0 -212
  92. zerochat-7.3.0/js/tools/builtin/download-pdf.tool.js +0 -111
  93. zerochat-7.3.0/js/tools/builtin/execute-javascript.tool.js +0 -219
  94. zerochat-7.3.0/js/tools/builtin/fetch-web-page.tool.js +0 -112
  95. zerochat-7.3.0/js/tools/builtin/list-documents.tool.js +0 -117
  96. zerochat-7.3.0/js/tools/builtin/read-knowledge-chunk.tool.js +0 -129
  97. zerochat-7.3.0/js/tools/builtin/read-knowledge-image.tool.js +0 -95
  98. zerochat-7.3.0/js/tools/builtin/render-chart.tool.js +0 -90
  99. zerochat-7.3.0/js/tools/builtin/search-knowledge-base.tool.js +0 -124
  100. zerochat-7.3.0/js/tools/builtin/search-web.tool.js +0 -125
  101. zerochat-7.3.0/js/tools/tool-manifest.js +0 -53
  102. zerochat-7.3.0/js/tools/tool-runtime.js +0 -77
  103. zerochat-7.3.0/js/ui-composer.js +0 -319
  104. zerochat-7.3.0/js/ui-conversation.js +0 -655
  105. zerochat-7.3.0/js/ui-dialogs.js +0 -132
  106. zerochat-7.3.0/js/ui-generation-status.js +0 -119
  107. zerochat-7.3.0/js/ui-inspector.js +0 -804
  108. zerochat-7.3.0/js/ui-mcp.js +0 -604
  109. zerochat-7.3.0/js/ui-profiles.js +0 -824
  110. zerochat-7.3.0/js/ui-reasoning.js +0 -146
  111. zerochat-7.3.0/js/ui-settings.js +0 -825
  112. zerochat-7.3.0/js/ui-shell.js +0 -215
  113. zerochat-7.3.0/js/ui-sidebar.js +0 -429
  114. zerochat-7.3.0/js/ui-telemetry.js +0 -404
  115. zerochat-7.3.0/js/ui-transfer.js +0 -262
  116. zerochat-7.3.0/js/utils.js +0 -216
  117. zerochat-7.3.0/js/vendor/orama.browser.js +0 -11
  118. zerochat-7.3.0/js/web-browser.js +0 -569
  119. zerochat-7.3.0/js/web-search.js +0 -519
  120. zerochat-7.3.0/manifest.webmanifest +0 -20
  121. zerochat-7.3.0/services/dummy_mcp/dummy_mcp_server.py +0 -47
  122. zerochat-7.3.0/services/dummy_mcp/service.json +0 -22
  123. zerochat-7.3.0/services/lsp/installer.json +0 -9
  124. zerochat-7.3.0/services/lsp/service.json +0 -22
  125. zerochat-7.3.0/services/memory/installer.json +0 -9
  126. zerochat-7.3.0/services/memory/service.json +0 -24
  127. zerochat-7.3.0/services/playwright/installer.json +0 -10
  128. zerochat-7.3.0/services/playwright/service.json +0 -39
  129. zerochat-7.3.0/setup.py +0 -42
  130. zerochat-7.3.0/sw.js +0 -164
  131. zerochat-7.3.0/zerochat.egg-info/SOURCES.txt +0 -134
  132. zerochat-7.3.0/zerochat.egg-info/top_level.txt +0 -2
  133. zerochat-7.3.0/zerochat.html +0 -671
  134. zerochat-7.3.0/zerochat_runtime/__init__.py +0 -1
  135. {zerochat-7.3.0 → zerochat-7.5.0}/LICENSE +0 -0
  136. {zerochat-7.3.0 → zerochat-7.5.0}/setup.cfg +0 -0
  137. {zerochat-7.3.0 → zerochat-7.5.0}/zerochat.egg-info/dependency_links.txt +0 -0
  138. {zerochat-7.3.0 → zerochat-7.5.0}/zerochat.egg-info/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zerochat
3
- Version: 7.3.0
3
+ Version: 7.5.0
4
4
  Summary: Private local web client for AI models, tools and MCP services
5
5
  Author: Alberto Balday
6
6
  License-Expression: MIT
@@ -41,13 +41,15 @@ Funciona de forma estática en el navegador (servido por GitHub Pages o directam
41
41
  ```bash
42
42
  python3 zerochat.py
43
43
  ```
44
- Inicia el asistente local, crea su entorno virtual de forma desatendida y abre la interfaz web en tu navegador.
44
+ Inicia el asistente local, crea `./zerochat/.venv` para los MCP y abre la interfaz web en tu navegador.
45
45
  - **Instalación con PyPI**:
46
46
  ```bash
47
- pipx install zerochat
47
+ pip install zerochat
48
48
  zerochat
49
49
  ```
50
- La distribución incluye una copia local de la interfaz. Las versiones `major.minor` indican compatibilidad del backend; los parches corresponden a la interfaz web.
50
+ Es la vía recomendada para instalar el servidor local desde PyPI. Igual que con la descarga directa, crea `./zerochat/` para el estado y los MCP; la interfaz se carga desde GitHub Pages.
51
+
52
+ En ambos modos, `./zerochat/` contiene configuración, token, servicios MCP y sus dependencias Python en `.venv/`. Borrar ese directorio elimina el estado local y los MCP, sin desinstalar el ejecutable de PyPI ni el archivo descargado.
51
53
 
52
54
  Las publicaciones se realizan desde el repositorio oficial mediante GitHub Actions y PyPI Trusted Publishing.
53
55
 
@@ -19,13 +19,15 @@ Funciona de forma estática en el navegador (servido por GitHub Pages o directam
19
19
  ```bash
20
20
  python3 zerochat.py
21
21
  ```
22
- Inicia el asistente local, crea su entorno virtual de forma desatendida y abre la interfaz web en tu navegador.
22
+ Inicia el asistente local, crea `./zerochat/.venv` para los MCP y abre la interfaz web en tu navegador.
23
23
  - **Instalación con PyPI**:
24
24
  ```bash
25
- pipx install zerochat
25
+ pip install zerochat
26
26
  zerochat
27
27
  ```
28
- La distribución incluye una copia local de la interfaz. Las versiones `major.minor` indican compatibilidad del backend; los parches corresponden a la interfaz web.
28
+ Es la vía recomendada para instalar el servidor local desde PyPI. Igual que con la descarga directa, crea `./zerochat/` para el estado y los MCP; la interfaz se carga desde GitHub Pages.
29
+
30
+ En ambos modos, `./zerochat/` contiene configuración, token, servicios MCP y sus dependencias Python en `.venv/`. Borrar ese directorio elimina el estado local y los MCP, sin desinstalar el ejecutable de PyPI ni el archivo descargado.
29
31
 
30
32
  Las publicaciones se realizan desde el repositorio oficial mediante GitHub Actions y PyPI Trusted Publishing.
31
33
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "zerochat"
7
- version = "7.3.0"
7
+ version = "7.5.0"
8
8
  description = "Private local web client for AI models, tools and MCP services"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -32,4 +32,3 @@ zerochat = "zerochat:main"
32
32
 
33
33
  [tool.setuptools]
34
34
  py-modules = ["zerochat"]
35
- packages = ["zerochat_runtime"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zerochat
3
- Version: 7.3.0
3
+ Version: 7.5.0
4
4
  Summary: Private local web client for AI models, tools and MCP services
5
5
  Author: Alberto Balday
6
6
  License-Expression: MIT
@@ -41,13 +41,15 @@ Funciona de forma estática en el navegador (servido por GitHub Pages o directam
41
41
  ```bash
42
42
  python3 zerochat.py
43
43
  ```
44
- Inicia el asistente local, crea su entorno virtual de forma desatendida y abre la interfaz web en tu navegador.
44
+ Inicia el asistente local, crea `./zerochat/.venv` para los MCP y abre la interfaz web en tu navegador.
45
45
  - **Instalación con PyPI**:
46
46
  ```bash
47
- pipx install zerochat
47
+ pip install zerochat
48
48
  zerochat
49
49
  ```
50
- La distribución incluye una copia local de la interfaz. Las versiones `major.minor` indican compatibilidad del backend; los parches corresponden a la interfaz web.
50
+ Es la vía recomendada para instalar el servidor local desde PyPI. Igual que con la descarga directa, crea `./zerochat/` para el estado y los MCP; la interfaz se carga desde GitHub Pages.
51
+
52
+ En ambos modos, `./zerochat/` contiene configuración, token, servicios MCP y sus dependencias Python en `.venv/`. Borrar ese directorio elimina el estado local y los MCP, sin desinstalar el ejecutable de PyPI ni el archivo descargado.
51
53
 
52
54
  Las publicaciones se realizan desde el repositorio oficial mediante GitHub Actions y PyPI Trusted Publishing.
53
55
 
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ zerochat.py
5
+ zerochat.egg-info/PKG-INFO
6
+ zerochat.egg-info/SOURCES.txt
7
+ zerochat.egg-info/dependency_links.txt
8
+ zerochat.egg-info/entry_points.txt
9
+ zerochat.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ zerochat
@@ -16,7 +16,6 @@ import atexit
16
16
  import datetime
17
17
  import hmac
18
18
  import importlib.metadata
19
- import importlib.resources
20
19
  import json
21
20
  import os
22
21
  import platform
@@ -38,6 +37,8 @@ import webbrowser
38
37
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
39
38
  from pathlib import Path
40
39
 
40
+ SOURCE_BACKEND_VERSION = "7.5.0"
41
+
41
42
  def _read_source_version(filename: str) -> str | None:
42
43
  """Lee la versión de un archivo del repositorio cuando se ejecuta desde fuentes."""
43
44
  try:
@@ -54,10 +55,13 @@ def _read_source_version(filename: str) -> str | None:
54
55
 
55
56
  def _read_backend_version() -> str:
56
57
  """Devuelve la versión publicada del backend, sin depender de package.json instalado."""
58
+ source_version = _read_source_version("pyproject.toml")
59
+ if source_version:
60
+ return source_version
57
61
  try:
58
62
  return importlib.metadata.version("zerochat")
59
63
  except importlib.metadata.PackageNotFoundError:
60
- return _read_source_version("pyproject.toml") or "7.3.0"
64
+ return SOURCE_BACKEND_VERSION
61
65
 
62
66
 
63
67
  def _read_ui_version() -> str:
@@ -85,6 +89,7 @@ DEFAULT_PORT = 6388
85
89
  DEFAULT_HOST = "127.0.0.1"
86
90
  DEFAULT_UI_URL = "https://albalday.github.io/zerochat/zerochat.html"
87
91
  REMOTE_VERSION_URL = "https://raw.githubusercontent.com/albalday/zerochat/master/package.json"
92
+ PYPI_VERSION_URL = "https://pypi.org/pypi/zerochat/json"
88
93
  REMOTE_SCRIPT_URL = "https://raw.githubusercontent.com/albalday/zerochat/master/zerochat.py"
89
94
  CONSOLE_STATUS_IDLE_SECONDS = 8.0
90
95
  CONSOLE_CONTROL = None
@@ -229,18 +234,6 @@ def console_log(message: str, *, flush: bool = True):
229
234
  else:
230
235
  print(message, flush=flush)
231
236
 
232
- def get_packaged_assets_root() -> Path | None:
233
- """Devuelve los recursos incluidos en la distribución PyPI, si existen."""
234
- try:
235
- root = importlib.resources.files("zerochat_runtime").joinpath("assets")
236
- path = Path(str(root))
237
- if (path / "zerochat.html").is_file():
238
- return path
239
- except (ModuleNotFoundError, TypeError):
240
- pass
241
- return None
242
-
243
-
244
237
  def get_dev_root() -> Path | None:
245
238
  """
246
239
  Detecta si zerochat.py se está ejecutando en el directorio de desarrollo del repositorio.
@@ -255,34 +248,36 @@ def get_dev_root() -> Path | None:
255
248
 
256
249
 
257
250
  def get_static_root() -> Path | None:
258
- """Prioriza el árbol de desarrollo y usa los recursos empaquetados fuera de él."""
259
- return get_dev_root() or get_packaged_assets_root()
260
-
261
-
262
- def is_packaged_runtime() -> bool:
263
- return get_dev_root() is None and get_packaged_assets_root() is not None
251
+ """La interfaz local solo se sirve al ejecutar el repositorio de desarrollo."""
252
+ return get_dev_root()
264
253
 
265
254
 
266
- def get_venv_dir() -> Path:
267
- """Devuelve el estado persistente; PyPI nunca crea un venv en el proyecto del usuario."""
255
+ def is_installed_runtime() -> bool:
256
+ """Identifica el ejecutable instalado desde PyPI, sin confundirlo con el repositorio."""
268
257
  if get_dev_root() is not None:
269
- return (Path.cwd() / "zerochat").resolve()
258
+ return False
259
+ try:
260
+ return importlib.metadata.version("zerochat") == BACKEND_PACKAGE_VERSION
261
+ except importlib.metadata.PackageNotFoundError:
262
+ return False
263
+
270
264
 
265
+ def get_data_dir() -> Path:
266
+ """Devuelve el directorio local que concentra el estado y los MCP de ZeroChat."""
271
267
  configured = os.environ.get("ZEROCHAT_DATA_DIR", "").strip()
272
268
  if configured:
273
269
  return Path(configured).expanduser().resolve()
274
- if sys.platform.startswith("win"):
275
- base = Path(os.environ.get("APPDATA", Path.home()))
276
- elif sys.platform == "darwin":
277
- base = Path.home() / "Library" / "Application Support"
278
- else:
279
- base = Path(os.environ.get("XDG_STATE_HOME", Path.home() / ".local" / "state"))
280
- return (base / "zerochat").resolve()
270
+ return (Path.cwd() / "zerochat").resolve()
271
+
272
+
273
+ def get_venv_dir() -> Path:
274
+ """Devuelve el entorno aislado usado exclusivamente por los MCP Python."""
275
+ return get_data_dir() / ".venv"
281
276
 
282
277
 
283
278
  def get_daily_token() -> str:
284
279
  """Devuelve un token de sesión diario persistido en ./zerochat/config/token.json."""
285
- config_dir = get_venv_dir() / "config"
280
+ config_dir = get_data_dir() / "config"
286
281
  config_dir.mkdir(parents=True, exist_ok=True)
287
282
  token_file = config_dir / "token.json"
288
283
  today = datetime.date.today().isoformat()
@@ -320,12 +315,10 @@ def get_venv_python(venv_dir: Path) -> Path:
320
315
 
321
316
  def ensure_virtual_environment():
322
317
  """
323
- Comprueba si existe el entorno virtual en ./zerochat. Si no existe, lo crea.
324
- Si el proceso actual no se está ejecutando bajo dicho entorno, se re-ejecuta.
318
+ Crea ./zerochat/.venv para dependencias MCP en ambos modos de distribución.
319
+ El servidor conserva el intérprete con el que fue iniciado; el entorno se usa
320
+ exclusivamente al lanzar procesos MCP Python.
325
321
  """
326
- if is_packaged_runtime():
327
- return
328
-
329
322
  venv_dir = get_venv_dir()
330
323
  venv_py = get_venv_python(venv_dir)
331
324
 
@@ -339,19 +332,6 @@ def ensure_virtual_environment():
339
332
  console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Advertencia al crear venv: {err}. Continuando con intérprete actual.", flush=True)
340
333
  return
341
334
 
342
- # 2. Comprobar si ya estamos ejecutándonos dentro del venv
343
- try:
344
- current_py = Path(sys.executable).resolve()
345
- target_py = venv_py.resolve()
346
- if current_py != target_py and target_py.exists():
347
- console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Re-ejecutando bajo {venv_py}...", flush=True)
348
- # Re-ejecutar con los mismos argumentos
349
- args = [str(target_py), str(Path(__file__).resolve())] + sys.argv[1:]
350
- os.execv(str(target_py), args)
351
- except Exception as err:
352
- console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Error en re-ejecución: {err}. Continuando.", flush=True)
353
-
354
-
355
335
  def parse_version(ver: str) -> tuple[int, ...]:
356
336
  """Convierte una cadena de versión semántica en tupla de enteros para comparación."""
357
337
  parts = []
@@ -362,29 +342,43 @@ def parse_version(ver: str) -> tuple[int, ...]:
362
342
  return tuple(parts)
363
343
 
364
344
 
345
+ def _read_remote_version(url: str) -> str | None:
346
+ """Obtiene una versión publicada desde package.json o la API JSON de PyPI."""
347
+ req = urllib.request.Request(url, headers={"User-Agent": f"ZeroChat/{VERSION}"})
348
+ with urllib.request.urlopen(req, timeout=3) as resp:
349
+ content = resp.read(4096).decode("utf-8", errors="ignore")
350
+ try:
351
+ data = json.loads(content)
352
+ if isinstance(data, dict):
353
+ candidate = data.get("version")
354
+ if not isinstance(candidate, str) and isinstance(data.get("info"), dict):
355
+ candidate = data["info"].get("version")
356
+ if isinstance(candidate, str):
357
+ return candidate.strip()
358
+ except (json.JSONDecodeError, TypeError):
359
+ pass
360
+ match = re.search(r'["\']?version["\']?\s*[:=]\s*["\'](\d+\.\d+\.\d+)["\']', content)
361
+ return match.group(1) if match else None
362
+
363
+
364
+ def has_new_backend_version(remote_version: str, local_version: str = VERSION) -> bool:
365
+ """Compara solo major.minor: los parches pertenecen a la interfaz web."""
366
+ return parse_version(compatibility_version(remote_version)) > parse_version(compatibility_version(local_version))
367
+
368
+
365
369
  def check_version():
366
- """Comprueba si hay una nueva versión de zerochat.py en el repositorio remoto (solo en modo producción/standalone)."""
367
- if get_dev_root() is not None or is_packaged_runtime():
370
+ """Informa de actualizaciones del backend, sin avisar por parches web."""
371
+ if get_dev_root() is not None:
368
372
  return
369
373
  try:
370
- req = urllib.request.Request(REMOTE_VERSION_URL, headers={"User-Agent": f"ZeroChat/{VERSION}"})
371
- with urllib.request.urlopen(req, timeout=3) as resp:
372
- content = resp.read(2048).decode("utf-8", errors="ignore")
373
- remote_ver = None
374
- try:
375
- data = json.loads(content)
376
- if isinstance(data, dict) and "version" in data and isinstance(data["version"], str):
377
- remote_ver = data["version"].strip()
378
- except Exception:
379
- pass
380
- if not remote_ver:
381
- match = re.search(r'["\']?version["\']?\s*[:=]\s*["\'](\d+\.\d+\.\d+)["\']', content)
382
- if match:
383
- remote_ver = match.group(1)
384
- if remote_ver and re.match(r"^\d+(\.\d+)+", remote_ver):
385
- if parse_version(remote_ver) > parse_version(VERSION):
386
- console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] ¡Nueva versión disponible! (Local: {VERSION}, Remota: {remote_ver})", flush=True)
387
- console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Actualiza con: curl -sSL {REMOTE_SCRIPT_URL} -o zerochat.py", flush=True)
374
+ installed = is_installed_runtime()
375
+ remote_ver = _read_remote_version(PYPI_VERSION_URL if installed else REMOTE_VERSION_URL)
376
+ if remote_ver and re.match(r"^\d+(\.\d+)+", remote_ver) and has_new_backend_version(remote_ver):
377
+ console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Nueva versión del servidor disponible (Local: {VERSION}, Remota: {compatibility_version(remote_ver)})", flush=True)
378
+ if installed:
379
+ console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Actualiza cuando quieras con: {sys.executable} -m pip install --upgrade zerochat", flush=True)
380
+ else:
381
+ console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Actualiza con: curl -sSL {REMOTE_SCRIPT_URL} -o zerochat.py", flush=True)
388
382
  except Exception:
389
383
  # Modo offline o timeout ignorado de forma segura
390
384
  pass
@@ -791,11 +785,11 @@ class McpServiceManager:
791
785
  else:
792
786
  candidates = [
793
787
  Path.cwd() / "services",
794
- get_venv_dir() / "services"
788
+ get_data_dir() / "services"
795
789
  ]
796
790
  self.services_root = candidates[0] if get_dev_root() is not None and candidates[0].is_dir() else candidates[1]
797
791
  self.services_root.mkdir(parents=True, exist_ok=True)
798
- self.config_file = get_venv_dir() / "config" / "services.json"
792
+ self.config_file = get_data_dir() / "config" / "services.json"
799
793
  self.config_file.parent.mkdir(parents=True, exist_ok=True)
800
794
  self.clients: dict[str, StdioMcpClient] = {}
801
795
  self.states: dict[str, str] = {}
@@ -805,28 +799,7 @@ class McpServiceManager:
805
799
  self.services = self._load_services()
806
800
  self.preferences = self._load_preferences()
807
801
 
808
- def _copy_packaged_service_templates(self):
809
- """Inicializa descriptores MCP editables sin copiar dependencias instaladas."""
810
- assets_root = get_packaged_assets_root()
811
- if not assets_root:
812
- return
813
- templates_root = assets_root / "services"
814
- if not templates_root.is_dir():
815
- return
816
- for source in templates_root.rglob("*"):
817
- if not source.is_file():
818
- continue
819
- relative = source.relative_to(templates_root)
820
- if any(part in {"node_modules", ".playwright-mcp"} or part.startswith(".") for part in relative.parts):
821
- continue
822
- target = self.services_root / relative
823
- if target.exists():
824
- continue
825
- target.parent.mkdir(parents=True, exist_ok=True)
826
- shutil.copy2(source, target)
827
-
828
802
  def _ensure_default_services(self):
829
- self._copy_packaged_service_templates()
830
803
  dummy_dir = self.services_root / "dummy_mcp"
831
804
  dummy_dir.mkdir(parents=True, exist_ok=True)
832
805
  service_json_file = dummy_dir / "service.json"
@@ -1138,7 +1111,7 @@ for raw in sys.stdin:
1138
1111
 
1139
1112
  return {
1140
1113
  "serviceDir": str(service_dir),
1141
- "pythonExecutable": sys.executable,
1114
+ "pythonExecutable": str(get_venv_python(get_venv_dir())) if get_venv_python(get_venv_dir()).is_file() else sys.executable,
1142
1115
  "nodeExecutable": node
1143
1116
  }
1144
1117
 
@@ -1350,7 +1323,7 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
1350
1323
  console_log(f"[{now}] <-- {status_text} {detail}{err_suffix} ({duration_ms:.1f}ms)", flush=True)
1351
1324
 
1352
1325
  def serve_static_file(self, rel_path: str) -> bool:
1353
- """Sirve los recursos web del repositorio o de la distribución instalada."""
1326
+ """Sirve recursos estáticos solo desde el repositorio de desarrollo."""
1354
1327
  static_root = get_static_root()
1355
1328
  if not static_root:
1356
1329
  return False
@@ -1890,7 +1863,7 @@ def main():
1890
1863
  parser.add_argument("--ui-url", default=None, help="URL de la interfaz web a abrir (por defecto: interfaz local en desarrollo o GitHub Pages)")
1891
1864
  parser.add_argument("--no-browser", action="store_true", help="No abrir automáticamente el navegador")
1892
1865
  parser.add_argument("--no-exit-on-close", action="store_true", help="No detener el servidor automáticamente al cerrar el navegador")
1893
- parser.add_argument("--no-venv", action="store_true", help="Omitir la comprobación/creación del venv ./zerochat")
1866
+ parser.add_argument("--no-venv", action="store_true", help="Omitir la comprobación/creación de ./zerochat/.venv")
1894
1867
  parser.add_argument("--test", action="store_true", help="Ejecutar autocomprobación interna de herramientas")
1895
1868
  parser.add_argument("--version", action="version", version=f"ZeroChat {VERSION}")
1896
1869
  args = parser.parse_args()
@@ -1902,7 +1875,7 @@ def main():
1902
1875
  print(f"[{time.strftime('%H:%M:%S')}] TEST all local tools ready.")
1903
1876
  return
1904
1877
 
1905
- # 1. Asegurar entorno virtual ./zerochat solo cuando se ejecuta desde fuentes.
1878
+ # 1. Asegurar el entorno MCP aislado en ambos modos de distribución.
1906
1879
  if not args.no_venv:
1907
1880
  ensure_virtual_environment()
1908
1881
 
@@ -1910,7 +1883,7 @@ def main():
1910
1883
  dev_root = get_dev_root()
1911
1884
  static_root = get_static_root()
1912
1885
  is_dev = dev_root is not None
1913
- is_packaged = is_packaged_runtime()
1886
+ is_installed = is_installed_runtime()
1914
1887
 
1915
1888
  # 3. Comprobar versión remota en segundo plano (solo fuera del entorno de desarrollo local)
1916
1889
  if not is_dev:
@@ -1939,11 +1912,12 @@ def main():
1939
1912
  print("=" * 64)
1940
1913
  print(f" ZeroChat Local Server v{VERSION} (UI {UI_VERSION})")
1941
1914
  print(f" Directorio de trabajo : {Path.cwd()}")
1942
- print(f" Entorno virtual : {get_venv_dir()}")
1915
+ print(f" Datos y MCP : {get_data_dir()}")
1916
+ print(f" Entorno MCP : {get_venv_dir()}")
1943
1917
  if is_dev:
1944
1918
  print(f" Modo de ejecución : Desarrollo local ({dev_root})")
1945
- elif is_packaged:
1946
- print(" Modo de ejecución : Paquete PyPI (interfaz local)")
1919
+ elif is_installed:
1920
+ print(" Modo de ejecución : Paquete PyPI (GitHub Pages)")
1947
1921
  else:
1948
1922
  print(f" Modo de ejecución : Producción (Web universal)")
1949
1923
  print(f" Servidor HTTP/SSE : http://{ACTIVE_HOST}:{ACTIVE_PORT}")
@@ -1,15 +0,0 @@
1
- include LICENSE
2
- include README.md
3
- include zerochat.html
4
- include manifest.webmanifest
5
- include sw.js
6
- recursive-include js *
7
- recursive-include css *
8
- recursive-include help *
9
- recursive-include services *.json *.py
10
- prune services/*/node_modules
11
- prune services/*/.playwright-mcp
12
- exclude services/*/.installed.json
13
- exclude services/*/package.json
14
- exclude services/*/package-lock.json
15
- exclude services/*/*.jsonl
@@ -1,199 +0,0 @@
1
- /* ==========================================================================
2
- ZeroChat Base Styles - Reseteo, Normalización, Animaciones y Accesibilidad
3
- ========================================================================== */
4
- * {
5
- box-sizing: border-box;
6
- margin: 0;
7
- padding: 0;
8
- }
9
-
10
- img {
11
- max-width: 100%;
12
- height: auto;
13
- }
14
-
15
- html, body {
16
- height: 100%;
17
- height: 100dvh;
18
- height: var(--app-height, 100dvh);
19
- min-height: -webkit-fill-available;
20
- font-family: var(--font-sans);
21
- background-color: var(--bg-app);
22
- color: var(--text-main);
23
- line-height: 1.5;
24
- -webkit-font-smoothing: antialiased;
25
- -moz-osx-font-smoothing: grayscale;
26
- }
27
-
28
- body {
29
- display: flex;
30
- flex-direction: row;
31
- height: 100%;
32
- height: 100dvh;
33
- height: var(--app-height, 100dvh);
34
- width: 100%;
35
- width: 100vw;
36
- overflow: hidden;
37
- position: relative;
38
- }
39
-
40
- /* ==========================================================================
41
- Accesibilidad de Foco (WCAG 2.1 AA)
42
- ========================================================================== */
43
- :focus-visible {
44
- outline: 2px solid var(--primary);
45
- outline-offset: 2px;
46
- }
47
-
48
- button:focus-visible,
49
- a:focus-visible,
50
- summary:focus-visible {
51
- outline: 2px solid var(--primary);
52
- outline-offset: 2px;
53
- }
54
-
55
- input:focus,
56
- input:focus-visible,
57
- textarea:focus,
58
- textarea:focus-visible,
59
- select:focus,
60
- select:focus-visible {
61
- outline: none;
62
- }
63
-
64
- .chat-textarea,
65
- .chat-textarea:focus,
66
- .chat-textarea:focus-visible {
67
- outline: none !important;
68
- box-shadow: none !important;
69
- border: none !important;
70
- }
71
-
72
- /* ==========================================================================
73
- Barras de Desplazamiento (Scrollbars) Sutiles
74
- ========================================================================== */
75
- ::-webkit-scrollbar {
76
- width: 6px;
77
- height: 6px;
78
- }
79
-
80
- ::-webkit-scrollbar-track {
81
- background: transparent;
82
- }
83
-
84
- ::-webkit-scrollbar-thumb {
85
- background: var(--scrollbar-thumb);
86
- border-radius: var(--radius-full);
87
- }
88
-
89
- ::-webkit-scrollbar-thumb:hover {
90
- background: var(--scrollbar-thumb-hover);
91
- }
92
-
93
- /* ==========================================================================
94
- Animaciones del Sistema
95
- ========================================================================== */
96
- @keyframes spin {
97
- from { transform: rotate(0deg); }
98
- to { transform: rotate(360deg); }
99
- }
100
-
101
- /* slideUpFadeIn: solo se aplica a mensajes nuevos via .is-new-message (nunca al historial) */
102
- @keyframes slideUpFadeIn {
103
- from { opacity: 0; translate: 0 6px; }
104
- to { opacity: 1; translate: 0 0; }
105
- }
106
-
107
- /* typingBounce: para el indicador de escritura del asistente */
108
- @keyframes typingBounce {
109
- 0%, 80%, 100% { transform: scale(1); opacity: 0.4; }
110
- 40% { transform: scale(1.3); opacity: 1; }
111
- }
112
-
113
- /* pulseStop: mantener para btn-stop-stream */
114
-
115
-
116
- /* La interfaz responde inmediatamente: no se aplican animaciones ni transiciones. */
117
- *,
118
- *::before,
119
- *::after {
120
- animation: none !important;
121
- transition: none !important;
122
- }
123
-
124
- /* ==========================================================================
125
- Utilidades de Accesibilidad (WCAG 2.1 AA)
126
- ========================================================================== */
127
- .sr-only {
128
- position: absolute;
129
- width: 1px;
130
- height: 1px;
131
- padding: 0;
132
- margin: -1px;
133
- overflow: hidden;
134
- clip: rect(0, 0, 0, 0);
135
- white-space: nowrap;
136
- border-width: 0;
137
- }
138
-
139
- /* ==========================================================================
140
- Fundación UI y Consolidación de Clases Base
141
- ========================================================================== */
142
- .btn-primary,
143
- .btn-secondary,
144
- .btn-close,
145
- .btn-attach,
146
- .btn-send,
147
- .btn-stop-stream,
148
- .btn-reasoning,
149
- .btn-query-server,
150
- .btn-profile-action,
151
- .btn-mcp-action,
152
- .btn-debug-action,
153
- .btn-danger-outline,
154
- .modal-btn-action,
155
- .modal-btn-close,
156
- .export-card-btn {
157
- font-family: inherit;
158
- cursor: pointer;
159
- }
160
-
161
- .btn-primary,
162
- .btn-secondary,
163
- .btn-close,
164
- .btn-query-server,
165
- .btn-profile-action,
166
- .btn-mcp-action,
167
- .btn-danger-outline {
168
- display: inline-flex;
169
- align-items: center;
170
- justify-content: center;
171
- border-radius: var(--radius-md);
172
- font-weight: 500;
173
- transition: all 0.15s ease;
174
- }
175
-
176
- .chat-chart-card,
177
- .inspector-intro-card,
178
- .setting-toggle-card,
179
- .connection-active-card,
180
- .rag-branch-details-card,
181
- .rag-active-tip-card,
182
- .mcp-status-card,
183
- .mcp-config-card,
184
- .mcp-instructions-card,
185
- .mcp-tool-card {
186
- background-color: var(--bg-surface);
187
- border: 1px solid var(--border-color);
188
- border-radius: var(--radius-md);
189
- }
190
-
191
- .chat-chart-card,
192
- .inspector-intro-card,
193
- .mcp-status-card,
194
- .mcp-config-card,
195
- .mcp-instructions-card {
196
- border-radius: var(--radius-lg);
197
- padding: 1rem;
198
- box-shadow: var(--shadow-sm);
199
- }