arga-cli 0.1.7__tar.gz → 0.1.9__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. {arga_cli-0.1.7 → arga_cli-0.1.9}/PKG-INFO +1 -1
  2. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/main.py +57 -3
  3. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/wizard/__init__.py +13 -1
  4. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/wizard/provision.py +10 -1
  5. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli.egg-info/PKG-INFO +1 -1
  6. {arga_cli-0.1.7 → arga_cli-0.1.9}/pyproject.toml +1 -1
  7. {arga_cli-0.1.7 → arga_cli-0.1.9}/README.md +0 -0
  8. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/__init__.py +0 -0
  9. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/mcp.py +0 -0
  10. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/wizard/constants.py +0 -0
  11. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/wizard/env.py +0 -0
  12. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/wizard/output.py +0 -0
  13. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/wizard/prompts.py +0 -0
  14. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/wizard/session.py +0 -0
  15. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli/wizard/summary.py +0 -0
  16. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli.egg-info/SOURCES.txt +0 -0
  17. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli.egg-info/dependency_links.txt +0 -0
  18. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli.egg-info/entry_points.txt +0 -0
  19. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli.egg-info/requires.txt +0 -0
  20. {arga_cli-0.1.7 → arga_cli-0.1.9}/arga_cli.egg-info/top_level.txt +0 -0
  21. {arga_cli-0.1.7 → arga_cli-0.1.9}/setup.cfg +0 -0
  22. {arga_cli-0.1.7 → arga_cli-0.1.9}/tests/test_cli_git.py +0 -0
  23. {arga_cli-0.1.7 → arga_cli-0.1.9}/tests/test_cli_mcp.py +0 -0
  24. {arga_cli-0.1.7 → arga_cli-0.1.9}/tests/test_cli_runs.py +0 -0
  25. {arga_cli-0.1.7 → arga_cli-0.1.9}/tests/test_cli_test_url.py +0 -0
  26. {arga_cli-0.1.7 → arga_cli-0.1.9}/tests/test_cli_validate_config.py +0 -0
  27. {arga_cli-0.1.7 → arga_cli-0.1.9}/tests/test_cli_validate_pr.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arga-cli
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Command-line interface for Arga authentication, MCP installation, and browser validation
5
5
  Author: Arga Labs
6
6
  Project-URL: Homepage, https://github.com/ArgaLabs/arga-cli
@@ -36,6 +36,44 @@ def _cli_version() -> str:
36
36
  return "unknown"
37
37
 
38
38
 
39
+ VERSION_CHECK_PATH = Path.home() / ".config" / "arga" / "version_check.json"
40
+ VERSION_CHECK_TTL_SECONDS = 86400 # 24 hours
41
+
42
+
43
+ def _check_for_update() -> None:
44
+ """Print a warning if a newer version is available on PyPI. Caches for 24h."""
45
+ try:
46
+ current = _cli_version()
47
+ if current == "unknown":
48
+ return
49
+
50
+ now = time.time()
51
+ cached_latest: str | None = None
52
+ if VERSION_CHECK_PATH.exists():
53
+ data = json.loads(VERSION_CHECK_PATH.read_text())
54
+ if now - data.get("checked_at", 0) < VERSION_CHECK_TTL_SECONDS:
55
+ cached_latest = data.get("latest")
56
+
57
+ if cached_latest is None:
58
+ resp = httpx.get("https://pypi.org/pypi/arga-cli/json", timeout=3.0)
59
+ resp.raise_for_status()
60
+ cached_latest = resp.json()["info"]["version"]
61
+ VERSION_CHECK_PATH.parent.mkdir(parents=True, exist_ok=True)
62
+ VERSION_CHECK_PATH.write_text(json.dumps({"latest": cached_latest, "checked_at": now}))
63
+
64
+ if cached_latest and cached_latest != current:
65
+ latest_parts = tuple(int(x) for x in cached_latest.split("."))
66
+ current_parts = tuple(int(x) for x in current.split("."))
67
+ if latest_parts > current_parts:
68
+ print(
69
+ f"\033[33mwarning: arga-cli {cached_latest} available (you have {current}). "
70
+ f"Update with: uv tool upgrade arga-cli\033[0m",
71
+ file=sys.stderr,
72
+ )
73
+ except Exception:
74
+ pass
75
+
76
+
39
77
  class CliError(Exception):
