conduct-cli 0.14.4__tar.gz → 0.14.6__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.14.4 → conduct_cli-0.14.6}/PKG-INFO +1 -1
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/pyproject.toml +1 -1
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/guard.py +7 -3
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/posttooluse.py +26 -2
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/session_start.py +7 -4
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli.egg-info/PKG-INFO +1 -1
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli.egg-info/SOURCES.txt +1 -0
- conduct_cli-0.14.6/tests/test_session_start.py +40 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/LICENSE +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/README.md +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/setup.cfg +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/setup.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/__init__.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/api.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/guard_policy.md +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/__init__.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/base.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/precompact.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/pretooluse.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/session_parser.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/session_report_push.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/hooks/stop.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/log_util.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/main.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/memory.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/paxel.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli/tool_groups.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli.egg-info/entry_points.txt +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli.egg-info/requires.txt +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/src/conduct_cli.egg-info/top_level.txt +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_advisory_and_hook.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_api_client.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_bash_operator_signature.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_bash_scan_target.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_blast_radius.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_cli_matrix.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_cmd_run.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_command_word_matcher.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_drain_daemon_health.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_guard_approvals.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_guard_policy.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_guard_savings.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_guard_status.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_hook_dispatch.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_hook_journal.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_hook_receipt.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_install_all.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_instruction_files.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_journal_drain.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_log_util.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_pretooluse_approval_e2e.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_pretooluse_check_policy.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_proxy_coverage.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_proxy_env.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_report_tool_coverage.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_signed_policy.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_switch.py +0 -0
- {conduct_cli-0.14.4 → conduct_cli-0.14.6}/tests/test_token_lifecycle.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conduct-cli
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.6
|
|
4
4
|
Summary: Runtime governance for AI agents. CLI for Conduct AI — secure, govern, install agents, manage projects, run tests
|
|
5
5
|
Author-email: Conduct AI <hello@conductai.ai>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "conduct-cli"
|
|
7
|
-
version = "0.14.
|
|
7
|
+
version = "0.14.6"
|
|
8
8
|
description = "Runtime governance for AI agents. CLI for Conduct AI — secure, govern, install agents, manage projects, run tests"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -1232,17 +1232,21 @@ def _detect_ai_tools() -> list[dict]:
|
|
|
1232
1232
|
if codex_dir.exists():
|
|
1233
1233
|
config = codex_dir / "config.toml"
|
|
1234
1234
|
is_desktop = _check_toml_str(config, "[desktop]")
|
|
1235
|
+
# Codex Desktop uses the same provider configuration as Codex CLI.
|
|
1236
|
+
# Do not report it as partial when the Conduct provider is selected.
|
|
1237
|
+
codex_proxy = _check_toml_str(config, 'model_provider = "conduct"')
|
|
1238
|
+
codex_mcp = _check_toml_str(config, "mcp_servers.conduct") or _check_toml_str(config, "conduct-mcp")
|
|
1235
1239
|
if is_desktop:
|
|
1236
1240
|
tools.append({
|
|
1237
1241
|
"name": "codex-desktop",
|
|
1238
|
-
"mcp_registered":
|
|
1242
|
+
"mcp_registered": codex_mcp,
|
|
1239
1243
|
"hook_registered": _check_toml_str(config, "conductguard") or _check_toml_str(config, "conduct"),
|
|
1240
|
-
"proxy_routed":
|
|
1244
|
+
"proxy_routed": codex_proxy,
|
|
1241
1245
|
})
|
|
1242
1246
|
else:
|
|
1243
1247
|
tools.append({
|
|
1244
1248
|
"name": "codex",
|
|
1245
|
-
"mcp_registered":
|
|
1249
|
+
"mcp_registered": codex_mcp,
|
|
1246
1250
|
"hook_registered": _check_toml_str(config, "conductguard") or _check_toml_str(config, "conduct"),
|
|
1247
1251
|
"proxy_routed": _is_openai_proxied(),
|
|
1248
1252
|
})
|
|
@@ -236,8 +236,16 @@ def post_codex_main() -> None:
|
|
|
236
236
|
time.sleep(2)
|
|
237
237
|
transcript_path = args.get("transcript_path", "")
|
|
238
238
|
tokens_in, tokens_out = _scan_codex_tokens(transcript_path)
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
_post_usage(
|
|
240
|
+
args.get("session_id"),
|
|
241
|
+
args.get("tool_name") or "unknown",
|
|
242
|
+
tokens_in,
|
|
243
|
+
tokens_out,
|
|
244
|
+
None,
|
|
245
|
+
args.get("blast_radius"),
|
|
246
|
+
args.get("execution_status"),
|
|
247
|
+
args.get("result_summary"),
|
|
248
|
+
)
|
|
241
249
|
sys.exit(0)
|
|
242
250
|
|
|
243
251
|
|
|
@@ -280,6 +288,8 @@ def main() -> None:
|
|
|
280
288
|
"tool_name": tool_name,
|
|
281
289
|
"transcript_path": transcript_path,
|
|
282
290
|
"blast_radius": blast_radius,
|
|
291
|
+
"execution_status": execution_status,
|
|
292
|
+
"result_summary": result_summary,
|
|
283
293
|
}))
|
|
284
294
|
subprocess.Popen(
|
|
285
295
|
[sys.executable, str(_this_file), "post-codex", str(pending)],
|
|
@@ -301,6 +311,20 @@ def main() -> None:
|
|
|
301
311
|
if action == "warn" and session_id and rule_id:
|
|
302
312
|
_record_session_warn(session_id, rule_id)
|
|
303
313
|
post_event(tool_name, {}, decision, rule_id, message, session_id, drain_via=_this_file, blast_radius=blast_radius)
|
|
314
|
+
elif session_id:
|
|
315
|
+
# Some Codex clients omit transcript_path. Still send the PostToolUse
|
|
316
|
+
# update so the pre-tool audit row is correlated and execution status
|
|
317
|
+
# is visible; token backfill can remain unavailable for this call.
|
|
318
|
+
_post_usage(
|
|
319
|
+
session_id,
|
|
320
|
+
tool_name or "unknown",
|
|
321
|
+
0,
|
|
322
|
+
0,
|
|
323
|
+
None,
|
|
324
|
+
blast_radius,
|
|
325
|
+
execution_status,
|
|
326
|
+
result_summary,
|
|
327
|
+
)
|
|
304
328
|
|
|
305
329
|
sys.exit(0)
|
|
306
330
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
|
+
import re
|
|
5
6
|
import sys
|
|
6
7
|
from datetime import datetime, timezone
|
|
7
8
|
from pathlib import Path
|
|
@@ -18,6 +19,11 @@ from conduct_cli.hooks.base import (
|
|
|
18
19
|
|
|
19
20
|
MAX_AGE_HOURS = 2
|
|
20
21
|
|
|
22
|
+
_PROXY_TOKEN_RE = re.compile(
|
|
23
|
+
r"^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*['\"]?(?:cond_agt_|guard-mt-)[A-Za-z0-9_-]+['\"]?\s*$",
|
|
24
|
+
re.MULTILINE,
|
|
25
|
+
)
|
|
26
|
+
|
|
21
27
|
|
|
22
28
|
def _check_proxy_token() -> None:
|
|
23
29
|
"""Warn + alert server if the proxy token is missing, malformed, or expired.
|
|
@@ -31,10 +37,7 @@ def _check_proxy_token() -> None:
|
|
|
31
37
|
return
|
|
32
38
|
env_text = CONDUCT_ENV_PATH.read_text()
|
|
33
39
|
has_base_url = "ANTHROPIC_BASE_URL=" in env_text
|
|
34
|
-
has_valid_token = (
|
|
35
|
-
'ANTHROPIC_API_KEY="guard-mt-' in env_text
|
|
36
|
-
or "ANTHROPIC_API_KEY='guard-mt-" in env_text
|
|
37
|
-
)
|
|
40
|
+
has_valid_token = bool(_PROXY_TOKEN_RE.search(env_text))
|
|
38
41
|
if not has_base_url:
|
|
39
42
|
return
|
|
40
43
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conduct-cli
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.6
|
|
4
4
|
Summary: Runtime governance for AI agents. CLI for Conduct AI — secure, govern, install agents, manage projects, run tests
|
|
5
5
|
Author-email: Conduct AI <hello@conductai.ai>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from datetime import datetime, timedelta, timezone
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from conduct_cli.hooks import session_start
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize("token", ["cond_agt_current-token", "guard-mt-legacy-token"])
|
|
9
|
+
def test_proxy_token_check_accepts_current_and_legacy_tokens(tmp_path, monkeypatch, capsys, token):
|
|
10
|
+
env_path = tmp_path / "env"
|
|
11
|
+
env_path.write_text(
|
|
12
|
+
"export ANTHROPIC_BASE_URL=\"https://api.conductai.ai/gateway/v1/anthropic\"\n"
|
|
13
|
+
f"export ANTHROPIC_API_KEY=\"{token}\"\n"
|
|
14
|
+
)
|
|
15
|
+
monkeypatch.setattr(session_start, "CONDUCT_ENV_PATH", env_path)
|
|
16
|
+
monkeypatch.setattr(
|
|
17
|
+
session_start,
|
|
18
|
+
"load_config",
|
|
19
|
+
lambda: {"token_expires_at": (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat()},
|
|
20
|
+
)
|
|
21
|
+
monkeypatch.setattr(session_start, "post_event", lambda *args, **kwargs: pytest.fail("unexpected warning"))
|
|
22
|
+
|
|
23
|
+
session_start._check_proxy_token()
|
|
24
|
+
|
|
25
|
+
assert "missing or malformed" not in capsys.readouterr().out
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_proxy_token_check_warns_for_malformed_token(tmp_path, monkeypatch, capsys):
|
|
29
|
+
env_path = tmp_path / "env"
|
|
30
|
+
env_path.write_text(
|
|
31
|
+
"export ANTHROPIC_BASE_URL=\"https://api.conductai.ai/gateway/v1/anthropic\"\n"
|
|
32
|
+
"export ANTHROPIC_API_KEY=\"not-a-conduct-token\"\n"
|
|
33
|
+
)
|
|
34
|
+
monkeypatch.setattr(session_start, "CONDUCT_ENV_PATH", env_path)
|
|
35
|
+
monkeypatch.setattr(session_start, "load_config", lambda: {})
|
|
36
|
+
monkeypatch.setattr(session_start, "post_event", lambda *args, **kwargs: None)
|
|
37
|
+
|
|
38
|
+
session_start._check_proxy_token()
|
|
39
|
+
|
|
40
|
+
assert "proxy token missing or malformed" in capsys.readouterr().out
|
|
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
|
|
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
|