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.
- ragfly_cli/__init__.py +1 -0
- ragfly_cli/__main__.py +5 -0
- ragfly_cli/_http.py +67 -0
- ragfly_cli/cli.py +918 -0
- ragfly_cli/cloud_commands.py +201 -0
- ragfly_cli/config.py +102 -0
- ragfly_cli/grupo_activo.py +142 -0
- ragfly_cli/keyring_store.py +70 -0
- ragfly_cli/oop/__init__.py +12 -0
- ragfly_cli/oop/cli_command.py +86 -0
- ragfly_cli/oop/http_client.py +106 -0
- ragfly_cli/version_check.py +96 -0
- ragfly_cli-1.16.0.dist-info/METADATA +73 -0
- ragfly_cli-1.16.0.dist-info/RECORD +17 -0
- ragfly_cli-1.16.0.dist-info/WHEEL +5 -0
- ragfly_cli-1.16.0.dist-info/entry_points.txt +2 -0
- ragfly_cli-1.16.0.dist-info/top_level.txt +1 -0
|
@@ -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 @@
|
|
|
1
|
+
ragfly_cli
|