conduct-cli 0.5.7__tar.gz → 0.5.8__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.
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/PKG-INFO +1 -1
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/pyproject.toml +1 -1
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/guard.py +6 -1
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/hook_template.py +36 -1
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/PKG-INFO +1 -1
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/README.md +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/setup.cfg +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/setup.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/__init__.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/api.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/guardmcp.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/hook_precompact_template.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/hook_session_start_template.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/hook_stop_template.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/main.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/mcp_server.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/memory.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/paxel.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/entry_points.txt +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/requires.txt +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/top_level.txt +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_bash_operator_signature.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_guard_policy.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_guard_savings.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_hook_syntax.py +0 -0
- {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_switch.py +0 -0
|
@@ -572,8 +572,13 @@ def cmd_guard_install(args):
|
|
|
572
572
|
api_key=api_key,
|
|
573
573
|
)
|
|
574
574
|
_save_policy(policy)
|
|
575
|
+
# Mirror fail_mode into guard config so the hook can enforce it
|
|
576
|
+
# even when POLICY_PATH is missing or unreadable.
|
|
577
|
+
cfg = _load_guard_config()
|
|
578
|
+
cfg["fail_mode"] = policy.get("fail_mode", "fail_open")
|
|
579
|
+
_save_guard_config(cfg)
|
|
575
580
|
rule_count = len(policy.get("rules", []))
|
|
576
|
-
print(f" {GREEN}Guard policies:{RESET} {rule_count} rule(s) active")
|
|
581
|
+
print(f" {GREEN}Guard policies:{RESET} {rule_count} rule(s) active · fail mode: {cfg['fail_mode']}")
|
|
577
582
|
except SystemExit:
|
|
578
583
|
rule_count = 0
|
|
579
584
|
|
|
@@ -86,7 +86,17 @@ def _maybe_sync_policy():
|
|
|
86
86
|
POLICY_PATH.write_text(json.dumps(remote, indent=2))
|
|
87
87
|
VERSION_CACHE_PATH.write_text(json.dumps({"ts": time.time(), "version": remote_version}))
|
|
88
88
|
except Exception:
|
|
89
|
-
pass #
|
|
89
|
+
pass # sync failure does not directly block; policy file may still be present
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _get_fail_mode():
|
|
93
|
+
"""Read fail_mode from ~/.conduct/config.json. Default: fail_open."""
|
|
94
|
+
try:
|
|
95
|
+
cfg = json.loads(CONFIG_PATH.read_text()) if CONFIG_PATH.exists() else {}
|
|
96
|
+
except Exception:
|
|
97
|
+
return "fail_open"
|
|
98
|
+
mode = cfg.get("fail_mode", "fail_open")
|
|
99
|
+
return mode if mode in ("fail_open", "fail_closed") else "fail_open"
|
|
90
100
|
|
|
91
101
|
|
|
92
102
|
def _load_budget_cache():
|
|
@@ -685,10 +695,35 @@ def main():
|
|
|
685
695
|
# Policy version check (cached 60s) — auto-syncs if server version differs
|
|
686
696
|
_maybe_sync_policy()
|
|
687
697
|
|
|
698
|
+
# Fail-closed gate: if the local policy file is missing and the workspace
|
|
699
|
+
# is configured for fail_closed, block every tool call with a clear
|
|
700
|
+
# message. fail_open (default) silently allows.
|
|
701
|
+
if _get_fail_mode() == "fail_closed" and not POLICY_PATH.exists():
|
|
702
|
+
tool_name = (data.get("tool_name") or "").lower()
|
|
703
|
+
tool_input = data.get("tool_input") or {}
|
|
704
|
+
session_id = data.get("session_id")
|
|
705
|
+
msg = "[ConductGuard] Policy cache unavailable — fail-closed mode is on. Run `conduct guard sync` or contact your admin."
|
|
706
|
+
print(msg)
|
|
707
|
+
print(msg, file=sys.stderr)
|
|
708
|
+
_post_event(tool_name, tool_input, "blocked", "guard-unavailable", msg, session_id=session_id)
|
|
709
|
+
sys.exit(2)
|
|
710
|
+
|
|
688
711
|
# Hard budget cap (cached 5 min)
|
|
689
712
|
hard_blocked, reason = _load_budget_cache()
|
|
690
713
|
if hard_blocked is None:
|
|
691
714
|
hard_blocked, reason = _fetch_budget_status()
|
|
715
|
+
# Fail-closed: if the budget check returned default (None reason) AND
|
|
716
|
+
# we just hit the API for a fresh value, the API was unreachable.
|
|
717
|
+
# Treat as unavailable and block.
|
|
718
|
+
if not hard_blocked and reason is None and _get_fail_mode() == "fail_closed":
|
|
719
|
+
tool_name = (data.get("tool_name") or "").lower()
|
|
720
|
+
tool_input = data.get("tool_input") or {}
|
|
721
|
+
session_id = data.get("session_id")
|
|
722
|
+
msg = "[ConductGuard] Spend budget check unavailable — fail-closed mode is on."
|
|
723
|
+
print(msg)
|
|
724
|
+
print(msg, file=sys.stderr)
|
|
725
|
+
_post_event(tool_name, tool_input, "blocked", "guard-unavailable", msg, session_id=session_id)
|
|
726
|
+
sys.exit(2)
|
|
692
727
|
if hard_blocked:
|
|
693
728
|
msg = f"[ConductGuard] {reason or 'Budget hard cap reached. Contact your manager.'}"
|
|
694
729
|
print(msg)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|