ragfly-cli 1.19.0__tar.gz → 1.19.1__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 (27) hide show
  1. {ragfly_cli-1.19.0/ragfly_cli.egg-info → ragfly_cli-1.19.1}/PKG-INFO +14 -14
  2. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/README.md +13 -13
  3. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/pyproject.toml +1 -1
  4. ragfly_cli-1.19.1/ragfly_cli/__init__.py +1 -0
  5. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/_http.py +13 -11
  6. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/cli.py +26 -0
  7. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/grupo_activo.py +1 -1
  8. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/oop/cli_command.py +2 -2
  9. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/oop/http_client.py +2 -2
  10. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/version_check.py +2 -2
  11. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1/ragfly_cli.egg-info}/PKG-INFO +14 -14
  12. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli.egg-info/SOURCES.txt +2 -1
  13. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/tests/test_agent_context_cli.py +13 -0
  14. ragfly_cli-1.19.1/tests/test_api_key_group_override.py +18 -0
  15. ragfly_cli-1.19.0/ragfly_cli/__init__.py +0 -1
  16. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/LICENSE +0 -0
  17. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/__main__.py +0 -0
  18. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/_runtime.py +0 -0
  19. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/cloud_commands.py +0 -0
  20. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/config.py +0 -0
  21. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/keyring_store.py +0 -0
  22. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli/oop/__init__.py +0 -0
  23. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli.egg-info/dependency_links.txt +0 -0
  24. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli.egg-info/entry_points.txt +0 -0
  25. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli.egg-info/requires.txt +0 -0
  26. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/ragfly_cli.egg-info/top_level.txt +0 -0
  27. {ragfly_cli-1.19.0 → ragfly_cli-1.19.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ragfly-cli
3
- Version: 1.19.0
3
+ Version: 1.19.1
4
4
  Summary: RAGfly CLI — operate RAGfly from the terminal and CI (login + cloud API). Lightweight, no desktop dependencies.
5
5
  Author: CAB Ltda
6
6
  License: MIT
@@ -49,24 +49,24 @@ export RAGFLY_API_KEY=rf_xxxxxxxxxx
49
49
  `cloud group` and `cloud api-key` manage a person's session and credentials: they
50
50
  need `ragfly login`, and the server refuses them for an API key.
51
51
 
52
- ## `RAGFLY_ROOT` (optional) — open original files on disk
52
+ ## Opening an original file on disk (optional)
53
53
 
54
- Searching, asking and citing need zero extra config. Only if a script or agent
55
- on this machine must open the **original file** on disk, set `RAGFLY_ROOT` once:
56
- it is the **parent folder** of the folder you selected when uploading your
57
- documents via the web app. RAGfly never reads it nor stores your absolute
58
- path — documents uploaded via browser carry a *relative* `ruta_archivo`, and
59
- the real path is `$RAGFLY_ROOT + ruta_archivo`.
54
+ Searching and citing need no local path setup. A document result may include an
55
+ `fs` object. For a local relative file, read `fs.home_var` and join that
56
+ environment variable's value with `fs.relative_path`. Each root has its own
57
+ variable; configure only the roots available on this machine.
60
58
 
61
59
  ```bash
62
- # Example: you uploaded /Users/ana/Dropbox/MisDocumentos → the PARENT is the root
63
- echo 'export RAGFLY_ROOT="/Users/ana/Dropbox"' >> ~/.zshrc
64
- # "/MisDocumentos/letras/cancion.txt" → /Users/ana/Dropbox/MisDocumentos/letras/cancion.txt
60
+ export RAGFLY_HOME_442681="/Users/ana/Dropbox"
61
+ export RAGFLY_HOME_991203="/Volumes/Archive"
65
62
  ```
66
63
 
67
- Not needed for documents loaded via RAGfly Desktop (their paths are already
68
- absolute). Step-by-step walkthrough:
69
- <https://ragfly.ai/build/mcp#setting-up-ragfly_root--once-per-machine-in-3-steps>
64
+ If `fs.is_cloud_only` is true, use the authorized cloud provider. If
65
+ `fs.is_public_url` is true, open its public URL. If `fs.is_absolute` is true,
66
+ use the absolute path on the machine where it exists. If `fs.home_var` is null
67
+ or unset, do not guess a local root. See the
68
+ [MCP guide](https://ragfly.ai/build/mcp#opening-a-document-on-disk-fs-block)
69
+ for the full order and examples.
70
70
 
71
71
  ## Command surface
72
72
 
@@ -29,24 +29,24 @@ export RAGFLY_API_KEY=rf_xxxxxxxxxx
29
29
  `cloud group` and `cloud api-key` manage a person's session and credentials: they
30
30
  need `ragfly login`, and the server refuses them for an API key.
31
31
 
32
- ## `RAGFLY_ROOT` (optional) — open original files on disk
32
+ ## Opening an original file on disk (optional)
33
33
 
34
- Searching, asking and citing need zero extra config. Only if a script or agent
35
- on this machine must open the **original file** on disk, set `RAGFLY_ROOT` once:
36
- it is the **parent folder** of the folder you selected when uploading your
37
- documents via the web app. RAGfly never reads it nor stores your absolute
38
- path — documents uploaded via browser carry a *relative* `ruta_archivo`, and
39
- the real path is `$RAGFLY_ROOT + ruta_archivo`.
34
+ Searching and citing need no local path setup. A document result may include an
35
+ `fs` object. For a local relative file, read `fs.home_var` and join that
36
+ environment variable's value with `fs.relative_path`. Each root has its own
37
+ variable; configure only the roots available on this machine.
40
38
 
41
39
  ```bash
42
- # Example: you uploaded /Users/ana/Dropbox/MisDocumentos → the PARENT is the root
43
- echo 'export RAGFLY_ROOT="/Users/ana/Dropbox"' >> ~/.zshrc
44
- # "/MisDocumentos/letras/cancion.txt" → /Users/ana/Dropbox/MisDocumentos/letras/cancion.txt
40
+ export RAGFLY_HOME_442681="/Users/ana/Dropbox"
41
+ export RAGFLY_HOME_991203="/Volumes/Archive"
45
42
  ```
46
43
 
47
- Not needed for documents loaded via RAGfly Desktop (their paths are already
48
- absolute). Step-by-step walkthrough:
49
- <https://ragfly.ai/build/mcp#setting-up-ragfly_root--once-per-machine-in-3-steps>
44
+ If `fs.is_cloud_only` is true, use the authorized cloud provider. If
45
+ `fs.is_public_url` is true, open its public URL. If `fs.is_absolute` is true,
46
+ use the absolute path on the machine where it exists. If `fs.home_var` is null
47
+ or unset, do not guess a local root. See the
48
+ [MCP guide](https://ragfly.ai/build/mcp#opening-a-document-on-disk-fs-block)
49
+ for the full order and examples.
50
50
 
51
51
  ## Command surface
52
52
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ragfly-cli"
7
- version = "1.19.0"
7
+ version = "1.19.1"
8
8
  description = "RAGfly CLI — operate RAGfly from the terminal and CI (login + cloud API). Lightweight, no desktop dependencies."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1 @@
1
+ __version__ = "1.19.1"
@@ -41,9 +41,8 @@ def default_headers(
41
41
  Returns:
42
42
  dict de headers listo para pasar a httpx.
43
43
 
44
- El header `X-Override-Grupo` se envía cuando hay grupo activo configurado
45
- o explícito. El backend lo respeta como override de sesión (mismo patrón
46
- que el dropdown de grupo del frontend web).
44
+ El header `X-Override-Grupo` solo se envía para una sesión humana. Una API
45
+ key tiene un grupo fijo en el servidor y nunca hereda el grupo local del CLI.
47
46
  """
48
47
  from ragfly_cli import __version__ as _client_version
49
48
 
@@ -53,14 +52,17 @@ def default_headers(
53
52
  if content_type:
54
53
  headers["Content-Type"] = "application/json"
55
54
 
56
- # Resolver grupo activo: explícito > config
57
- grupo = grupo_override
58
- if grupo is None:
59
- try:
60
- from .config import get_config
61
- grupo = get_config().codigo_grupo or None
62
- except Exception:
63
- grupo = None
55
+ # El override pertenece a la sesión humana. Enviar el grupo guardado en
56
+ # otra sesión con una API key puede dar 403 y nunca debe cambiar su alcance.
57
+ grupo = None
58
+ if not (token and token.startswith(("rf_", "slm_live_"))):
59
+ grupo = grupo_override
60
+ if grupo is None:
61
+ try:
62
+ from .config import get_config
63
+ grupo = get_config().codigo_grupo or None
64
+ except Exception:
65
+ grupo = None
64
66
  if grupo:
65
67
  headers["X-Override-Grupo"] = grupo
66
68
 
@@ -236,6 +236,32 @@ def cloud_me(output: str):
236
236
  ("Active group", "group"), ("Active entity", "entity")])
237
237
 
238
238
 
239
+ # ── cloud entity ────────────────────────────────────────────────────────────
240
+
241
+ @cloud.group("entity")
242
+ def cloud_entity():
243
+ """Active entity focus for the authenticated credential."""
244
+
245
+
246
+ @cloud_entity.command("set")
247
+ @click.argument("entity_code", required=False)
248
+ @click.option("--release", is_flag=True, help="Release the focus and use all authorized entities.")
249
+ @click.option("-o", "--output", type=OUTPUT_TABLE, default="table")
250
+ def cloud_entity_set(entity_code: str | None, release: bool, output: str):
251
+ """Set ENTITY_CODE as active, or use --release to clear the focus."""
252
+ if release == (entity_code is not None):
253
+ raise click.UsageError("Pass exactly one of ENTITY_CODE or --release.")
254
+ data = _v1(
255
+ "POST", "/v1/session/active-entity",
256
+ body={"entity_code": None if release else entity_code},
257
+ )
258
+ if output == "json":
259
+ _emit_json(data)
260
+ return
261
+ active = data.get("active_entity") or "all authorized entities"
262
+ console.print(f"[green]✓ Active entity:[/green] [bold]{active}[/bold]")
263
+
264
+
239
265
  # ── cloud group (signed-in person only) ──────────────────────────────────────
240
266
 
241
267
  @cloud.group("group")
@@ -49,7 +49,7 @@ def set_grupo_activo(codigo_grupo: str) -> None:
49
49
  path = _config_path()
50
50
  if not path.exists():
51
51
  raise FileNotFoundError(
52
- f"No existe {path}. Ejecuta `ragfly setup` primero."
52
+ f"{path} does not exist. Run `ragfly setup` first."
53
53
  )
54
54
 
55
55
  content = path.read_text()
@@ -74,10 +74,10 @@ class CliCommand:
74
74
  except SystemExit:
75
75
  raise
76
76
  except KeyboardInterrupt:
77
- self.aviso("Interrumpido.")
77
+ self.aviso("Interrupted.")
78
78
  raise SystemExit(130)
79
79
  except Exception as e:
80
- self.error(f"Error inesperado: {e}")
80
+ self.error(f"Unexpected error: {e}")
81
81
  raise SystemExit(on_unexpected_exit_code)
82
82
 
83
83
  def salir(self, mensaje: str, exit_code: int = 1) -> NoReturn:
@@ -9,7 +9,7 @@ Encapsula el patrón repetido en `cloud_commands.py`:
9
9
  except httpx.HTTPStatusError as e:
10
10
  _manejar_http_error(e)
11
11
  except httpx.RequestError as e:
12
- raise CloudError(f"Error de conexión: {e}", exit_code=2)
12
+ raise CloudError(f"Could not connect to the server: {e}", exit_code=2)
13
13
 
14
14
  Ejemplo:
15
15
  from ragfly_cli.oop import CloudHttpClient
@@ -99,7 +99,7 @@ class CloudHttpClient:
99
99
  except httpx.HTTPStatusError as e:
100
100
  _manejar_http_error(e)
101
101
  except httpx.RequestError as e:
102
- raise CloudError(f"Error de conexión: {e}", exit_code=2)
102
+ raise CloudError(f"Could not connect to the server: {e}", exit_code=2)
103
103
 
104
104
  # ── Verbos HTTP ───────────────────────────────────────────────────────────
105
105
 
@@ -88,9 +88,9 @@ def chequear_actualizacion(
88
88
 
89
89
  obligatoria = data.get("obligatoria", False)
90
90
  notas = data.get("notas") or ""
91
- prefix = "⚠️ ACTUALIZACIÓN OBLIGATORIA" if obligatoria else "ℹ️ Hay una nueva versión disponible"
91
+ prefix = "⚠️ MANDATORY UPDATE" if obligatoria else "ℹ️ A new version is available"
92
92
 
93
- msg = f"{prefix}: RAGfly Desktop v{latest} (tu versión: v{__version__})"
93
+ msg = f"{prefix}: RAGfly Desktop v{latest} (your version: v{__version__})"
94
94
  if notas:
95
95
  msg += f"\n {notas}"
96
96
  return msg
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ragfly-cli
3
- Version: 1.19.0
3
+ Version: 1.19.1
4
4
  Summary: RAGfly CLI — operate RAGfly from the terminal and CI (login + cloud API). Lightweight, no desktop dependencies.
5
5
  Author: CAB Ltda
6
6
  License: MIT
@@ -49,24 +49,24 @@ export RAGFLY_API_KEY=rf_xxxxxxxxxx
49
49
  `cloud group` and `cloud api-key` manage a person's session and credentials: they
50
50
  need `ragfly login`, and the server refuses them for an API key.
51
51
 
52
- ## `RAGFLY_ROOT` (optional) — open original files on disk
52
+ ## Opening an original file on disk (optional)
53
53
 
54
- Searching, asking and citing need zero extra config. Only if a script or agent
55
- on this machine must open the **original file** on disk, set `RAGFLY_ROOT` once:
56
- it is the **parent folder** of the folder you selected when uploading your
57
- documents via the web app. RAGfly never reads it nor stores your absolute
58
- path — documents uploaded via browser carry a *relative* `ruta_archivo`, and
59
- the real path is `$RAGFLY_ROOT + ruta_archivo`.
54
+ Searching and citing need no local path setup. A document result may include an
55
+ `fs` object. For a local relative file, read `fs.home_var` and join that
56
+ environment variable's value with `fs.relative_path`. Each root has its own
57
+ variable; configure only the roots available on this machine.
60
58
 
61
59
  ```bash
62
- # Example: you uploaded /Users/ana/Dropbox/MisDocumentos → the PARENT is the root
63
- echo 'export RAGFLY_ROOT="/Users/ana/Dropbox"' >> ~/.zshrc
64
- # "/MisDocumentos/letras/cancion.txt" → /Users/ana/Dropbox/MisDocumentos/letras/cancion.txt
60
+ export RAGFLY_HOME_442681="/Users/ana/Dropbox"
61
+ export RAGFLY_HOME_991203="/Volumes/Archive"
65
62
  ```
66
63
 
67
- Not needed for documents loaded via RAGfly Desktop (their paths are already
68
- absolute). Step-by-step walkthrough:
69
- <https://ragfly.ai/build/mcp#setting-up-ragfly_root--once-per-machine-in-3-steps>
64
+ If `fs.is_cloud_only` is true, use the authorized cloud provider. If
65
+ `fs.is_public_url` is true, open its public URL. If `fs.is_absolute` is true,
66
+ use the absolute path on the machine where it exists. If `fs.home_var` is null
67
+ or unset, do not guess a local root. See the
68
+ [MCP guide](https://ragfly.ai/build/mcp#opening-a-document-on-disk-fs-block)
69
+ for the full order and examples.
70
70
 
71
71
  ## Command surface
72
72
 
@@ -20,4 +20,5 @@ ragfly_cli.egg-info/top_level.txt
20
20
  ragfly_cli/oop/__init__.py
21
21
  ragfly_cli/oop/cli_command.py
22
22
  ragfly_cli/oop/http_client.py
23
- tests/test_agent_context_cli.py
23
+ tests/test_agent_context_cli.py
24
+ tests/test_api_key_group_override.py
@@ -47,6 +47,19 @@ def test_every_cloud_call_goes_to_v1_with_the_cli_surface(wire):
47
47
  assert call["headers"]["X-RAGfly-Client"] == "cli"
48
48
 
49
49
 
50
+ def test_entity_set_and_release_use_the_single_public_session_operation(wire):
51
+ wire.reply = httpx.Response(200, json={"active_entity": "E1"})
52
+ result = run("cloud", "entity", "set", "E1", "-o", "json")
53
+ assert result.exit_code == 0, result.output
54
+ assert wire.calls[0]["url"].endswith("/v1/session/active-entity")
55
+ assert wire.calls[0]["json"] == {"entity_code": "E1"}
56
+
57
+ wire.reply = httpx.Response(200, json={"active_entity": None})
58
+ result = run("cloud", "entity", "set", "--release", "-o", "json")
59
+ assert result.exit_code == 0, result.output
60
+ assert wire.calls[1]["json"] == {"entity_code": None}
61
+
62
+
50
63
  def test_agent_context_and_tool(wire):
51
64
  wire.reply = httpx.Response(200, json={"function_profile": "support_chat", "system_prompt_hash": "abc", "tools": []})
52
65
  result = run("cloud", "agent", "context", "--profile", "support_chat")
@@ -0,0 +1,18 @@
1
+ from types import SimpleNamespace
2
+
3
+ from ragfly_cli import config
4
+ from ragfly_cli._http import default_headers
5
+
6
+
7
+ def test_api_key_never_inherits_a_saved_group(monkeypatch):
8
+ monkeypatch.setattr(config, "get_config", lambda: SimpleNamespace(codigo_grupo="OTHER-GROUP"))
9
+ for token in ("rf_fixture", "slm_live_fixture"):
10
+ headers = default_headers(token=token)
11
+ assert headers["Authorization"] == f"Bearer {token}"
12
+ assert "X-Override-Grupo" not in headers
13
+
14
+
15
+ def test_human_session_keeps_its_saved_group(monkeypatch):
16
+ monkeypatch.setattr(config, "get_config", lambda: SimpleNamespace(codigo_grupo="MY-GROUP"))
17
+ headers = default_headers(token="jwt.header.signature")
18
+ assert headers["X-Override-Grupo"] == "MY-GROUP"
@@ -1 +0,0 @@
1
- __version__ = "1.19.0"
File without changes
File without changes