40
78
  """Base CLI error."""
41
79
 
@@ -275,7 +313,8 @@ class ApiClient:
275
313
  if response.status_code == 401:
276
314
  raise NotAuthenticatedError("Error: Not authenticated. Run `arga login`.")
277
315
  if response.status_code == 403:
278
- message += " Your plan may not support this feature. Check with `arga whoami`."
316
+ if not detail:
317
+ message += " Your plan may not support this feature. Check with `arga whoami`."
279
318
  elif response.status_code == 404:
280
319
  message += " Check that your plan supports this feature with `arga whoami`."
281
320
  elif response.status_code == 429:
@@ -482,11 +521,16 @@ def _print_twin_env_vars(status: dict) -> None:
482
521
  from arga_cli.wizard.provision import with_proxy_token
483
522
 
484
523
  proxy_token = status.get("proxy_token")
524
+ # Public `pub-` hosts don't do proxy auth, so the base_url is directly
525
+ # callable from any native SDK (Slack, Stripe, Discord, …). Appending
526
+ # `?token=…` would be harmless but misleading — it'd suggest the token
527
+ # is required, which is exactly the friction public twins eliminate.
528
+ is_public = bool(status.get("is_public"))
485
529
  print("\nTwin environment variables — update your app's config to point at these:\n")
486
530
  for name, info in status.get("twins", {}).items():
487
531
  label = info.get("label", name)
488
532
  base_url = info.get("base_url", "")
489
- if proxy_token and base_url:
533
+ if not is_public and proxy_token and base_url:
490
534
  base_url = with_proxy_token(base_url, proxy_token)
491
535
  print(f" {label}:")
492
536
  print(f" Base URL: {base_url}")
@@ -1337,6 +1381,7 @@ def _wizard_help_text() -> str:
1337
1381
  " env Re-run .env rewriting step\n\n"
1338
1382
  "Options:\n"
1339
1383
  " --api-url API base URL\n"
1384
+ " --ttl MINUTES Session TTL in minutes (paid/team: 1-480, default 60; free: fixed 10)\n"
1340
1385
  " --no-shape-detect Disable heuristic detection of API keys by value pattern\n"
1341
1386
  " -h, --help Show this help"
1342
1387
  )
@@ -1345,6 +1390,7 @@ def _wizard_help_text() -> str:
1345
1390
  def _build_wizard_init_parser() -> argparse.ArgumentParser:
1346
1391
  parser = argparse.ArgumentParser(prog="arga wizard", allow_abbrev=False)
1347
1392
  parser.add_argument("--api-url", default=DEFAULT_API_URL, help="Arga API base URL")
1393
+ parser.add_argument("--ttl", type=int, default=None, help="Session TTL in minutes (paid/team: 1-480, default 60)")
1348
1394
  parser.add_argument("--no-shape-detect", action="store_true", default=False)
1349
1395
  return parser
1350
1396
 
@@ -1369,6 +1415,7 @@ def run_wizard_init(args: argparse.Namespace) -> int:
1369
1415
  api_key=api_key,
1370
1416
  cwd=os.getcwd(),
1371
1417
  shape_detect=not getattr(args, "no_shape_detect", False),
1418
+ ttl_minutes=args.ttl,
1372
1419
  )
1373
1420
 
1374
1421
 
@@ -1396,9 +1443,15 @@ def run_wizard_status(_args: argparse.Namespace) -> int:
1396
1443
  f"Status: {'[green]' + status['status'] + '[/green]' if status['status'] == 'ready' else '[yellow]' + status['status'] + '[/yellow]'}",
1397
1444
  "",
1398
1445
  ]
1446
+ # Public `pub-` hosts are drop-in callable without the proxy token; only
1447
+ # decorate private hosts so the printed URL accurately reflects what
1448
+ # the user needs to use.
1449
+ is_public = bool(status.get("is_public"))
1450
+ proxy_token = status.get("proxy_token")
1399
1451
  for name, info in status.get("twins", {}).items():
