fluxloop-cli 0.3.3__tar.gz → 0.3.5__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.
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/PKG-INFO +1 -1
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/__init__.py +1 -1
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/api_utils.py +32 -9
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/__init__.py +4 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/auth.py +6 -13
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/config.py +3 -8
- fluxloop_cli-0.3.5/fluxloop_cli/commands/data.py +657 -0
- fluxloop_cli-0.3.5/fluxloop_cli/commands/evaluate.py +188 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/inputs.py +17 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/personas.py +14 -4
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/projects.py +8 -2
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/sync.py +46 -13
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/test.py +52 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/context_manager.py +3 -3
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/conversation_supervisor.py +63 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/main.py +4 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/templates.py +1 -2
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/turns.py +23 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli.egg-info/PKG-INFO +1 -1
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli.egg-info/SOURCES.txt +2 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/pyproject.toml +1 -1
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/README.md +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/arg_binder.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/auth_manager.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/apikeys.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/bundles.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/context.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/criteria.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/generate.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/init.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/local_context.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/run.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/scenarios.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/commands/status.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/common_options.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/config_loader.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/config_schema.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/constants.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/environment.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/http_client.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/input_generator.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/llm_generator.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/project_paths.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/runner.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/target_loader.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/testing/__init__.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/testing/pytest_plugin.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/testing/types.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/token_usage.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli/validators.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli.egg-info/dependency_links.txt +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli.egg-info/entry_points.txt +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli.egg-info/requires.txt +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/fluxloop_cli.egg-info/top_level.txt +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/setup.cfg +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_analysis_recommendations.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_arg_binder.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_config_command.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_conversation_supervisor.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_evaluate_command.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_evaluation_llm.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_init_pytest_template.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_input_generator.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_prompt_library.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_pytest_plugin.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_run_command.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_runner_multi_turn.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_success_criteria.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_target_loader.py +0 -0
- {fluxloop_cli-0.3.3 → fluxloop_cli-0.3.5}/tests/test_testing_types.py +0 -0
|
@@ -20,18 +20,41 @@ from rich.console import Console
|
|
|
20
20
|
console = Console()
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
PRODUCTION_API_URL = "https://api.fluxloop.ai"
|
|
24
|
+
STAGING_API_URL = "https://staging.api.fluxloop.ai"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _read_project_json_api_url() -> Optional[str]:
|
|
28
|
+
"""Read api_url from .fluxloop/project.json if it exists."""
|
|
29
|
+
try:
|
|
30
|
+
from .context_manager import load_project_connection
|
|
31
|
+
|
|
32
|
+
conn = load_project_connection()
|
|
33
|
+
if conn and conn.api_url:
|
|
34
|
+
return conn.api_url
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def resolve_api_url(
|
|
41
|
+
override: Optional[str] = None,
|
|
42
|
+
*,
|
|
43
|
+
staging: bool = False,
|
|
44
|
+
) -> str:
|
|
24
45
|
"""
|
|
25
|
-
Resolve API URL
|
|
46
|
+
Resolve API URL with a single, unified priority chain.
|
|
26
47
|
|
|
27
48
|
Priority:
|
|
28
|
-
1.
|
|
29
|
-
2. FLUXLOOP_API_URL
|
|
30
|
-
3.
|
|
31
|
-
4.
|
|
49
|
+
1. ``override`` parameter (--api-url)
|
|
50
|
+
2. FLUXLOOP_API_URL environment variable
|
|
51
|
+
3. .fluxloop/project.json ``api_url``
|
|
52
|
+
4. ``--staging`` flag → staging URL
|
|
53
|
+
5. Hardcoded production URL (final fallback)
|
|
32
54
|
|
|
33
55
|
Args:
|
|
34
|
-
override: Optional URL override.
|
|
56
|
+
override: Optional URL override (--api-url).
|
|
57
|
+
staging: When True, use staging URL as fallback instead of production.
|
|
35
58
|
|
|
36
59
|
Returns:
|
|
37
60
|
Resolved API URL (trailing slash removed).
|
|
@@ -39,8 +62,8 @@ def resolve_api_url(override: Optional[str]) -> str:
|
|
|
39
62
|
url = (
|
|
40
63
|
override
|
|
41
64
|
or os.getenv("FLUXLOOP_API_URL")
|
|
42
|
-
or
|
|
43
|
-
or
|
|
65
|
+
or _read_project_json_api_url()
|
|
66
|
+
or (STAGING_API_URL if staging else PRODUCTION_API_URL)
|
|
44
67
|
)
|
|
45
68
|
return url.rstrip("/")
|
|
46
69
|
|
|
@@ -16,6 +16,7 @@ from rich.panel import Panel
|
|
|
16
16
|
from rich.spinner import Spinner
|
|
17
17
|
from rich.live import Live
|
|
18
18
|
|
|
19
|
+
from ..api_utils import resolve_api_url
|
|
19
20
|
from ..auth_manager import (
|
|
20
21
|
start_device_code_flow,
|
|
21
22
|
poll_device_code,
|
|
@@ -35,17 +36,6 @@ app = typer.Typer(help="Manage FluxLoop authentication")
|
|
|
35
36
|
console = Console()
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def _resolve_api_url(override: Optional[str]) -> str:
|
|
39
|
-
"""Resolve API URL from override or environment variables."""
|
|
40
|
-
url = (
|
|
41
|
-
override
|
|
42
|
-
or os.getenv("FLUXLOOP_API_URL")
|
|
43
|
-
or os.getenv("FLUXLOOP_SYNC_URL")
|
|
44
|
-
or "https://api.fluxloop.ai"
|
|
45
|
-
)
|
|
46
|
-
return url.rstrip("/")
|
|
47
|
-
|
|
48
|
-
|
|
49
39
|
def _is_agent_environment() -> bool:
|
|
50
40
|
"""Detect if running in an agent/headless environment."""
|
|
51
41
|
# Check common agent environment indicators
|
|
@@ -63,6 +53,9 @@ def login(
|
|
|
63
53
|
api_url: Optional[str] = typer.Option(
|
|
64
54
|
None, "--api-url", help="FluxLoop API base URL"
|
|
65
55
|
),
|
|
56
|
+
staging: bool = typer.Option(
|
|
57
|
+
False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
|
|
58
|
+
),
|
|
66
59
|
force: bool = typer.Option(
|
|
67
60
|
False, "--force", "-f", help="Force re-login even if already logged in"
|
|
68
61
|
),
|
|
@@ -87,7 +80,7 @@ def login(
|
|
|
87
80
|
unless --force is specified. This makes the command idempotent and
|
|
88
81
|
safe to call from automated scripts or AI agents.
|
|
89
82
|
"""
|
|
90
|
-
api_url =
|
|
83
|
+
api_url = resolve_api_url(api_url, staging=staging)
|
|
91
84
|
|
|
92
85
|
# Check if already logged in (idempotent behavior)
|
|
93
86
|
if not force:
|
|
@@ -290,7 +283,7 @@ def status():
|
|
|
290
283
|
If token is expired or about to expire, automatically attempts to refresh.
|
|
291
284
|
Only suggests re-login if refresh fails.
|
|
292
285
|
"""
|
|
293
|
-
api_url =
|
|
286
|
+
api_url = resolve_api_url()
|
|
294
287
|
token = load_auth_token()
|
|
295
288
|
|
|
296
289
|
if not token:
|
|
@@ -191,8 +191,7 @@ def env(
|
|
|
191
191
|
loaded_paths = []
|
|
192
192
|
|
|
193
193
|
env_vars = [
|
|
194
|
-
("
|
|
195
|
-
("FLUXLOOP_API_KEY", "API key for authentication", None),
|
|
194
|
+
("FLUXLOOP_API_URL", "API base URL override (optional, project.json is primary)", None),
|
|
196
195
|
("FLUXLOOP_SYNC_API_KEY", "Sync API key for uploads", None),
|
|
197
196
|
("FLUXLOOP_ENABLED", "Enable/disable tracing", "true"),
|
|
198
197
|
("FLUXLOOP_DEBUG", "Enable debug mode", "false"),
|
|
@@ -327,13 +326,12 @@ def set_llm(
|
|
|
327
326
|
@app.command()
|
|
328
327
|
def set_sync_key(
|
|
329
328
|
api_key: str = typer.Argument(..., help="Sync API key"),
|
|
330
|
-
api_url: Optional[str] = typer.Option(None, "--url", help="Sync API base URL"),
|
|
331
329
|
overwrite_env: bool = typer.Option(False, "--overwrite-env", help="Overwrite existing key in .env"),
|
|
332
330
|
env_file: Path = typer.Option(Path(".env"), "--env-file", help="Path to environment file"),
|
|
333
331
|
project: Optional[str] = typer.Option(None, "--project", help="Project name under the FluxLoop root"),
|
|
334
332
|
root: Path = typer.Option(Path(DEFAULT_ROOT_DIR_NAME), "--root", help="FluxLoop root directory"),
|
|
335
333
|
):
|
|
336
|
-
"""Save Sync API
|
|
334
|
+
"""Save Sync API key to .env."""
|
|
337
335
|
|
|
338
336
|
env_path = resolve_env_path(env_file, project, root)
|
|
339
337
|
env_contents: Dict[str, str] = {}
|
|
@@ -351,12 +349,9 @@ def set_sync_key(
|
|
|
351
349
|
else:
|
|
352
350
|
env_contents["FLUXLOOP_SYNC_API_KEY"] = api_key
|
|
353
351
|
|
|
354
|
-
if api_url:
|
|
355
|
-
env_contents["FLUXLOOP_SYNC_URL"] = api_url
|
|
356
|
-
|
|
357
352
|
env_lines = [f"{key}={value}" for key, value in env_contents.items()]
|
|
358
353
|
env_path.write_text("\n".join(env_lines) + "\n")
|
|
359
|
-
console.print(f"[green]✓[/green] Saved sync
|
|
354
|
+
console.print(f"[green]✓[/green] Saved sync API key to {env_path}")
|
|
360
355
|
|
|
361
356
|
|
|
362
357
|
@app.command()
|