conduct-cli 0.4.15__tar.gz → 0.4.17__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.15
3
+ Version: 0.4.17
4
4
  Summary: CLI for Conduct AI — install agents, manage projects, run tests
5
5
  Author-email: Conduct AI <hello@conductai.ai>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "conduct-cli"
7
- version = "0.4.15"
7
+ version = "0.4.17"
8
8
  description = "CLI for Conduct AI — install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -55,11 +55,14 @@ def _fetch_budget_status():
55
55
  cfg = json.loads(CONFIG_PATH.read_text()) if CONFIG_PATH.exists() else {}
56
56
  except Exception:
57
57
  return False, None
58
- workspace_id = cfg.get("workspace_id")
59
- api_url = cfg.get("api_url", "https://api.conductai.ai").rstrip("/")
58
+ workspace_id = cfg.get("workspace_id")
59
+ clerk_user_id = cfg.get("clerk_user_id") or ""
60
+ api_url = cfg.get("api_url", "https://api.conductai.ai").rstrip("/")
60
61
  if not workspace_id:
61
62
  return False, None
62
63
  url = f"{api_url}/guard/spend/budget-check?workspace_id={workspace_id}"
64
+ if clerk_user_id:
65
+ url += f"&clerk_user_id={clerk_user_id}"
63
66
  try:
64
67
  with urllib.request.urlopen(urllib.request.Request(url), timeout=5) as resp:
65
68
  data = json.loads(resp.read())
@@ -399,6 +402,24 @@ if __name__ == "__main__":
399
402
  main()
400
403
  '''
401
404
 
405
+ # ── Hook write helper ─────────────────────────────────────────────────────────
406
+
407
+ def _write_hook(path: Path) -> None:
408
+ """Write _HOOK_SCRIPT to path, then py_compile-validate it.
409
+ Raises RuntimeError if the written file fails to compile — prevents
410
+ silently deploying a syntactically broken hook."""
411
+ import py_compile, tempfile, os
412
+ path.write_text(_HOOK_SCRIPT)
413
+ path.chmod(0o755)
414
+ try:
415
+ py_compile.compile(str(path), doraise=True)
416
+ except py_compile.PyCompileError as exc:
417
+ path.unlink(missing_ok=True)
418
+ raise RuntimeError(
419
+ f"hook.py failed syntax check after write — hook NOT installed.\n{exc}"
420
+ ) from exc
421
+
422
+
402
423
  # ── Guard config helpers ──────────────────────────────────────────────────────
403
424
 
404
425
  def _load_guard_config() -> dict:
@@ -666,16 +687,18 @@ def cmd_guard_install(args):
666
687
  print(f" {GRAY}Guard not installed for this workspace — skipping{RESET}")
667
688
  return
668
689
 
669
- member_token = result.get("member_token") or ""
670
- user_email = result.get("user_email") or ""
690
+ member_token = result.get("member_token") or ""
691
+ user_email = result.get("user_email") or ""
692
+ clerk_user_id = result.get("clerk_user_id") or ""
671
693
 
672
694
  # Persist guard config — include api_key so CLI commands can authenticate
673
695
  _save_guard_config({
674
- "workspace_id": workspace_id,
675
- "member_token": member_token,
676
- "user_email": user_email,
677
- "api_key": api_key,
678
- "api_url": server,
696
+ "workspace_id": workspace_id,
697
+ "member_token": member_token,
698
+ "user_email": user_email,
699
+ "clerk_user_id": clerk_user_id,
700
+ "api_key": api_key,
701
+ "api_url": server,
679
702
  })
680
703
 
681
704
  # Download policies
@@ -693,8 +716,7 @@ def cmd_guard_install(args):
693
716
 
694
717
  # Write hook script
695
718
  hook_path = GUARD_DIR / "hook.py"
696
- hook_path.write_text(_HOOK_SCRIPT)
697
- hook_path.chmod(0o755)
719
+ _write_hook(hook_path)
698
720
 
699
721
  # Install PreToolUse hooks — Claude Code + Codex (real interception)
700
722
  _install_claude_hook(hook_path)
@@ -743,8 +765,7 @@ def cmd_guard_join(args):
743
765
 
744
766
  # Write hook script
745
767
  hook_path = GUARD_DIR / "hook.py"
746
- hook_path.write_text(_HOOK_SCRIPT)
747
- hook_path.chmod(0o755)
768
+ _write_hook(hook_path)
748
769
  print(f" {GREEN}Hook script written:{RESET} {hook_path}")
749
770
 
750
771
  # Install PreToolUse hook in ~/.claude/settings.json
@@ -776,8 +797,7 @@ def cmd_guard_sync(args):
776
797
 
777
798
  # Refresh hook script + re-register in all tools
778
799
  hook_path = GUARD_DIR / "hook.py"
779
- hook_path.write_text(_HOOK_SCRIPT)
780
- hook_path.chmod(0o755)
800
+ _write_hook(hook_path)
781
801
  _install_claude_hook(hook_path)
782
802
  _install_codex_hook(hook_path)
783
803
  cfg2 = _load_guard_config()
@@ -794,19 +814,21 @@ def cmd_guard_status(args):
794
814
  api_key = cfg.get("api_key", "")
795
815
  base_url = _api_url(cfg)
796
816
 
797
- # Auto-refresh user_email into config if it was installed before this was wired up
798
- if not user_email and api_key:
817
+ # Auto-refresh user_email + clerk_user_id into config if missing
818
+ if (not user_email or not cfg.get("clerk_user_id")) and api_key:
799
819
  try:
800
820
  installed = _req("GET", f"{base_url}/guard/config/installed", api_key=api_key)
801
821
  fetched_email = installed.get("user_email") or ""
822
+ fetched_clerk = installed.get("clerk_user_id") or ""
802
823
  if fetched_email:
803
824
  cfg["user_email"] = fetched_email
804
- _save_guard_config(cfg)
805
- # Rewrite hook script so future events carry the email
806
- hook_path = GUARD_DIR / "hook.py"
807
- hook_path.write_text(_HOOK_SCRIPT)
808
- hook_path.chmod(0o755)
809
825
  user_email = fetched_email
826
+ if fetched_clerk:
827
+ cfg["clerk_user_id"] = fetched_clerk
828
+ _save_guard_config(cfg)
829
+ # Rewrite hook script so future events carry the email
830
+ hook_path = GUARD_DIR / "hook.py"
831
+ _write_hook(hook_path)
810
832
  except Exception:
811
833
  pass
812
834
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.15
3
+ Version: 0.4.17
4
4
  Summary: CLI for Conduct AI — install agents, manage projects, run tests
5
5
  Author-email: Conduct AI <hello@conductai.ai>
6
6
  License: MIT
File without changes
File without changes
File without changes