ragfly-cli 1.16.0__py3-none-any.whl

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,106 @@
1
+ """
2
+ CloudHttpClient — wrapper HTTP unificado para la API cloud.
3
+
4
+ Encapsula el patrón repetido en `cloud_commands.py`:
5
+ try:
6
+ r = httpx.METODO(...)
7
+ r.raise_for_status()
8
+ return r.json()
9
+ except httpx.HTTPStatusError as e:
10
+ _manejar_http_error(e)
11
+ except httpx.RequestError as e:
12
+ raise CloudError(f"Error de conexión: {e}", exit_code=2)
13
+
14
+ Ejemplo:
15
+ from ragfly_cli.oop import CloudHttpClient
16
+ cli = CloudHttpClient()
17
+ me = cli.get("/auth/me")
18
+ cli.post("/cloud/algo", body={"x": 1})
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from typing import Any, Optional
24
+
25
+ import httpx
26
+
27
+ # Import diferido para evitar ciclo
28
+ from ragfly_cli.cloud_commands import (
29
+ CLOUD_URL,
30
+ CloudError,
31
+ _headers,
32
+ _manejar_http_error,
33
+ )
34
+
35
+
36
+ class CloudHttpClient:
37
+ """Cliente HTTP unificado contra la API cloud."""
38
+
39
+ def __init__(
40
+ self,
41
+ url: str = CLOUD_URL,
42
+ *,
43
+ timeout_get: int = 30,
44
+ timeout_write: int = 60,
45
+ ):
46
+ self.url = url
47
+ self.timeout_get = timeout_get
48
+ self.timeout_write = timeout_write
49
+
50
+ # ── Helper interno ────────────────────────────────────────────────────────
51
+
52
+ def _request(
53
+ self,
54
+ method: str,
55
+ path: str,
56
+ *,
57
+ params: Optional[dict] = None,
58
+ body: Optional[dict] = None,
59
+ token: Optional[str] = None,
60
+ timeout: Optional[int] = None,
61
+ ) -> Any:
62
+ """Ejecuta el request con manejo uniforme de errores."""
63
+ method = method.upper()
64
+ is_write = method in ("POST", "PUT", "PATCH", "DELETE")
65
+ final_timeout = timeout if timeout is not None else (
66
+ self.timeout_write if is_write else self.timeout_get
67
+ )
68
+
69
+ try:
70
+ kwargs: dict = {
71
+ "params": params,
72
+ "headers": _headers(token),
73
+ "timeout": final_timeout,
74
+ }
75
+ if is_write and method != "DELETE":
76
+ kwargs["json"] = body or {}
77
+
78
+ r = httpx.request(method, f"{self.url}{path}", **kwargs)
79
+ r.raise_for_status()
80
+ if r.status_code == 204 or not r.content:
81
+ return None
82
+ try:
83
+ return r.json()
84
+ except Exception:
85
+ return r.text
86
+ except httpx.HTTPStatusError as e:
87
+ _manejar_http_error(e)
88
+ except httpx.RequestError as e:
89
+ raise CloudError(f"Error de conexión: {e}", exit_code=2)
90
+
91
+ # ── Verbos HTTP ───────────────────────────────────────────────────────────
92
+
93
+ def get(self, path: str, *, params: Optional[dict] = None, token: Optional[str] = None, timeout: Optional[int] = None) -> Any:
94
+ return self._request("GET", path, params=params, token=token, timeout=timeout)
95
+
96
+ def post(self, path: str, *, body: Optional[dict] = None, params: Optional[dict] = None, token: Optional[str] = None, timeout: Optional[int] = None) -> Any:
97
+ return self._request("POST", path, body=body, params=params, token=token, timeout=timeout)
98
+
99
+ def put(self, path: str, *, body: Optional[dict] = None, params: Optional[dict] = None, token: Optional[str] = None, timeout: Optional[int] = None) -> Any:
100
+ return self._request("PUT", path, body=body, params=params, token=token, timeout=timeout)
101
+
102
+ def patch(self, path: str, *, body: Optional[dict] = None, params: Optional[dict] = None, token: Optional[str] = None, timeout: Optional[int] = None) -> Any:
103
+ return self._request("PATCH", path, body=body, params=params, token=token, timeout=timeout)
104
+
105
+ def delete(self, path: str, *, params: Optional[dict] = None, token: Optional[str] = None, timeout: Optional[int] = None) -> Any:
106
+ return self._request("DELETE", path, params=params, token=token, timeout=timeout)
@@ -0,0 +1,96 @@
1
+ """
2
+ Check de versión del RAGfly Cliente.
3
+
4
+ Consulta GET /version/cliente/latest al backend y compara con la versión actual.
5
+ Si hay una versión nueva, retorna un mensaje legible para mostrar al usuario.
6
+ Si no hay nueva versión o el backend no responde, retorna None silenciosamente
7
+ (la app sigue funcionando sin red).
8
+
9
+ Uso:
10
+
11
+ from ragfly_cli.version_check import chequear_actualizacion
12
+ aviso = chequear_actualizacion()
13
+ if aviso:
14
+ click.echo(aviso, err=True)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import re
20
+ from typing import Optional
21
+
22
+ import httpx
23
+
24
+ from . import __version__
25
+ from ._http import default_headers
26
+
27
+ _RE_VERSION = re.compile(r"^(\d+)\.(\d+)\.(\d+)(?:(a|b|rc)(\d+))?")
28
+ _RANGO_PRE = {"a": 0, "b": 1, "rc": 2} # final (sin pre) = 3, por encima de todas
29
+
30
+
31
+ def _version_tuple(v: str) -> tuple[int, ...]:
32
+ """Ordena versiones PEP 440 simples: 2.0.0a8 < 2.0.0a9 < 2.0.0b1 < 2.0.0rc1 < 2.0.0.
33
+
34
+ Devuelve (major, minor, patch, rango_pre, num_pre). El sufijo alfa/beta/rc
35
+ YA NO se ignora (antes colapsaba a (0,) y el aviso de upgrade nunca se
36
+ disparaba entre versiones aN). Una versión final ranquea por encima de sus
37
+ pre-releases (rango 3 > 2/1/0).
38
+ """
39
+ m = _RE_VERSION.match(v.strip())
40
+ if not m:
41
+ return (0,)
42
+ major, minor, patch = int(m.group(1)), int(m.group(2)), int(m.group(3))
43
+ rango = _RANGO_PRE.get(m.group(4), 3)
44
+ num_pre = int(m.group(5)) if m.group(5) else 0
45
+ return (major, minor, patch, rango, num_pre)
46
+
47
+
48
+ def chequear_actualizacion(
49
+ *,
50
+ cloud_url: Optional[str] = None,
51
+ timeout: int = 5,
52
+ ) -> Optional[str]:
53
+ """Consulta /version/cliente/latest. Si hay versión nueva, retorna aviso.
54
+
55
+ Returns:
56
+ - str con mensaje "Hay una nueva versión..." si versión actual < latest.
57
+ - None si está al día, si no hay red, o si el endpoint no existe (404).
58
+
59
+ Nota: silencioso ante errores de red — la app no debe romperse por esto.
60
+ """
61
+ if cloud_url is None:
62
+ try:
63
+ from .config import get_config
64
+ cloud_url = get_config().cloud_url
65
+ except Exception:
66
+ return None
67
+
68
+ url = f"{cloud_url.rstrip('/')}/version/cliente/latest"
69
+ try:
70
+ r = httpx.get(url, headers=default_headers(), timeout=timeout)
71
+ except Exception:
72
+ return None # sin red u otro error: silencioso
73
+
74
+ if r.status_code != 200:
75
+ return None
76
+
77
+ try:
78
+ data = r.json()
79
+ except Exception:
80
+ return None
81
+
82
+ latest = data.get("version")
83
+ if not latest:
84
+ return None
85
+
86
+ if _version_tuple(__version__) >= _version_tuple(latest):
87
+ return None # estamos al día o más nuevos
88
+
89
+ obligatoria = data.get("obligatoria", False)
90
+ notas = data.get("notas") or ""
91
+ prefix = "⚠️ ACTUALIZACIÓN OBLIGATORIA" if obligatoria else "ℹ️ Hay una nueva versión disponible"
92
+
93
+ msg = f"{prefix}: RAGfly Desktop v{latest} (tu versión: v{__version__})"
94
+ if notas:
95
+ msg += f"\n {notas}"
96
+ return msg
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: ragfly-cli
3
+ Version: 1.16.0
4
+ Summary: RAGfly CLI — operate RAGfly from the terminal and CI (login + cloud API). Lightweight, no desktop dependencies.
5
+ Author: CAB Ltda
6
+ License: Proprietary
7
+ Project-URL: Homepage, https://ragfly.ai
8
+ Project-URL: Documentation, https://api.ragfly.ai/docs
9
+ Keywords: ragfly,rag,cli,retrieval,ai,mcp
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: click>=8.1
13
+ Requires-Dist: rich>=13.0
14
+ Requires-Dist: httpx>=0.27
15
+ Requires-Dist: keyring>=25.0
16
+ Requires-Dist: pydantic>=2.0
17
+ Requires-Dist: pydantic-settings>=2.6
18
+
19
+ # RAGfly CLI
20
+
21
+ Operate RAGfly from the terminal and CI — `login` + the full `cloud` API surface
22
+ against `api.ragfly.ai`. Lightweight: depends only on `click`, `rich`, `httpx`
23
+ and `keyring`. **No desktop / PySide6 / OCR dependencies.**
24
+
25
+ ```bash
26
+ pip install ragfly-cli
27
+ ragfly version
28
+ ragfly login
29
+ ragfly cloud me
30
+ ```
31
+
32
+ > This package ships the **`ragfly` binary** for scripting and automation.
33
+ > It is distinct from `pip install ragfly` (the **Python SDK**, import `import ragfly`)
34
+ > and from **RAGfly Desktop** (the DMG/exe that bundles the local file worker for
35
+ > `ragfly local scan/sync/daemon`).
36
+
37
+ ## Authentication
38
+
39
+ ```bash
40
+ # Interactive (JWT, stored in the OS keyring)
41
+ ragfly login
42
+
43
+ # Non-interactive / CI (API key, no expiry)
44
+ export RAGFLY_TOKEN=slm_live_xxxxxxxxxx
45
+ ```
46
+
47
+ ## Command surface
48
+
49
+ ```
50
+ ragfly
51
+ ├── login / logout / version
52
+ └── cloud ← operations against api.ragfly.ai
53
+ ├── me
54
+ ├── grupo listar | cambiar | limpiar
55
+ ├── documento listar | ver
56
+ ├── espacio listar | ver
57
+ ├── cola ver | ejecuciones
58
+ ├── habilidad listar | ver | ejecutar
59
+ ├── catalogo
60
+ ├── buscar
61
+ └── chat preguntar
62
+ ```
63
+
64
+ Full reference: <https://api.ragfly.ai/docs> and `docs/integradores/CLI.md`.
65
+
66
+ > **Local operations** (`ragfly local scan/sync/daemon`) require the local file
67
+ > worker and ship with **RAGfly Desktop**, not with this package.
68
+
69
+ ## Source
70
+
71
+ The command logic is extracted from the RAGfly Desktop client
72
+ (`cliente/ragfly/`). Canonical source of each module lives there; keep this
73
+ package's copies in sync when the cloud command surface changes.
@@ -0,0 +1,17 @@
1
+ ragfly_cli/__init__.py,sha256=zXjo5icgcGx5X40awUvUOk5_ohh0Pz1N3VFKYCfYEcM,23
2
+ ragfly_cli/__main__.py,sha256=DBYMZTzAAf_L8mKAB-tea6WqnNAeMMReFFDUuCRIFM4,107
3
+ ragfly_cli/_http.py,sha256=XkRMvauPJO_ZWT-Hf8vo2h-LDtRnAOf_0dBLcjQD0bE,2262
4
+ ragfly_cli/cli.py,sha256=9OsdxDpV0qHtAyqKQwseFWogPCNYsEP3jHeiNuBqq5Y,36721
5
+ ragfly_cli/cloud_commands.py,sha256=M-emTLhWbx2dbmg6zAhLc_r-P8u4IXTD27J_b1vFIes,7138
6
+ ragfly_cli/config.py,sha256=mUXFgOls6ww7lA6LQw2MPchUTAF7SqqfOKU9L5mVuSA,3578
7
+ ragfly_cli/grupo_activo.py,sha256=nFMWtQc7WoUB2mLeCnZihMSVQLXo7YPaVRYCvjLgjnY,4599
8
+ ragfly_cli/keyring_store.py,sha256=_EERcMoVl6FKwWu4pt4wtz3EHvI01MBfun--fuRng0Y,2143
9
+ ragfly_cli/version_check.py,sha256=mluP0v7oVyFZ5BEJ0FwU_FZFVO88AVtmvy1RhaTmXwg,3005
10
+ ragfly_cli/oop/__init__.py,sha256=AFp0nkOgQu0MgIi6JvyigcY8DDfn6pxHeEGNBqPyFME,414
11
+ ragfly_cli/oop/cli_command.py,sha256=IpFaB0LEd6CG2KutorBHkXcAy1YgquPEqKjXhsru-gU,2748
12
+ ragfly_cli/oop/http_client.py,sha256=9kpmwr4wPsLAnY1bKUKad057oYpkPuVKNvcR6wZVeBE,4036
13
+ ragfly_cli-1.16.0.dist-info/METADATA,sha256=7zb23zm8_59mW8_5T-_Xm_8qiDOinAAh9Ixs8l_UU1I,2273
14
+ ragfly_cli-1.16.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
15
+ ragfly_cli-1.16.0.dist-info/entry_points.txt,sha256=_4aVAF9r1xKUjOI-XDjeWOgyEEmp60POOQkx8Qz7y9M,46
16
+ ragfly_cli-1.16.0.dist-info/top_level.txt,sha256=xFhtEPu_3-oWUbNi36KnBkri5RwLrLBiI8GPrM88BNk,11
17
+ ragfly_cli-1.16.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ragfly = ragfly_cli.cli:app
@@ -0,0 +1 @@
1
+ ragfly_cli