1400
1452
  label = TWIN_CATALOG.get(name, {}).get("label", name).ljust(16)
1401
- url = with_proxy_token(info.get("base_url", ""), status.get("proxy_token"))
1453
+ base_url = info.get("base_url", "")
1454
+ url = base_url if is_public else with_proxy_token(base_url, proxy_token)
1402
1455
  lines.append(f"{label} [underline]{url}[/underline]")
1403
1456
  if status.get("expires_at"):
1404
1457
  lines.append("")
@@ -1728,6 +1781,7 @@ def main() -> None:
1728
1781
  except httpx.HTTPError as exc:
1729
1782
  print(f"Network error: {exc}", file=sys.stderr)
1730
1783
  raise SystemExit(1) from exc
1784
+ _check_for_update()
1731
1785
  raise SystemExit(exit_code)
1732
1786
 
1733
1787
 
@@ -15,6 +15,7 @@ def run_wizard(
15
15
  api_key: str | None = None,
16
16
  cwd: str,
17
17
  shape_detect: bool = True,
18
+ ttl_minutes: int | None = None,
18
19
  ) -> int:
19
20
  """Run the full quickstart wizard."""
20
21
  from arga_cli.main import ApiClient
@@ -57,9 +58,20 @@ def run_wizard(
57
58
  env_changes = rewrite_env_files(cwd, selected, shape_detect=shape_detect)
58
59
 
59
60
  # Step 5: Provision
61
+ if ttl_minutes is not None:
62
+ resolved_ttl = ttl_minutes
63
+ elif billing_plan == "free":
64
+ resolved_ttl = 10
65
+ else:
66
+ resolved_ttl = 60
60
67
  try:
61
- status = provision_twins(client, selected, ttl_minutes=10, scenario_prompt=scenario_prompt)
68
+ status = provision_twins(client, selected, ttl_minutes=resolved_ttl, scenario_prompt=scenario_prompt)
62
69
  except Exception as exc:
70
+ msg = str(exc)
71
+ if "provisions remaining" in msg.lower():
72
+ error("\n You've used all 5 quickstart provisions.")
73
+ yellow(" Run `arga login` to authenticate with your full account for unlimited access.\n")
74
+ return 1
63
75
  error(f"\n Provisioning failed: {exc}")
64
76
  if env_changes:
65
77
  yellow(" Your .env has been updated. You can re-run the wizard to retry provisioning.")
@@ -114,7 +114,7 @@ def _format_seed_summary(seed_info: dict) -> list[str]:
114
114
  counts = [
115
115
  f"{key.replace('_', ' ')}: {value}"
116
116
  for key, value in seed_info.items()
117
- if key not in {"status", "twin"} and isinstance(value, (int, str))
117
+ if key not in {"status", "twin", "guild_id", "channel_ids"} and isinstance(value, (int, str))
118
118
  ]
119
119
  return [f"Seeded — {', '.join(counts)}"] if counts else ["Seeded."]
120
120
  if status_value == "skipped":
@@ -167,6 +167,15 @@ def seed_and_report(client: Any, status: dict) -> None:
167
167
  env_vars = info.get("env_vars", {})
168
168
  for key, val in env_vars.items():
169
169
  console.print(f" [dim]{key}[/dim]: {val}")
170
+
171
+ # Print IDs from seed results (guild_id, channel_ids, etc.)
172
+ if seed_info is not None:
173
+ if seed_info.get("guild_id"):
174
+ console.print(f" [dim]GUILD_ID[/dim]: {seed_info['guild_id']}")
175
+ channel_ids = seed_info.get("channel_ids")
176
+ if channel_ids:
177
+ console.print(f" [dim]CHANNEL_IDS[/dim]: {', '.join(channel_ids)}")
178
+
170
179
  console.print()
171
180
 
172
181
  # Report backend-only twins
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arga-cli
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Command-line interface for Arga authentication, MCP installation, and browser validation
5
5
  Author: Arga Labs
6
6
  Project-URL: Homepage, https://github.com/ArgaLabs/arga-cli
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "arga-cli"
7
- version = "0.1.7"
7
+ version = "0.1.9"
8
8
  description = "Command-line interface for Arga authentication, MCP installation, and browser validation"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes