zerochat 7.11.0__tar.gz → 8.0.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.
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: zerochat
3
+ Version: 8.0.0
4
+ Summary: Private local web client for AI models, tools and MCP services
5
+ Author: Alberto Alday
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://albalday.github.io/zerochat/
8
+ Project-URL: Repository, https://github.com/albalday/zerochat
9
+ Project-URL: Issues, https://github.com/albalday/zerochat/issues
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Dynamic: license-file
22
+
23
+ # ZeroChat
24
+
25
+ ZeroChat es un cliente web de uso personal para conversar con modelos de IA, consultar documentos y utilizar herramientas. Esta documentación corresponde a la serie **8.x**, que consolida las funcionalidades existentes. El desarrollo de nuevas funcionalidades queda en pausa por el momento.
26
+
27
+ La interfaz se sirve como archivos estáticos: `zerochat.html` necesita sus módulos `js/` y `css/`. El historial y la configuración se guardan en el navegador; no se sincronizan automáticamente entre dispositivos. Los proveedores remotos reciben el contenido incluido en las peticiones, y las herramientas web pueden utilizar servicios intermediarios. Almacenamiento local no significa privacidad absoluta.
28
+
29
+ *English:* ZeroChat is a web client for personal use: chatting with AI models, consulting documents and using tools. This documentation covers the **8.x** series, which consolidates existing features. New feature development is paused for now.
30
+
31
+ The interface is served as static files: `zerochat.html` needs its `js/` and `css/` modules. History and settings are stored in the browser, without automatic cross-device sync. Remote providers receive content included in requests, and web tools may use intermediary services. Local storage does not mean absolute privacy.
32
+
33
+ ## Características principales / Main features
34
+
35
+ - **Perfiles:** OpenAI y APIs de chat compatibles, Ollama, OpenRouter, Claude y Gemini. La visión, las herramientas y el razonamiento dependen del modelo y del adaptador.
36
+ - **WebLLM:** inferencia en el navegador con WebGPU, tras descargar un modelo. Necesita hardware compatible y memoria suficiente.
37
+ - **Documentos:** adjuntos y colecciones RAG con extracción local y búsqueda léxica mediante Orama; sin embeddings. Los fragmentos recuperados se envían al modelo elegido. La extracción de PDF tiene límites y no incluye OCR general.
38
+ - **Herramientas:** JavaScript aislado, búsqueda y lectura web, gráficos, planes y puntos de control. El backend opcional añade archivos, comandos, diagnósticos, automatización de navegador y servicios MCP.
39
+ - **Historial y diagnóstico:** ramas de conversación, exportación JSON y Markdown, impresión a PDF, métricas de contexto y revisión de peticiones antes del envío.
40
+
41
+ *English:*
42
+
43
+ - **Profiles:** OpenAI and compatible chat APIs, Ollama, OpenRouter, Claude and Gemini. Vision, tools and reasoning depend on the model and adapter.
44
+ - **WebLLM:** browser inference through WebGPU after downloading a model. Requires compatible hardware and enough memory.
45
+ - **Documents:** attachments and RAG collections with local extraction and Orama lexical search, without embeddings. Retrieved passages are sent to the selected model. PDF extraction has limitations and no general OCR.
46
+ - **Tools:** isolated JavaScript, web search and retrieval, charts, plans and checkpoints. The optional backend adds files, commands, diagnostics, browser automation and MCP services.
47
+ - **History and diagnostics:** conversation branches, JSON and Markdown export, printing to PDF, context metrics and request inspection before sending.
48
+
49
+ ## Inicio rápido / Quick start
50
+
51
+ Abre [ZeroChat en GitHub Pages](https://albalday.github.io/zerochat/zerochat.html), crea un perfil, introduce la URL y la clave si corresponde, consulta los modelos con **Query** y guarda. El perfil **Espejo** permite probar la interfaz sin inferencia. Para WebLLM, prepara un modelo del catálogo.
52
+
53
+ *English:* Open [ZeroChat on GitHub Pages](https://albalday.github.io/zerochat/zerochat.html), create a profile, enter the URL and key where required, query models with **Query**, and save. **Mirror** lets you try the interface without inference. For WebLLM, prepare a catalogue model.
54
+
55
+ Para añadir herramientas del equipo, instala y ejecuta el backend con Python. / To add computer tools, install and run the Python backend:
56
+
57
+ ```bash
58
+ pip install zerochat
59
+ zerochat
60
+ ```
61
+
62
+ También puedes descargar el script y ejecutarlo. / You can also download and run the script:
63
+
64
+ ```bash
65
+ curl -sL https://albalday.github.io/zerochat/zerochat.py -o zerochat.py
66
+ python3 zerochat.py
67
+ ```
68
+
69
+ Fuera del repositorio de desarrollo, ambos modos abren la interfaz de GitHub Pages. Crean `~/zerochat/` para configuración y servicios, con `.venv/` para dependencias MCP Python. Mantén el proceso abierto y usa su enlace de arranque para autenticar la conexión local. Revisa **Ajustes → MCP** y **Permisos** antes de habilitar herramientas: los comandos actúan con los permisos del proceso local y detenerlos no deshace cambios.
70
+
71
+ Borrar `~/zerochat/` elimina ese estado y los MCP gestionados, no el ejecutable, los archivos modificados fuera de esa carpeta ni los datos del navegador. Exporta las conversaciones y perfiles que quieras conservar; sus copias no incluyen las colecciones RAG ni los modelos descargados. El cifrado predeterminado de perfiles utiliza una clave integrada conocida: trata las copias como archivos sensibles.
72
+
73
+ *English:* Outside the development repository, both modes open the GitHub Pages interface. They create `~/zerochat/` for configuration and services, with `.venv/` for Python MCP dependencies. Keep the process running and use its startup link to authenticate the local connection. Review **Settings → MCP** and **Permissions** before enabling tools: commands run with the local process permissions, and stopping them does not undo changes.
74
+
75
+ Deleting `~/zerochat/` removes that state and managed MCP services, not the executable, files changed elsewhere or browser data. Export conversations and profiles you want to keep; these backups do not include RAG collections or downloaded models. Default profile encryption uses a known built-in key: treat backups as sensitive files.
76
+
77
+ ## Documentación y ayuda / Documentation and help
78
+
79
+ - [Ayuda de ZeroChat 8.x — Español](https://albalday.github.io/zerochat/help/index.html)
80
+ - [ZeroChat 8.x help — English](https://albalday.github.io/zerochat/help/en/index.html)
81
+
82
+ Las guías explican configuración, uso y límites. Los informes conservan su versión y fecha de revisión originales; no son una certificación de 8.x ni una promesa de nuevas funcionalidades.
83
+
84
+ *English:* The guides explain setup, usage and limits. Reports retain their original reviewed version and date; they are not an 8.x certification or a promise of new features.
85
+
86
+ ## Desarrollo / Development
87
+
88
+ Trabaja en `dev`. Para instalar dependencias y ejecutar las pruebas del proyecto: / Work on `dev`. To install dependencies and run the project tests:
89
+
90
+ ```bash
91
+ npm install
92
+ npm test
93
+ ```
94
+
95
+ Dentro del repositorio, `python3 zerochat.py` sirve la interfaz local de desarrollo. Si modificas `py/`, reconstruye `zerochat.py` con `npm run build:backend`. La web carga directamente sus módulos, sin un bundle de aplicación.
96
+
97
+ *English:* Inside the repository, `python3 zerochat.py` serves the local development interface. After changing `py/`, rebuild `zerochat.py` with `npm run build:backend`. The web app loads its modules directly, without an application bundle.
98
+
99
+ Las normas y validaciones se describen en [`AGENTS.md`](./AGENTS.md) y [`tests/README.md`](./tests/README.md).
100
+
101
+ *English:* See those files for the applicable development rules and validation.
@@ -0,0 +1,79 @@
1
+ # ZeroChat
2
+
3
+ ZeroChat es un cliente web de uso personal para conversar con modelos de IA, consultar documentos y utilizar herramientas. Esta documentación corresponde a la serie **8.x**, que consolida las funcionalidades existentes. El desarrollo de nuevas funcionalidades queda en pausa por el momento.
4
+
5
+ La interfaz se sirve como archivos estáticos: `zerochat.html` necesita sus módulos `js/` y `css/`. El historial y la configuración se guardan en el navegador; no se sincronizan automáticamente entre dispositivos. Los proveedores remotos reciben el contenido incluido en las peticiones, y las herramientas web pueden utilizar servicios intermediarios. Almacenamiento local no significa privacidad absoluta.
6
+
7
+ *English:* ZeroChat is a web client for personal use: chatting with AI models, consulting documents and using tools. This documentation covers the **8.x** series, which consolidates existing features. New feature development is paused for now.
8
+
9
+ The interface is served as static files: `zerochat.html` needs its `js/` and `css/` modules. History and settings are stored in the browser, without automatic cross-device sync. Remote providers receive content included in requests, and web tools may use intermediary services. Local storage does not mean absolute privacy.
10
+
11
+ ## Características principales / Main features
12
+
13
+ - **Perfiles:** OpenAI y APIs de chat compatibles, Ollama, OpenRouter, Claude y Gemini. La visión, las herramientas y el razonamiento dependen del modelo y del adaptador.
14
+ - **WebLLM:** inferencia en el navegador con WebGPU, tras descargar un modelo. Necesita hardware compatible y memoria suficiente.
15
+ - **Documentos:** adjuntos y colecciones RAG con extracción local y búsqueda léxica mediante Orama; sin embeddings. Los fragmentos recuperados se envían al modelo elegido. La extracción de PDF tiene límites y no incluye OCR general.
16
+ - **Herramientas:** JavaScript aislado, búsqueda y lectura web, gráficos, planes y puntos de control. El backend opcional añade archivos, comandos, diagnósticos, automatización de navegador y servicios MCP.
17
+ - **Historial y diagnóstico:** ramas de conversación, exportación JSON y Markdown, impresión a PDF, métricas de contexto y revisión de peticiones antes del envío.
18
+
19
+ *English:*
20
+
21
+ - **Profiles:** OpenAI and compatible chat APIs, Ollama, OpenRouter, Claude and Gemini. Vision, tools and reasoning depend on the model and adapter.
22
+ - **WebLLM:** browser inference through WebGPU after downloading a model. Requires compatible hardware and enough memory.
23
+ - **Documents:** attachments and RAG collections with local extraction and Orama lexical search, without embeddings. Retrieved passages are sent to the selected model. PDF extraction has limitations and no general OCR.
24
+ - **Tools:** isolated JavaScript, web search and retrieval, charts, plans and checkpoints. The optional backend adds files, commands, diagnostics, browser automation and MCP services.
25
+ - **History and diagnostics:** conversation branches, JSON and Markdown export, printing to PDF, context metrics and request inspection before sending.
26
+
27
+ ## Inicio rápido / Quick start
28
+
29
+ Abre [ZeroChat en GitHub Pages](https://albalday.github.io/zerochat/zerochat.html), crea un perfil, introduce la URL y la clave si corresponde, consulta los modelos con **Query** y guarda. El perfil **Espejo** permite probar la interfaz sin inferencia. Para WebLLM, prepara un modelo del catálogo.
30
+
31
+ *English:* Open [ZeroChat on GitHub Pages](https://albalday.github.io/zerochat/zerochat.html), create a profile, enter the URL and key where required, query models with **Query**, and save. **Mirror** lets you try the interface without inference. For WebLLM, prepare a catalogue model.
32
+
33
+ Para añadir herramientas del equipo, instala y ejecuta el backend con Python. / To add computer tools, install and run the Python backend:
34
+
35
+ ```bash
36
+ pip install zerochat
37
+ zerochat
38
+ ```
39
+
40
+ También puedes descargar el script y ejecutarlo. / You can also download and run the script:
41
+
42
+ ```bash
43
+ curl -sL https://albalday.github.io/zerochat/zerochat.py -o zerochat.py
44
+ python3 zerochat.py
45
+ ```
46
+
47
+ Fuera del repositorio de desarrollo, ambos modos abren la interfaz de GitHub Pages. Crean `~/zerochat/` para configuración y servicios, con `.venv/` para dependencias MCP Python. Mantén el proceso abierto y usa su enlace de arranque para autenticar la conexión local. Revisa **Ajustes → MCP** y **Permisos** antes de habilitar herramientas: los comandos actúan con los permisos del proceso local y detenerlos no deshace cambios.
48
+
49
+ Borrar `~/zerochat/` elimina ese estado y los MCP gestionados, no el ejecutable, los archivos modificados fuera de esa carpeta ni los datos del navegador. Exporta las conversaciones y perfiles que quieras conservar; sus copias no incluyen las colecciones RAG ni los modelos descargados. El cifrado predeterminado de perfiles utiliza una clave integrada conocida: trata las copias como archivos sensibles.
50
+
51
+ *English:* Outside the development repository, both modes open the GitHub Pages interface. They create `~/zerochat/` for configuration and services, with `.venv/` for Python MCP dependencies. Keep the process running and use its startup link to authenticate the local connection. Review **Settings → MCP** and **Permissions** before enabling tools: commands run with the local process permissions, and stopping them does not undo changes.
52
+
53
+ Deleting `~/zerochat/` removes that state and managed MCP services, not the executable, files changed elsewhere or browser data. Export conversations and profiles you want to keep; these backups do not include RAG collections or downloaded models. Default profile encryption uses a known built-in key: treat backups as sensitive files.
54
+
55
+ ## Documentación y ayuda / Documentation and help
56
+
57
+ - [Ayuda de ZeroChat 8.x — Español](https://albalday.github.io/zerochat/help/index.html)
58
+ - [ZeroChat 8.x help — English](https://albalday.github.io/zerochat/help/en/index.html)
59
+
60
+ Las guías explican configuración, uso y límites. Los informes conservan su versión y fecha de revisión originales; no son una certificación de 8.x ni una promesa de nuevas funcionalidades.
61
+
62
+ *English:* The guides explain setup, usage and limits. Reports retain their original reviewed version and date; they are not an 8.x certification or a promise of new features.
63
+
64
+ ## Desarrollo / Development
65
+
66
+ Trabaja en `dev`. Para instalar dependencias y ejecutar las pruebas del proyecto: / Work on `dev`. To install dependencies and run the project tests:
67
+
68
+ ```bash
69
+ npm install
70
+ npm test
71
+ ```
72
+
73
+ Dentro del repositorio, `python3 zerochat.py` sirve la interfaz local de desarrollo. Si modificas `py/`, reconstruye `zerochat.py` con `npm run build:backend`. La web carga directamente sus módulos, sin un bundle de aplicación.
74
+
75
+ *English:* Inside the repository, `python3 zerochat.py` serves the local development interface. After changing `py/`, rebuild `zerochat.py` with `npm run build:backend`. The web app loads its modules directly, without an application bundle.
76
+
77
+ Las normas y validaciones se describen en [`AGENTS.md`](./AGENTS.md) y [`tests/README.md`](./tests/README.md).
78
+
79
+ *English:* See those files for the applicable development rules and validation.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "zerochat"
7
- version = "7.11.0"
7
+ version = "8.0.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"
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: zerochat
3
+ Version: 8.0.0
4
+ Summary: Private local web client for AI models, tools and MCP services
5
+ Author: Alberto Alday
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://albalday.github.io/zerochat/
8
+ Project-URL: Repository, https://github.com/albalday/zerochat
9
+ Project-URL: Issues, https://github.com/albalday/zerochat/issues
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Dynamic: license-file
22
+
23
+ # ZeroChat
24
+
25
+ ZeroChat es un cliente web de uso personal para conversar con modelos de IA, consultar documentos y utilizar herramientas. Esta documentación corresponde a la serie **8.x**, que consolida las funcionalidades existentes. El desarrollo de nuevas funcionalidades queda en pausa por el momento.
26
+
27
+ La interfaz se sirve como archivos estáticos: `zerochat.html` necesita sus módulos `js/` y `css/`. El historial y la configuración se guardan en el navegador; no se sincronizan automáticamente entre dispositivos. Los proveedores remotos reciben el contenido incluido en las peticiones, y las herramientas web pueden utilizar servicios intermediarios. Almacenamiento local no significa privacidad absoluta.
28
+
29
+ *English:* ZeroChat is a web client for personal use: chatting with AI models, consulting documents and using tools. This documentation covers the **8.x** series, which consolidates existing features. New feature development is paused for now.
30
+
31
+ The interface is served as static files: `zerochat.html` needs its `js/` and `css/` modules. History and settings are stored in the browser, without automatic cross-device sync. Remote providers receive content included in requests, and web tools may use intermediary services. Local storage does not mean absolute privacy.
32
+
33
+ ## Características principales / Main features
34
+
35
+ - **Perfiles:** OpenAI y APIs de chat compatibles, Ollama, OpenRouter, Claude y Gemini. La visión, las herramientas y el razonamiento dependen del modelo y del adaptador.
36
+ - **WebLLM:** inferencia en el navegador con WebGPU, tras descargar un modelo. Necesita hardware compatible y memoria suficiente.
37
+ - **Documentos:** adjuntos y colecciones RAG con extracción local y búsqueda léxica mediante Orama; sin embeddings. Los fragmentos recuperados se envían al modelo elegido. La extracción de PDF tiene límites y no incluye OCR general.
38
+ - **Herramientas:** JavaScript aislado, búsqueda y lectura web, gráficos, planes y puntos de control. El backend opcional añade archivos, comandos, diagnósticos, automatización de navegador y servicios MCP.
39
+ - **Historial y diagnóstico:** ramas de conversación, exportación JSON y Markdown, impresión a PDF, métricas de contexto y revisión de peticiones antes del envío.
40
+
41
+ *English:*
42
+
43
+ - **Profiles:** OpenAI and compatible chat APIs, Ollama, OpenRouter, Claude and Gemini. Vision, tools and reasoning depend on the model and adapter.
44
+ - **WebLLM:** browser inference through WebGPU after downloading a model. Requires compatible hardware and enough memory.
45
+ - **Documents:** attachments and RAG collections with local extraction and Orama lexical search, without embeddings. Retrieved passages are sent to the selected model. PDF extraction has limitations and no general OCR.
46
+ - **Tools:** isolated JavaScript, web search and retrieval, charts, plans and checkpoints. The optional backend adds files, commands, diagnostics, browser automation and MCP services.
47
+ - **History and diagnostics:** conversation branches, JSON and Markdown export, printing to PDF, context metrics and request inspection before sending.
48
+
49
+ ## Inicio rápido / Quick start
50
+
51
+ Abre [ZeroChat en GitHub Pages](https://albalday.github.io/zerochat/zerochat.html), crea un perfil, introduce la URL y la clave si corresponde, consulta los modelos con **Query** y guarda. El perfil **Espejo** permite probar la interfaz sin inferencia. Para WebLLM, prepara un modelo del catálogo.
52
+
53
+ *English:* Open [ZeroChat on GitHub Pages](https://albalday.github.io/zerochat/zerochat.html), create a profile, enter the URL and key where required, query models with **Query**, and save. **Mirror** lets you try the interface without inference. For WebLLM, prepare a catalogue model.
54
+
55
+ Para añadir herramientas del equipo, instala y ejecuta el backend con Python. / To add computer tools, install and run the Python backend:
56
+
57
+ ```bash
58
+ pip install zerochat
59
+ zerochat
60
+ ```
61
+
62
+ También puedes descargar el script y ejecutarlo. / You can also download and run the script:
63
+
64
+ ```bash
65
+ curl -sL https://albalday.github.io/zerochat/zerochat.py -o zerochat.py
66
+ python3 zerochat.py
67
+ ```
68
+
69
+ Fuera del repositorio de desarrollo, ambos modos abren la interfaz de GitHub Pages. Crean `~/zerochat/` para configuración y servicios, con `.venv/` para dependencias MCP Python. Mantén el proceso abierto y usa su enlace de arranque para autenticar la conexión local. Revisa **Ajustes → MCP** y **Permisos** antes de habilitar herramientas: los comandos actúan con los permisos del proceso local y detenerlos no deshace cambios.
70
+
71
+ Borrar `~/zerochat/` elimina ese estado y los MCP gestionados, no el ejecutable, los archivos modificados fuera de esa carpeta ni los datos del navegador. Exporta las conversaciones y perfiles que quieras conservar; sus copias no incluyen las colecciones RAG ni los modelos descargados. El cifrado predeterminado de perfiles utiliza una clave integrada conocida: trata las copias como archivos sensibles.
72
+
73
+ *English:* Outside the development repository, both modes open the GitHub Pages interface. They create `~/zerochat/` for configuration and services, with `.venv/` for Python MCP dependencies. Keep the process running and use its startup link to authenticate the local connection. Review **Settings → MCP** and **Permissions** before enabling tools: commands run with the local process permissions, and stopping them does not undo changes.
74
+
75
+ Deleting `~/zerochat/` removes that state and managed MCP services, not the executable, files changed elsewhere or browser data. Export conversations and profiles you want to keep; these backups do not include RAG collections or downloaded models. Default profile encryption uses a known built-in key: treat backups as sensitive files.
76
+
77
+ ## Documentación y ayuda / Documentation and help
78
+
79
+ - [Ayuda de ZeroChat 8.x — Español](https://albalday.github.io/zerochat/help/index.html)
80
+ - [ZeroChat 8.x help — English](https://albalday.github.io/zerochat/help/en/index.html)
81
+
82
+ Las guías explican configuración, uso y límites. Los informes conservan su versión y fecha de revisión originales; no son una certificación de 8.x ni una promesa de nuevas funcionalidades.
83
+
84
+ *English:* The guides explain setup, usage and limits. Reports retain their original reviewed version and date; they are not an 8.x certification or a promise of new features.
85
+
86
+ ## Desarrollo / Development
87
+
88
+ Trabaja en `dev`. Para instalar dependencias y ejecutar las pruebas del proyecto: / Work on `dev`. To install dependencies and run the project tests:
89
+
90
+ ```bash
91
+ npm install
92
+ npm test
93
+ ```
94
+
95
+ Dentro del repositorio, `python3 zerochat.py` sirve la interfaz local de desarrollo. Si modificas `py/`, reconstruye `zerochat.py` con `npm run build:backend`. La web carga directamente sus módulos, sin un bundle de aplicación.
96
+
97
+ *English:* Inside the repository, `python3 zerochat.py` serves the local development interface. After changing `py/`, rebuild `zerochat.py` with `npm run build:backend`. The web app loads its modules directly, without an application bundle.
98
+
99
+ Las normas y validaciones se describen en [`AGENTS.md`](./AGENTS.md) y [`tests/README.md`](./tests/README.md).
100
+
101
+ *English:* See those files for the applicable development rules and validation.
@@ -14,8 +14,10 @@ from __future__ import annotations
14
14
  import argparse
15
15
  import ast
16
16
  import atexit
17
+ import base64
17
18
  import datetime
18
19
  import fnmatch
20
+ import hashlib
19
21
  import hmac
20
22
  import importlib.metadata
21
23
  import json
@@ -40,7 +42,7 @@ import webbrowser
40
42
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
41
43
  from pathlib import Path
42
44
 
43
- SOURCE_BACKEND_VERSION = "7.11.0"
45
+ SOURCE_BACKEND_VERSION = "8.0.0"
44
46
 
45
47
  def _read_source_version(filename: str) -> str | None:
46
48
  """Lee la versión de un archivo del repositorio cuando se ejecuta desde fuentes."""
@@ -96,143 +98,97 @@ PYPI_VERSION_URL = "https://pypi.org/pypi/zerochat/json"
96
98
  REMOTE_SCRIPT_URL = "https://raw.githubusercontent.com/albalday/zerochat/master/zerochat.py"
97
99
  CONSOLE_STATUS_IDLE_SECONDS = 8.0
98
100
  CONSOLE_CONTROL = None
99
-
100
-
101
- def format_uptime(seconds: float) -> str:
102
- """Devuelve una duración breve y estable para la línea de estado de consola."""
103
- total = max(0, int(seconds))
104
- hours, remainder = divmod(total, 3600)
105
- minutes, secs = divmod(remainder, 60)
106
- return f"{hours:02d}:{minutes:02d}:{secs:02d}"
107
-
108
-
101
+ NOTICES: list[str] = []
102
+ NOTICES_LOCK = threading.Lock()
109
103
  class ConsoleControl:
110
- """Atajos de consola y línea de estado, solo para terminales interactivos."""
104
+ """Muestra una sola línea de ayuda tras un periodo de inactividad en consola."""
111
105
  def __init__(self, server: ThreadingHTTPServer, parser: argparse.ArgumentParser, target_url: str | None = None):
112
106
  self.server = server
113
107
  self.parser = parser
114
108
  self.target_url = target_url
115
- self.started_at = time.monotonic()
116
- self.last_activity = self.started_at
117
- self.stop_event = threading.Event()
109
+ self.last_activity = time.monotonic()
118
110
  self.lock = threading.Lock()
119
- self.status_visible = False
120
- self.enabled = bool(getattr(sys.stdin, "isatty", lambda: False)() and getattr(sys.stdout, "isatty", lambda: False)())
121
- self._threads: list[threading.Thread] = []
122
- self._terminal_fd: int | None = None
123
- self._terminal_state = None
111
+ self.enabled = bool(getattr(sys.stdout, "isatty", lambda: False)())
112
+ self._keyboard_enabled = bool(getattr(sys.stdin, "isatty", lambda: False)())
113
+ self._idle_timer: threading.Timer | None = None
114
+ self._keyboard_stop = threading.Event()
115
+ self._keyboard_thread: threading.Thread | None = None
116
+ self._timer_generation = 0
124
117
  self._closed = False
125
118
 
126
119
  def start(self):
127
- if not self.enabled:
128
- return
129
- if os.name != "nt":
130
- try:
131
- import termios
132
- import tty
133
- self._terminal_fd = sys.stdin.fileno()
134
- self._terminal_state = termios.tcgetattr(self._terminal_fd)
135
- tty.setcbreak(self._terminal_fd)
136
- except (OSError, ValueError):
137
- self._restore_terminal()
138
- self.enabled = False
139
- return
140
- self._threads = [
141
- threading.Thread(target=self._status_loop, name="zerochat-console-status", daemon=True),
142
- threading.Thread(target=self._keyboard_loop, name="zerochat-console-input", daemon=True),
143
- ]
144
- for thread in self._threads:
145
- thread.start()
120
+ if self.enabled:
121
+ self._reset_idle_timer()
122
+ if self._keyboard_enabled:
123
+ self._keyboard_thread = threading.Thread(target=self._keyboard_loop, name="zerochat-console-input", daemon=True)
124
+ self._keyboard_thread.start()
146
125
 
147
126
  def close(self):
148
- if self._closed:
149
- return
150
- self._closed = True
151
- self.stop_event.set()
152
- for thread in self._threads:
153
- if thread is not threading.current_thread():
154
- thread.join(timeout=1.0)
155
- self._restore_terminal()
156
- self.clear_status(final=True)
157
-
158
- def _restore_terminal(self):
159
- if self._terminal_fd is None or self._terminal_state is None:
160
- return
161
- try:
162
- import termios
163
- termios.tcsetattr(self._terminal_fd, termios.TCSADRAIN, self._terminal_state)
164
- except OSError:
165
- pass
166
- finally:
167
- self._terminal_fd = None
168
- self._terminal_state = None
169
-
170
- def clear_status(self, *, final: bool = False):
171
- if not self.enabled:
172
- return
173
127
  with self.lock:
174
- if self.status_visible:
175
- sys.stdout.write("\r\033[2K")
176
- self.status_visible = False
177
- if final:
178
- sys.stdout.write("\r\n")
179
- sys.stdout.flush()
128
+ self._closed = True
129
+ self._keyboard_stop.set()
130
+ if self._idle_timer:
131
+ self._idle_timer.cancel()
132
+ self._idle_timer = None
180
133
 
181
- def log(self, message: str, *, flush: bool = True):
134
+ def _reset_idle_timer(self):
182
135
  with self.lock:
183
- if self.enabled and self.status_visible:
184
- sys.stdout.write("\r\033[2K")
185
- self.status_visible = False
186
- print(message, flush=flush)
136
+ if self._closed:
137
+ return
187
138
  self.last_activity = time.monotonic()
139
+ self._timer_generation += 1
140
+ if self._idle_timer:
141
+ self._idle_timer.cancel()
142
+ generation = self._timer_generation
143
+ self._idle_timer = threading.Timer(CONSOLE_STATUS_IDLE_SECONDS, self._show_commands_once, args=(generation,))
144
+ self._idle_timer.daemon = True
145
+ self._idle_timer.start()
146
+
147
+ def _show_commands_once(self, generation: int):
148
+ with self.lock:
149
+ if self._closed or generation != self._timer_generation:
150
+ return
151
+ commands = "[h] ayuda · [n] Navegador"
152
+ if get_notices():
153
+ commands += " · [i] información"
154
+ print(f"Comandos de consola: {commands} · [x] salir ordenadamente", flush=True)
155
+ self._idle_timer = None
156
+
157
+ def log(self, message: str, *, flush: bool = True):
158
+ print(message, flush=flush)
159
+ if self.enabled:
160
+ self._reset_idle_timer()
188
161
 
189
162
  def show_help(self):
190
- with self.lock:
191
- if self.status_visible:
192
- sys.stdout.write("\r\033[2K")
193
- self.status_visible = False
194
- print("\nComandos de consola: [h] ayuda · [n] Navegador · [x] salir ordenadamente\n", flush=True)
195
- print(self.parser.format_help().rstrip(), flush=True)
196
- self.last_activity = time.monotonic()
163
+ print(self.parser.format_help().rstrip(), flush=True)
197
164
 
198
- def _render_status(self):
199
- if not self.enabled:
165
+ def show_notices(self):
166
+ notices = get_notices()
167
+ if not notices:
200
168
  return
201
- uptime = format_uptime(time.monotonic() - self.started_at)
202
- with self.lock:
203
- if time.monotonic() - self.last_activity < CONSOLE_STATUS_IDLE_SECONDS:
204
- return
205
- sys.stdout.write(f"\r\033[2KZeroChat activo {uptime} · [h] ayuda · [n] Navegador · [x] salir")
206
- sys.stdout.flush()
207
- self.status_visible = True
208
-
209
- def _status_loop(self):
210
- while not self.stop_event.wait(1.0):
211
- self._render_status()
169
+ print("Información:", flush=True)
170
+ for notice in notices:
171
+ print(f"- {notice}", flush=True)
212
172
 
213
- def _handle_key(self, key: str):
214
- if key.lower() == "h":
173
+ def _handle_command(self, command: str):
174
+ key = command.strip().lower()
175
+ if key == "h":
215
176
  self.show_help()
216
- elif key.lower() == "n":
217
- if self.target_url:
218
- launch_browser(self.target_url)
219
- elif key.lower() == "x":
177
+ elif key == "n" and self.target_url:
178
+ launch_browser(self.target_url)
179
+ elif key == "i":
180
+ self.show_notices()
181
+ elif key == "x":
220
182
  self.log(f"[{time.strftime('%H:%M:%S')}] Deteniendo servidor ZeroChat...")
221
183
  stop_zerochat_server(self.server)
222
184
 
223
185
  def _keyboard_loop(self):
224
- if os.name == "nt":
225
- import msvcrt
226
- while not self.stop_event.wait(0.05):
227
- if msvcrt.kbhit():
228
- self._handle_key(msvcrt.getwch())
229
- return
230
-
231
- import select
232
- while not self.stop_event.is_set():
233
- ready, _, _ = select.select([sys.stdin], [], [], 0.1)
234
- if ready:
235
- self._handle_key(sys.stdin.read(1))
186
+ """Lee comandos normales terminados con Intro, sin alterar el terminal."""
187
+ while not self._keyboard_stop.is_set():
188
+ line = sys.stdin.readline()
189
+ if not line:
190
+ return
191
+ self._handle_command(line)
236
192
 
237
193
 
238
194
  def console_log(message: str, *, flush: bool = True):
@@ -282,6 +238,26 @@ def get_venv_dir() -> Path:
282
238
  return get_data_dir() / ".venv"
283
239
 
284
240
 
241
+ def reset_notices():
242
+ """Reinicia los avisos transitorios de la ejecución actual."""
243
+ with NOTICES_LOCK:
244
+ NOTICES.clear()
245
+
246
+
247
+ def add_notice(message: str):
248
+ """Añade un aviso para la consola interactiva de la ejecución actual."""
249
+ if not isinstance(message, str) or not message.strip():
250
+ raise ValueError("El aviso debe ser texto no vacío")
251
+ with NOTICES_LOCK:
252
+ NOTICES.append(message.strip())
253
+
254
+
255
+ def get_notices() -> tuple[str, ...]:
256
+ """Devuelve una instantánea inmutable de los avisos actuales."""
257
+ with NOTICES_LOCK:
258
+ return tuple(NOTICES)
259
+
260
+
285
261
  def get_daily_token() -> str:
286
262
  """Devuelve un token de sesión diario persistido en ~/zerochat/config/token.json."""
287
263
  config_dir = get_data_dir() / "config"
@@ -310,6 +286,16 @@ SESSION_TOKEN = get_daily_token()
310
286
  ACTIVE_PORT = DEFAULT_PORT
311
287
  ACTIVE_HOST = DEFAULT_HOST
312
288
 
289
+ # Kept only for this Python process: binds an approved browser tool call to
290
+ # the exact JSON-RPC bytes sent to the local host.
291
+ TOOL_AUTH_VERSION = "zerochat-tool-auth-v1"
292
+ TOOL_AUTH_TTL_MS = 30_000
293
+ TOOL_AUTH_MAX_NONCES = 10_000
294
+ TOOL_AUTH_KEY = secrets.token_bytes(32)
295
+ TOOL_AUTH_SESSION_ID = secrets.token_urlsafe(18)
296
+ TOOL_AUTH_NONCES: dict[str, int] = {}
297
+ TOOL_AUTH_NONCES_LOCK = threading.Lock()
298
+
313
299
  DETECTED_OS = "windows" if sys.platform.startswith("win") else ("android" if "ANDROID_ROOT" in os.environ else "linux")
314
300
 
315
301
 
@@ -381,16 +367,15 @@ def check_version():
381
367
  installed = is_installed_runtime()
382
368
  remote_ver = _read_remote_version(PYPI_VERSION_URL if installed else REMOTE_VERSION_URL)
383
369
  if remote_ver and re.match(r"^\d+(\.\d+)+", remote_ver) and has_new_backend_version(remote_ver):
384
- console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Nueva versión del servidor disponible (Local: {VERSION}, Remota: {compatibility_version(remote_ver)})", flush=True)
370
+ notice = f"Nueva versión del servidor disponible (local: {VERSION}, remota: {compatibility_version(remote_ver)})."
385
371
  if installed:
386
- console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Actualiza cuando quieras con: {sys.executable} -m pip install --upgrade --no-cache-dir zerochat", flush=True)
372
+ notice += f" Actualiza cuando quieras con: {sys.executable} -m pip install --upgrade --no-cache-dir zerochat"
387
373
  else:
388
- console_log(f"[{time.strftime('%H:%M:%S')}] [zerochat] Actualiza con: curl -sSL {REMOTE_SCRIPT_URL} -o zerochat.py", flush=True)
374
+ notice += f" Actualiza con: curl -sSL {REMOTE_SCRIPT_URL} -o zerochat.py"
375
+ add_notice(notice)
389
376
  except Exception:
390
377
  # Modo offline o timeout ignorado de forma segura
391
378
  pass
392
-
393
-
394
379
  # ==============================================================================
395
380
  # Herramientas Locales Core
396
381
  # ==============================================================================
@@ -1163,6 +1148,38 @@ try {
1163
1148
  _BROWSER_SESSION = PersistentBrowserSession()
1164
1149
 
1165
1150
 
1151
+ def browser_action_availability() -> dict:
1152
+ """Comprueba requisitos locales sin lanzar Node.js ni Chromium."""
1153
+ if not shutil.which("node"):
1154
+ return {"available": False, "error": "Node.js is not installed or not found in system PATH."}
1155
+ playwright_locations = (
1156
+ Path.cwd() / "node_modules" / "playwright",
1157
+ get_data_dir() / "services" / "playwright" / "node_modules" / "playwright"
1158
+ )
1159
+ if not any(location.is_dir() for location in playwright_locations):
1160
+ return {"available": False, "error": "Playwright is not available."}
1161
+ browser_commands = (
1162
+ "google-chrome", "google-chrome-stable", "chromium", "chromium-browser",
1163
+ "msedge", "msedge.exe", "chrome", "chrome.exe"
1164
+ )
1165
+ if sys.platform == "darwin":
1166
+ playwright_cache = Path.home() / "Library" / "Caches" / "ms-playwright"
1167
+ elif sys.platform.startswith("win"):
1168
+ playwright_cache = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local")) / "ms-playwright"
1169
+ else:
1170
+ playwright_cache = Path.home() / ".cache" / "ms-playwright"
1171
+ try:
1172
+ has_playwright_browser = playwright_cache.is_dir() and any(
1173
+ child.is_dir() and child.name.startswith("chromium")
1174
+ for child in playwright_cache.iterdir()
1175
+ )
1176
+ except OSError:
1177
+ has_playwright_browser = False
1178
+ if has_playwright_browser or any(shutil.which(command) for command in browser_commands):
1179
+ return {"available": True}
1180
+ return {"available": False, "error": "Playwright Chromium is not installed."}
1181
+
1182
+
1166
1183
  def browser_action(action: str, url: str | None = None, selector: str | None = None, value: str | None = None) -> str:
1167
1184
  """Control a headless browser for UI testing and visual inspection."""
1168
1185
  try:
@@ -1321,7 +1338,6 @@ LOCAL_TOOL_HANDLERS = {
1321
1338
  "get_diagnostics": get_diagnostics,
1322
1339
  "browser_action": browser_action
1323
1340
  }
1324
-
1325
1341
  # ==============================================================================
1326
1342
  # Servidor HTTP JSON-RPC 2.0 y SSE con Autenticación por Token
1327
1343
  # ==============================================================================
@@ -2202,7 +2218,7 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
2202
2218
  return
2203
2219
  self.send_header("Access-Control-Allow-Origin", origin if origin else "*")
2204
2220
  self.send_header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
2205
- self.send_header("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization, X-ZeroChat-Token, X-ZeroChat-Client")
2221
+ self.send_header("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization, X-ZeroChat-Token, X-ZeroChat-Client, X-ZeroChat-Tool-Session, X-ZeroChat-Tool-Expires, X-ZeroChat-Tool-Nonce, X-ZeroChat-Tool-Signature")
2206
2222
  self.send_header("Access-Control-Allow-Private-Network", "true")
2207
2223
 
2208
2224
  def verify_token(self) -> bool:
@@ -2221,6 +2237,42 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
2221
2237
  mark_browser_active()
2222
2238
  return is_valid
2223
2239
 
2240
+ def verify_tool_authorization(self, body: bytes, request_path: str) -> str | None:
2241
+ """Validate and atomically consume an authorization for one tool call."""
2242
+ session_id = self.headers.get("X-ZeroChat-Tool-Session", "")
2243
+ expires_raw = self.headers.get("X-ZeroChat-Tool-Expires", "")
2244
+ nonce = self.headers.get("X-ZeroChat-Tool-Nonce", "")
2245
+ signature = self.headers.get("X-ZeroChat-Tool-Signature", "")
2246
+ if (not isinstance(session_id, str) or len(session_id) > 128 or
2247
+ not isinstance(nonce, str) or not re.fullmatch(r"[A-Za-z0-9_-]{22,128}", nonce) or
2248
+ not isinstance(signature, str) or not re.fullmatch(r"[A-Za-z0-9_-]{43,128}", signature)):
2249
+ return "Missing or invalid tool authorization"
2250
+ try:
2251
+ expires_at = int(expires_raw)
2252
+ except (TypeError, ValueError):
2253
+ return "Missing or invalid tool authorization"
2254
+ now_ms = int(time.time() * 1000)
2255
+ if session_id != TOOL_AUTH_SESSION_ID or expires_at < now_ms or expires_at > now_ms + TOOL_AUTH_TTL_MS:
2256
+ return "Expired or invalid tool authorization"
2257
+ body_hash = hashlib.sha256(body).hexdigest()
2258
+ signature_base = json.dumps(
2259
+ [TOOL_AUTH_VERSION, session_id, "POST", request_path or "/", expires_at, nonce, body_hash],
2260
+ separators=(",", ":"), ensure_ascii=True
2261
+ ).encode("utf-8")
2262
+ expected = base64.urlsafe_b64encode(hmac.new(TOOL_AUTH_KEY, signature_base, hashlib.sha256).digest()).rstrip(b"=").decode("ascii")
2263
+ if not hmac.compare_digest(signature, expected):
2264
+ return "Invalid tool authorization signature"
2265
+ with TOOL_AUTH_NONCES_LOCK:
2266
+ for value, expiry in list(TOOL_AUTH_NONCES.items()):
2267
+ if expiry < now_ms:
2268
+ del TOOL_AUTH_NONCES[value]
2269
+ if nonce in TOOL_AUTH_NONCES:
2270
+ return "Tool authorization was already used"
2271
+ if len(TOOL_AUTH_NONCES) >= TOOL_AUTH_MAX_NONCES:
2272
+ return "Tool authorization cache is full"
2273
+ TOOL_AUTH_NONCES[nonce] = expires_at
2274
+ return None
2275
+
2224
2276
  def do_OPTIONS(self):
2225
2277
  t0 = time.monotonic()
2226
2278
  safe_path = sanitize_log_path(self.path)
@@ -2270,6 +2322,13 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
2270
2322
  self._log_res(200, safe_path, (time.monotonic() - t0) * 1000)
2271
2323
  return
2272
2324
 
2325
+ # Browsers request this optional public resource without API credentials.
2326
+ if path_clean == "/favicon.ico":
2327
+ self.send_response(204)
2328
+ self.send_header("Content-Length", "0")
2329
+ self.end_headers()
2330
+ return
2331
+
2273
2332
  if not is_heartbeat:
2274
2333
  self._log_req("GET", safe_path)
2275
2334
 
@@ -2303,12 +2362,13 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
2303
2362
  self._log_res(200, f"{safe_path} [SSE canal activo]", (time.monotonic() - t0) * 1000)
2304
2363
  return
2305
2364
 
2306
- # Status general
2307
2365
  res_data = json.dumps({
2308
2366
  "status": "active",
2309
2367
  "server": "ZeroChat Local Server",
2310
2368
  "version": VERSION,
2369
+ "cwd": str(Path.cwd().resolve()),
2311
2370
  "tools_count": len(LOCAL_TOOLS_DEFINITIONS),
2371
+ "browser_action": browser_action_availability(),
2312
2372
  "os": DETECTED_OS
2313
2373
  }, ensure_ascii=False, indent=2).encode("utf-8")
2314
2374
 
@@ -2431,6 +2491,11 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
2431
2491
  self._send_json_response(400, {"error": tool_args_error})
2432
2492
  self._log_res(400, safe_path, (time.monotonic() - t0) * 1000, tool_args_error)
2433
2493
  return
2494
+ auth_error = self.verify_tool_authorization(post_data, self.path.split("?", 1)[0].rstrip("/") or "/")
2495
+ if auth_error:
2496
+ self._send_json_response(403, {"error": auth_error})
2497
+ self._log_res(403, safe_path, (time.monotonic() - t0) * 1000, auth_error)
2498
+ return
2434
2499
 
2435
2500
  req_path = self.path.split("?", 1)[0].rstrip("/")
2436
2501
 
@@ -2469,6 +2534,12 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
2469
2534
  },
2470
2535
  "capabilities": {
2471
2536
  "tools": {"listChanged": True}
2537
+ },
2538
+ "toolAuthorization": {
2539
+ "version": TOOL_AUTH_VERSION,
2540
+ "sessionId": TOOL_AUTH_SESSION_ID,
2541
+ "key": base64.urlsafe_b64encode(TOOL_AUTH_KEY).rstrip(b"=").decode("ascii"),
2542
+ "ttlMs": TOOL_AUTH_TTL_MS
2472
2543
  }
2473
2544
  }
2474
2545
  elif method == "tools/list":
@@ -2498,14 +2569,34 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
2498
2569
  "protocolVersion": "2024-11-05",
2499
2570
  "serverInfo": {
2500
2571
  "name": "ZeroChat Local Server",
2501
- "version": VERSION
2572
+ "version": VERSION,
2573
+ "cwd": str(Path.cwd().resolve())
2502
2574
  },
2503
2575
  "capabilities": {
2504
2576
  "tools": {"listChanged": True}
2577
+ },
2578
+ "toolAuthorization": {
2579
+ "version": TOOL_AUTH_VERSION,
2580
+ "sessionId": TOOL_AUTH_SESSION_ID,
2581
+ "key": base64.urlsafe_b64encode(TOOL_AUTH_KEY).rstrip(b"=").decode("ascii"),
2582
+ "ttlMs": TOOL_AUTH_TTL_MS
2505
2583
  }
2506
2584
  }
2507
2585
  elif method == "tools/list":
2508
- result = {"tools": list(LOCAL_TOOLS_DEFINITIONS)}
2586
+ browser_availability = browser_action_availability()
2587
+ tools = []
2588
+ for definition in LOCAL_TOOLS_DEFINITIONS:
2589
+ item = dict(definition)
2590
+ if item.get("name") == "browser_action":
2591
+ item["availability"] = browser_availability
2592
+ tools.append(item)
2593
+ result = {"tools": tools}
2594
+ elif method == "tools/availability":
2595
+ requested_name = params.get("name", "") if isinstance(params, dict) else ""
2596
+ if requested_name != "browser_action":
2597
+ error = {"code": -32602, "message": "Herramienta no compatible con comprobación de disponibilidad."}
2598
+ else:
2599
+ result = browser_action_availability()
2509
2600
  elif method == "tools/call":
2510
2601
  tool_name = params.get("name", "") if isinstance(params, dict) else ""
2511
2602
  tool_args = params.get("arguments", {}) if isinstance(params, dict) else {}
@@ -2595,7 +2686,6 @@ class ZeroChatServerHandler(BaseHTTPRequestHandler):
2595
2686
  def log_message(self, format, *args):
2596
2687
  # Silenciar logs ruidosos por defecto
2597
2688
  pass
2598
-
2599
2689
  def is_termux_environment() -> bool:
2600
2690
  """Devuelve si el proceso se ejecuta dentro de la instalación de Termux."""
2601
2691
  termux_prefix = "/data/data/com.termux/files/usr"
@@ -2752,6 +2842,7 @@ def launch_browser(url: str) -> bool:
2752
2842
 
2753
2843
  def main():
2754
2844
  global ACTIVE_PORT, ACTIVE_HOST, SESSION_TOKEN, CONSOLE_CONTROL
2845
+ global TOOL_AUTH_KEY, TOOL_AUTH_SESSION_ID, TOOL_AUTH_NONCES
2755
2846
 
2756
2847
  parser = argparse.ArgumentParser(description=f"ZeroChat Local Server v{VERSION}")
2757
2848
  parser.add_argument("--port", type=int, default=int(os.environ.get("ZEROCHAT_PORT", DEFAULT_PORT)), help=f"Puerto de escucha (default: {DEFAULT_PORT})")
@@ -2765,6 +2856,9 @@ def main():
2765
2856
  parser.add_argument("--version", action="version", version=f"ZeroChat {VERSION}")
2766
2857
  args = parser.parse_args()
2767
2858
 
2859
+ # Los avisos no persisten entre ejecuciones del servidor.
2860
+ reset_notices()
2861
+
2768
2862
  if args.test:
2769
2863
  print(f"[{time.strftime('%H:%M:%S')}] TEST list_directory {'ok' if json.loads(list_directory('.'))['success'] else 'error'}")
2770
2864
  print(f"[{time.strftime('%H:%M:%S')}] TEST read_file {'ok' if json.loads(read_file('package.json', max_lines=5))['success'] else 'error'}")
@@ -2776,6 +2870,14 @@ def main():
2776
2870
  if not args.no_venv:
2777
2871
  ensure_virtual_environment()
2778
2872
 
2873
+ browser_availability = browser_action_availability()
2874
+ if not browser_availability.get("available"):
2875
+ add_notice(
2876
+ "browser_action no está disponible y permanecerá desactivada. "
2877
+ "Instala Node.js, Playwright y Chromium: "
2878
+ "https://albalday.github.io/zerochat/help/browser-action.html"
2879
+ )
2880
+
2779
2881
  # 2. Detectar entorno de desarrollo y resolver URL de destino
2780
2882
  dev_root = get_dev_root()
2781
2883
  static_root = get_static_root()
@@ -2792,6 +2894,11 @@ def main():
2792
2894
  SESSION_TOKEN = args.token
2793
2895
  else:
2794
2896
  SESSION_TOKEN = get_daily_token()
2897
+ # A restart deliberately invalidates every browser-side signing credential.
2898
+ TOOL_AUTH_KEY = secrets.token_bytes(32)
2899
+ TOOL_AUTH_SESSION_ID = secrets.token_urlsafe(18)
2900
+ with TOOL_AUTH_NONCES_LOCK:
2901
+ TOOL_AUTH_NONCES = {}
2795
2902
 
2796
2903
  server = ThreadingHTTPServer((ACTIVE_HOST, ACTIVE_PORT), ZeroChatServerHandler)
2797
2904
 
zerochat-7.11.0/PKG-INFO DELETED
@@ -1,72 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: zerochat
3
- Version: 7.11.0
4
- Summary: Private local web client for AI models, tools and MCP services
5
- Author: Alberto Alday
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://albalday.github.io/zerochat/
8
- Project-URL: Repository, https://github.com/albalday/zerochat
9
- Project-URL: Issues, https://github.com/albalday/zerochat/issues
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Environment :: Web Environment
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3 :: Only
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
- Requires-Python: >=3.10
19
- Description-Content-Type: text/markdown
20
- License-File: LICENSE
21
- Dynamic: license-file
22
-
23
- # ZeroChat
24
-
25
- ZeroChat es un cliente web universal para interactuar con modelos de inteligencia artificial y agentes de forma privada, rápida y sin intermediarios.
26
-
27
- Funciona de forma estática en el navegador (servido por GitHub Pages o directamente desde local en un único archivo HTML), almacenando tus conversaciones, configuraciones y datos de forma segura en tu propio dispositivo (IndexedDB).
28
-
29
- ## Características principales
30
-
31
- - **Multi-proveedor y modelos locales**: compatible con APIs estándar (OpenAI, Anthropic Claude, Google Gemini, OpenRouter) y motores locales (Ollama, LM Studio, vLLM, WebLLM con WebGPU directo en el navegador).
32
- - **Herramientas y MCP (Model Context Protocol)**: soporte para agentes, herramientas de búsqueda/cálculo y servidores MCP locales coordinados mediante `zerochat.py`.
33
- - **Conocimiento local (RAG)**: carga de documentos y recuperación contextual procesada íntegramente en el navegador.
34
- - **Privacidad absoluta**: tus claves API y datos viajan directamente de tu navegador al proveedor seleccionado, sin servidores puente de terceros.
35
- - **Sin necesidad de empaquetado**: distribuible de forma universal y ligera.
36
-
37
- ## Inicio rápido
38
-
39
- - **Uso web directo**: abre [zerochat.html en GitHub Pages](https://albalday.github.io/zerochat/zerochat.html).
40
- - **Servidor local con herramientas MCP**:
41
- ```bash
42
- python3 zerochat.py
43
- ```
44
- Inicia el asistente local, crea `~/zerochat/.venv` para los MCP y abre la interfaz web en tu navegador.
45
- - **Instalación con PyPI**:
46
- ```bash
47
- pip install zerochat
48
- zerochat
49
- ```
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.
53
-
54
- Las publicaciones se realizan desde el repositorio oficial mediante GitHub Actions y PyPI Trusted Publishing.
55
-
56
- ## Documentación y Ayuda
57
-
58
- Para consultar guías paso a paso, configuración de modelos, perfiles y agentes:
59
-
60
- - 📖 **[Centro de Ayuda de ZeroChat (Español)](https://albalday.github.io/zerochat/help/index.html)**
61
- - 📖 **[ZeroChat Documentation & Help (English)](https://albalday.github.io/zerochat/help/en/index.html)**
62
-
63
- ## Desarrollo
64
-
65
- Para ejecutar las pruebas del proyecto:
66
-
67
- ```bash
68
- npm install
69
- npm test
70
- ```
71
-
72
- Consulta [`AGENTS.md`](./AGENTS.md) para conocer las normas de desarrollo y arquitectura, y [`tests/README.md`](./tests/README.md) para la suite de pruebas.
zerochat-7.11.0/README.md DELETED
@@ -1,50 +0,0 @@
1
- # ZeroChat
2
-
3
- ZeroChat es un cliente web universal para interactuar con modelos de inteligencia artificial y agentes de forma privada, rápida y sin intermediarios.
4
-
5
- Funciona de forma estática en el navegador (servido por GitHub Pages o directamente desde local en un único archivo HTML), almacenando tus conversaciones, configuraciones y datos de forma segura en tu propio dispositivo (IndexedDB).
6
-
7
- ## Características principales
8
-
9
- - **Multi-proveedor y modelos locales**: compatible con APIs estándar (OpenAI, Anthropic Claude, Google Gemini, OpenRouter) y motores locales (Ollama, LM Studio, vLLM, WebLLM con WebGPU directo en el navegador).
10
- - **Herramientas y MCP (Model Context Protocol)**: soporte para agentes, herramientas de búsqueda/cálculo y servidores MCP locales coordinados mediante `zerochat.py`.
11
- - **Conocimiento local (RAG)**: carga de documentos y recuperación contextual procesada íntegramente en el navegador.
12
- - **Privacidad absoluta**: tus claves API y datos viajan directamente de tu navegador al proveedor seleccionado, sin servidores puente de terceros.
13
- - **Sin necesidad de empaquetado**: distribuible de forma universal y ligera.
14
-
15
- ## Inicio rápido
16
-
17
- - **Uso web directo**: abre [zerochat.html en GitHub Pages](https://albalday.github.io/zerochat/zerochat.html).
18
- - **Servidor local con herramientas MCP**:
19
- ```bash
20
- python3 zerochat.py
21
- ```
22
- Inicia el asistente local, crea `~/zerochat/.venv` para los MCP y abre la interfaz web en tu navegador.
23
- - **Instalación con PyPI**:
24
- ```bash
25
- pip install zerochat
26
- zerochat
27
- ```
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.
31
-
32
- Las publicaciones se realizan desde el repositorio oficial mediante GitHub Actions y PyPI Trusted Publishing.
33
-
34
- ## Documentación y Ayuda
35
-
36
- Para consultar guías paso a paso, configuración de modelos, perfiles y agentes:
37
-
38
- - 📖 **[Centro de Ayuda de ZeroChat (Español)](https://albalday.github.io/zerochat/help/index.html)**
39
- - 📖 **[ZeroChat Documentation & Help (English)](https://albalday.github.io/zerochat/help/en/index.html)**
40
-
41
- ## Desarrollo
42
-
43
- Para ejecutar las pruebas del proyecto:
44
-
45
- ```bash
46
- npm install
47
- npm test
48
- ```
49
-
50
- Consulta [`AGENTS.md`](./AGENTS.md) para conocer las normas de desarrollo y arquitectura, y [`tests/README.md`](./tests/README.md) para la suite de pruebas.
@@ -1,72 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: zerochat
3
- Version: 7.11.0
4
- Summary: Private local web client for AI models, tools and MCP services
5
- Author: Alberto Alday
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://albalday.github.io/zerochat/
8
- Project-URL: Repository, https://github.com/albalday/zerochat
9
- Project-URL: Issues, https://github.com/albalday/zerochat/issues
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Environment :: Web Environment
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3 :: Only
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
- Requires-Python: >=3.10
19
- Description-Content-Type: text/markdown
20
- License-File: LICENSE
21
- Dynamic: license-file
22
-
23
- # ZeroChat
24
-
25
- ZeroChat es un cliente web universal para interactuar con modelos de inteligencia artificial y agentes de forma privada, rápida y sin intermediarios.
26
-
27
- Funciona de forma estática en el navegador (servido por GitHub Pages o directamente desde local en un único archivo HTML), almacenando tus conversaciones, configuraciones y datos de forma segura en tu propio dispositivo (IndexedDB).
28
-
29
- ## Características principales
30
-
31
- - **Multi-proveedor y modelos locales**: compatible con APIs estándar (OpenAI, Anthropic Claude, Google Gemini, OpenRouter) y motores locales (Ollama, LM Studio, vLLM, WebLLM con WebGPU directo en el navegador).
32
- - **Herramientas y MCP (Model Context Protocol)**: soporte para agentes, herramientas de búsqueda/cálculo y servidores MCP locales coordinados mediante `zerochat.py`.
33
- - **Conocimiento local (RAG)**: carga de documentos y recuperación contextual procesada íntegramente en el navegador.
34
- - **Privacidad absoluta**: tus claves API y datos viajan directamente de tu navegador al proveedor seleccionado, sin servidores puente de terceros.
35
- - **Sin necesidad de empaquetado**: distribuible de forma universal y ligera.
36
-
37
- ## Inicio rápido
38
-
39
- - **Uso web directo**: abre [zerochat.html en GitHub Pages](https://albalday.github.io/zerochat/zerochat.html).
40
- - **Servidor local con herramientas MCP**:
41
- ```bash
42
- python3 zerochat.py
43
- ```
44
- Inicia el asistente local, crea `~/zerochat/.venv` para los MCP y abre la interfaz web en tu navegador.
45
- - **Instalación con PyPI**:
46
- ```bash
47
- pip install zerochat
48
- zerochat
49
- ```
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.
53
-
54
- Las publicaciones se realizan desde el repositorio oficial mediante GitHub Actions y PyPI Trusted Publishing.
55
-
56
- ## Documentación y Ayuda
57
-
58
- Para consultar guías paso a paso, configuración de modelos, perfiles y agentes:
59
-
60
- - 📖 **[Centro de Ayuda de ZeroChat (Español)](https://albalday.github.io/zerochat/help/index.html)**
61
- - 📖 **[ZeroChat Documentation & Help (English)](https://albalday.github.io/zerochat/help/en/index.html)**
62
-
63
- ## Desarrollo
64
-
65
- Para ejecutar las pruebas del proyecto:
66
-
67
- ```bash
68
- npm install
69
- npm test
70
- ```
71
-
72
- Consulta [`AGENTS.md`](./AGENTS.md) para conocer las normas de desarrollo y arquitectura, y [`tests/README.md`](./tests/README.md) para la suite de pruebas.
File without changes
File without changes