conduct-cli 0.7.31__tar.gz → 0.7.34__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.7.31 → conduct_cli-0.7.34}/PKG-INFO +1 -1
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/pyproject.toml +1 -1
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/api.py +18 -5
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/main.py +35 -2
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli.egg-info/PKG-INFO +1 -1
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/README.md +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/setup.cfg +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/setup.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/__init__.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/guard.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/guardmcp.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/__init__.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/base.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/posttooluse.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/precompact.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/pretooluse.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/session_parser.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/session_report_push.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/session_start.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/hooks/stop.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/log_util.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/mcp_server.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/memory.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/paxel.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli/tool_groups.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli.egg-info/entry_points.txt +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli.egg-info/requires.txt +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/src/conduct_cli.egg-info/top_level.txt +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_advisory_and_hook.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_bash_operator_signature.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_blast_radius.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_command_word_matcher.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_drain_daemon_health.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_guard_policy.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_guard_savings.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_hook_dispatch.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_journal_drain.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_log_util.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_proxy_coverage.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_proxy_env.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_signed_policy.py +0 -0
- {conduct_cli-0.7.31 → conduct_cli-0.7.34}/tests/test_switch.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "conduct-cli"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.34"
|
|
8
8
|
description = "CLI for Conduct AI — secure, govern, install agents, manage projects, run tests"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -6,7 +6,20 @@ import urllib.error
|
|
|
6
6
|
|
|
7
7
|
RED = "\033[31m"
|
|
8
8
|
RESET = "\033[0m"
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
_HINTS = {
|
|
11
|
+
401: "run `conduct login` to re-authenticate",
|
|
12
|
+
403: "check your permissions or run `conduct login`",
|
|
13
|
+
404: "not found — check the agent name or run ID",
|
|
14
|
+
429: "rate limited — wait a moment and try again",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _friendly(code: int, detail: str) -> str:
|
|
19
|
+
hint = _HINTS.get(code)
|
|
20
|
+
if hint and hint.lower() not in detail.lower():
|
|
21
|
+
return f"{detail} — {hint}"
|
|
22
|
+
return detail
|
|
10
23
|
|
|
11
24
|
|
|
12
25
|
def headers(workspace_id: str, token=None, content_type="application/json", api_key=None) -> dict:
|
|
@@ -31,10 +44,10 @@ def req(method: str, url: str, hdrs: dict, body=None, timeout: int = 30) -> dict
|
|
|
31
44
|
detail = json.loads(raw).get("detail", raw)
|
|
32
45
|
except Exception:
|
|
33
46
|
detail = raw
|
|
34
|
-
print(f"{RED}
|
|
47
|
+
print(f"{RED}{_friendly(e.code, detail)}{RESET}")
|
|
35
48
|
sys.exit(1)
|
|
36
49
|
except (socket.timeout, TimeoutError):
|
|
37
|
-
print(f"{RED}Request timed out
|
|
50
|
+
print(f"{RED}Request timed out — check your network connection{RESET}")
|
|
38
51
|
sys.exit(1)
|
|
39
52
|
|
|
40
53
|
|
|
@@ -50,10 +63,10 @@ def req_text(method: str, url: str, hdrs: dict, body_text: str, timeout: int = 3
|
|
|
50
63
|
detail = json.loads(raw).get("detail", raw)
|
|
51
64
|
except Exception:
|
|
52
65
|
detail = raw
|
|
53
|
-
print(f"{RED}
|
|
66
|
+
print(f"{RED}{_friendly(e.code, detail)}{RESET}")
|
|
54
67
|
sys.exit(1)
|
|
55
68
|
except (socket.timeout, TimeoutError):
|
|
56
|
-
print(f"{RED}Request timed out
|
|
69
|
+
print(f"{RED}Request timed out — check your network connection{RESET}")
|
|
57
70
|
sys.exit(1)
|
|
58
71
|
|
|
59
72
|
|
|
@@ -160,6 +160,17 @@ def _stream_run(server: str, workflow_id: str, run_id: str, workspace_id: str, t
|
|
|
160
160
|
elif kind == "brain_tool_call":
|
|
161
161
|
summary = payload.get("summary", payload.get("tool", ""))
|
|
162
162
|
print(f" · {summary}{RESET}")
|
|
163
|
+
elif kind == "guard_check":
|
|
164
|
+
n = payload.get("rules_checked", 0)
|
|
165
|
+
block_type = payload.get("block_type", "")
|
|
166
|
+
if payload.get("warnings"):
|
|
167
|
+
for w in payload["warnings"]:
|
|
168
|
+
print(f"{YELLOW} ⚠ [guard:{block_type}] {w.get('rule_id', '')}: {w.get('message', '')}{RESET}")
|
|
169
|
+
elif payload.get("audited"):
|
|
170
|
+
for a in payload["audited"]:
|
|
171
|
+
print(f"{BLUE} ● [guard:{block_type}] {a.get('rule_id', '')}: {a.get('message', '')}{RESET}")
|
|
172
|
+
else:
|
|
173
|
+
print(f"{GREEN} ✓ [guard:{block_type}] {n} rules checked — passed{RESET}")
|
|
163
174
|
elif kind == "run_completed":
|
|
164
175
|
print(f"{BOLD}{GREEN} ✓ done{RESET}")
|
|
165
176
|
elif kind == "run_failed":
|
|
@@ -453,7 +464,11 @@ def _exchange_clerk_token(api_url: str, clerk_token: str, workspace_id: str) ->
|
|
|
453
464
|
}
|
|
454
465
|
except urllib.error.HTTPError as e:
|
|
455
466
|
body_text = e.read().decode(errors="replace")
|
|
456
|
-
|
|
467
|
+
try:
|
|
468
|
+
detail = json.loads(body_text).get("detail", body_text)
|
|
469
|
+
except Exception:
|
|
470
|
+
detail = body_text[:120]
|
|
471
|
+
print(f"{RED}Login failed: {detail} — run `conduct login` to try again{RESET}")
|
|
457
472
|
sys.exit(1)
|
|
458
473
|
|
|
459
474
|
|
|
@@ -2509,6 +2524,23 @@ def cmd_run(args):
|
|
|
2509
2524
|
except Exception:
|
|
2510
2525
|
pass # validate endpoint is best-effort; backend will re-check on /trigger
|
|
2511
2526
|
|
|
2527
|
+
# Full preflight validation — credentials, auth chain, proxy, block config.
|
|
2528
|
+
# Blocks on errors so bad runs never start.
|
|
2529
|
+
try:
|
|
2530
|
+
vr = api.req("POST", f"{server}/workflows/{workflow_id}/validate", json_h, {})
|
|
2531
|
+
v_errors = (vr or {}).get("errors") or []
|
|
2532
|
+
if v_errors:
|
|
2533
|
+
print(f"{RED}✗ Cannot start run — preflight validation failed:{RESET}\n")
|
|
2534
|
+
for e in v_errors:
|
|
2535
|
+
lbl = e.get("label") or e.get("block_id") or "?"
|
|
2536
|
+
print(f" {YELLOW}⚠ [{lbl}]{RESET} {e.get('message', '')}")
|
|
2537
|
+
print()
|
|
2538
|
+
sys.exit(2)
|
|
2539
|
+
except SystemExit:
|
|
2540
|
+
raise
|
|
2541
|
+
except Exception:
|
|
2542
|
+
pass # best-effort — don't block run if validate endpoint itself errors
|
|
2543
|
+
|
|
2512
2544
|
# Fix 3: /trigger returns run_id, not id.
|
|
2513
2545
|
if getattr(args, "max_turns", None):
|
|
2514
2546
|
body["__max_turns"] = args.max_turns
|
|
@@ -2998,7 +3030,8 @@ def cmd_skill(args):
|
|
|
2998
3030
|
msg = json.loads(body).get("detail", body)
|
|
2999
3031
|
except Exception:
|
|
3000
3032
|
msg = body
|
|
3001
|
-
|
|
3033
|
+
from conduct_cli.api import _friendly
|
|
3034
|
+
print(f"{RED}{_friendly(e.code, msg)}{RESET}")
|
|
3002
3035
|
sys.exit(1)
|
|
3003
3036
|
|
|
3004
3037
|
if skill_command == "list":
|
|
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
|
|
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
|