humanbound-cli 0.1.1__tar.gz → 0.3.0__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 (53) hide show
  1. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/PKG-INFO +83 -24
  2. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/README.md +80 -23
  3. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/__init__.py +1 -1
  4. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/client.py +121 -5
  5. humanbound_cli-0.3.0/humanbound_cli/commands/__init__.py +27 -0
  6. humanbound_cli-0.3.0/humanbound_cli/commands/api_keys.py +225 -0
  7. humanbound_cli-0.3.0/humanbound_cli/commands/campaigns.py +174 -0
  8. humanbound_cli-0.3.0/humanbound_cli/commands/coverage.py +160 -0
  9. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/experiments.py +138 -11
  10. humanbound_cli-0.3.0/humanbound_cli/commands/findings.py +177 -0
  11. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/init.py +159 -5
  12. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/logs.py +17 -9
  13. humanbound_cli-0.3.0/humanbound_cli/commands/members.py +179 -0
  14. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/posture.py +73 -1
  15. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/projects.py +96 -0
  16. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/test.py +45 -123
  17. humanbound_cli-0.3.0/humanbound_cli/commands/upload_logs.py +106 -0
  18. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/config.py +7 -1
  19. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/exceptions.py +18 -0
  20. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/main.py +33 -4
  21. humanbound_cli-0.3.0/humanbound_cli/report.py +719 -0
  22. humanbound_cli-0.3.0/humanbound_cli/serve/__init__.py +14 -0
  23. humanbound_cli-0.3.0/humanbound_cli/serve/config_builder.py +48 -0
  24. humanbound_cli-0.3.0/humanbound_cli/serve/local_server.py +206 -0
  25. humanbound_cli-0.3.0/humanbound_cli/serve/runtime_detector.py +421 -0
  26. humanbound_cli-0.3.0/humanbound_cli/serve/tunnel_client.py +236 -0
  27. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli.egg-info/PKG-INFO +83 -24
  28. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli.egg-info/SOURCES.txt +13 -0
  29. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli.egg-info/requires.txt +3 -0
  30. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli.egg-info/top_level.txt +2 -0
  31. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/pyproject.toml +4 -1
  32. humanbound_cli-0.3.0/relay/relay.py +347 -0
  33. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/tests/cli_integration_test.py +427 -170
  34. humanbound_cli-0.1.1/humanbound_cli/commands/__init__.py +0 -17
  35. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/LICENSE +0 -0
  36. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/auth.py +0 -0
  37. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/docs.py +0 -0
  38. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/guardrails.py +0 -0
  39. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/orgs.py +0 -0
  40. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/providers.py +0 -0
  41. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/commands/scan.py +0 -0
  42. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/extractors/__init__.py +0 -0
  43. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/extractors/openapi.py +0 -0
  44. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/extractors/repo.py +0 -0
  45. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/pytest_plugin/__init__.py +0 -0
  46. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/pytest_plugin/fixtures.py +0 -0
  47. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli/pytest_plugin/report.py +0 -0
  48. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli.egg-info/dependency_links.txt +0 -0
  49. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/humanbound_cli.egg-info/entry_points.txt +0 -0
  50. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/setup.cfg +0 -0
  51. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/tests/__init__.py +0 -0
  52. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/tests/conftest.py +0 -0
  53. {humanbound_cli-0.1.1 → humanbound_cli-0.3.0}/tests/test_cli_commands.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: humanbound-cli
3
- Version: 0.1.1
3
+ Version: 0.3.0
4
4
  Summary: Humanbound CLI - command line interface for AI agent security testing.
5
5
  Author-email: Kostas Siabanis <hello@humanbound.ai>, Demetris Gerogiannis <hello@humanbound.ai>
6
6
  License: Apache-2.0
@@ -19,18 +19,21 @@ Requires-Dist: click>=8.1.0
19
19
  Requires-Dist: rich>=13.0.0
20
20
  Requires-Dist: requests>=2.32.0
21
21
  Requires-Dist: pyyaml>=6.0.0
22
+ Provides-Extra: serve
23
+ Requires-Dist: websockets>=12.0; extra == "serve"
22
24
  Provides-Extra: pytest
23
25
  Requires-Dist: pytest>=7.0.0; extra == "pytest"
24
26
  Provides-Extra: dev
25
27
  Requires-Dist: pytest>=7.0.0; extra == "dev"
26
28
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
27
29
 
28
- # Humanbound CLI (Beta)
30
+ # Humanbound CLI
29
31
 
30
32
  > CLI-first security testing for AI agents and chatbots. Adversarial attacks, behavioral QA, posture scoring, and guardrails export — from your terminal to your CI/CD pipeline.
31
33
 
32
34
  [![PyPI](https://img.shields.io/pypi/v/humanbound-cli)](https://pypi.org/project/humanbound-cli/)
33
35
  [![License](https://img.shields.io/badge/license-proprietary-blue)]()
36
+ [![Version](https://img.shields.io/badge/version-0.2.0-green)]()
34
37
 
35
38
  ```
36
39
  pip install humanbound-cli
@@ -113,7 +116,7 @@ hb test
113
116
  hb test -e ./bot-config.json
114
117
 
115
118
  # Choose test category and depth
116
- hb test -t owasp_multi_turn -l system
119
+ hb test -t humanbound/adversarial/owasp_multi_turn -l system
117
120
  ```
118
121
 
119
122
  ### 4. Review results
@@ -285,6 +288,8 @@ Providers are LLM configurations used for running security tests.
285
288
  | `projects use <id>` | Select project |
286
289
  | `projects current` | Show current project |
287
290
  | `projects show [id]` | Show project details |
291
+ | `projects update [id]` | Update project name/description |
292
+ | `projects delete [id]` | Delete project (with confirmation) |
288
293
 
289
294
  <details>
290
295
  <summary><code>init</code> — scan bot & create project</summary>
@@ -324,27 +329,15 @@ Testing Level:
324
329
  --testing-level, -l Depth of testing (default: unit)
325
330
  unit | system | acceptance
326
331
 
327
- Chat Endpoint (required):
328
- --chat-endpoint Chat completion URL of the bot to test
329
- --chat-header Header for chat endpoint (repeatable)
330
- --chat-payload JSON payload template for chat
331
-
332
- Init Endpoint (optional):
333
- --init-endpoint Thread initialization URL
334
- --init-header Header for init endpoint (repeatable)
335
- --init-payload JSON payload for init
336
-
337
- Auth Endpoint (optional):
338
- --auth-endpoint Auth/token endpoint URL
339
- --auth-header Header for auth endpoint (repeatable)
340
- --auth-payload JSON payload for auth
332
+ Endpoint Override (optional — only needed if no default integration):
333
+ -e, --endpoint Bot integration config JSON string or file path.
334
+ Same shape as 'hb init --endpoint'. Overrides default.
341
335
 
342
336
  Other:
343
337
  --provider-id Provider to use (default: first available)
344
338
  --name, -n Experiment name (auto-generated if omitted)
345
339
  --lang Language (default: english). Accepts codes: en, de, es...
346
340
  --adaptive Enable adaptive mode (evolutionary attack strategy)
347
- --streaming Enable streaming mode (requires wss:// endpoint)
348
341
  --no-auto-start Create without starting (manual mode)
349
342
  --wait, -w Wait for completion
350
343
  --fail-on SEVERITY Exit non-zero if findings >= severity
@@ -363,7 +356,8 @@ Other:
363
356
  | `experiments status <id> --watch` | Watch until completion |
364
357
  | `experiments wait <id>` | Wait with progressive backoff (30s -> 60s -> 120s -> 300s) |
365
358
  | `experiments logs <id>` | List experiment logs |
366
- | `experiments report <id>` | Download HTML report |
359
+ | `experiments terminate <id>` | Stop a running experiment |
360
+ | `experiments delete <id>` | Delete experiment (with confirmation) |
367
361
 
368
362
  `status` is also available as a top-level alias — without an ID it shows the most recent experiment:
369
363
 
@@ -371,14 +365,79 @@ Other:
371
365
  hb status [experiment_id] [--watch]
372
366
  ```
373
367
 
368
+ ### Findings
369
+
370
+ Track long-term security vulnerabilities across experiments.
371
+
372
+ | Command | Description |
373
+ |---------|-------------|
374
+ | `findings` | List findings (filterable by --status, --severity) |
375
+ | `findings update <id>` | Update finding status or severity |
376
+
377
+ Finding states: **open** → **stale** (30+ days unseen) → **fixed** (resolved). Findings can also **regress** (was fixed, reappeared).
378
+
379
+ ### Coverage
380
+
381
+ | Command | Description |
382
+ |---------|-------------|
383
+ | `coverage` | Test coverage summary |
384
+ | `coverage --gaps` | Include untested categories |
385
+
386
+ ### Campaigns
387
+
388
+ Continuous security assurance with automated campaign management (ASCAM).
389
+
390
+ | Command | Description |
391
+ |---------|-------------|
392
+ | `campaigns` | Show current campaign plan |
393
+ | `campaigns break` | Stop a running campaign |
394
+
395
+ ASCAM phases: Reconnaissance → Hardening → Red Teaming → Analysis → Monitoring
396
+
397
+ ### Upload Conversation Logs
398
+
399
+ Evaluate real production conversations against security judges.
400
+
401
+ | Command | Description |
402
+ |---------|-------------|
403
+ | `upload-logs <file>` | Upload JSON conversation logs |
404
+
405
+ Options: `--tag`, `--lang`
406
+
407
+ ### API Keys
408
+
409
+ | Command | Description |
410
+ |---------|-------------|
411
+ | `api-keys list` | List API keys |
412
+ | `api-keys create` | Create new key (--name required, --scopes: admin/write/read) |
413
+ | `api-keys update <id>` | Update key name, scopes, or active state |
414
+ | `api-keys revoke <id>` | Revoke (delete) an API key |
415
+
416
+ ### Members
417
+
418
+ | Command | Description |
419
+ |---------|-------------|
420
+ | `members list` | List organisation members |
421
+ | `members invite <email>` | Invite member (--role: admin/developer) |
422
+ | `members remove <id>` | Remove member |
423
+
374
424
  ### Results & Export
375
425
 
376
426
  ```bash
377
- # View experiment results (table, json, or csv)
378
- hb logs [experiment_id] [--format table] [--verdict pass|fail] [--page N] [--size N]
427
+ # View experiment results
428
+ hb logs [experiment_id] [--format table|json|html] [--verdict pass|fail] [--page N] [--size N]
429
+
430
+ # Export branded HTML report
431
+ hb logs <experiment_id> --format=html [-o report.html]
432
+
433
+ # Security posture
434
+ hb posture [--json] [--trends]
435
+
436
+ # Test coverage
437
+ hb coverage [--gaps] [--json]
379
438
 
380
- # Security posture score
381
- hb posture [--json]
439
+ # Findings
440
+ hb findings [--status open] [--severity high] [--json]
382
441
 
383
442
  # Export guardrails configuration
384
443
  hb guardrails [--vendor humanbound|openai] [--format json|yaml] [-o FILE]
@@ -406,7 +465,7 @@ hb switch abc123
406
465
  hb init -n "Support Bot" -e ./bot-config.json
407
466
 
408
467
  # Run adversarial test (uses project's default integration)
409
- hb test -t owasp_multi_turn -l unit
468
+ hb test -t humanbound/adversarial/owasp_multi_turn -l unit
410
469
 
411
470
  # Watch and review
412
471
  hb status --watch
@@ -1,9 +1,10 @@
1
- # Humanbound CLI (Beta)
1
+ # Humanbound CLI
2
2
 
3
3
  > CLI-first security testing for AI agents and chatbots. Adversarial attacks, behavioral QA, posture scoring, and guardrails export — from your terminal to your CI/CD pipeline.
4
4
 
5
5
  [![PyPI](https://img.shields.io/pypi/v/humanbound-cli)](https://pypi.org/project/humanbound-cli/)
6
6
  [![License](https://img.shields.io/badge/license-proprietary-blue)]()
7
+ [![Version](https://img.shields.io/badge/version-0.2.0-green)]()
7
8
 
8
9
  ```
9
10
  pip install humanbound-cli
@@ -86,7 +87,7 @@ hb test
86
87
  hb test -e ./bot-config.json
87
88
 
88
89
  # Choose test category and depth
89
- hb test -t owasp_multi_turn -l system
90
+ hb test -t humanbound/adversarial/owasp_multi_turn -l system
90
91
  ```
91
92
 
92
93
  ### 4. Review results
@@ -258,6 +259,8 @@ Providers are LLM configurations used for running security tests.
258
259
  | `projects use <id>` | Select project |
259
260
  | `projects current` | Show current project |
260
261
  | `projects show [id]` | Show project details |
262
+ | `projects update [id]` | Update project name/description |
263
+ | `projects delete [id]` | Delete project (with confirmation) |
261
264
 
262
265
  <details>
263
266
  <summary><code>init</code> — scan bot & create project</summary>
@@ -297,27 +300,15 @@ Testing Level:
297
300
  --testing-level, -l Depth of testing (default: unit)
298
301
  unit | system | acceptance
299
302
 
300
- Chat Endpoint (required):
301
- --chat-endpoint Chat completion URL of the bot to test
302
- --chat-header Header for chat endpoint (repeatable)
303
- --chat-payload JSON payload template for chat
304
-
305
- Init Endpoint (optional):
306
- --init-endpoint Thread initialization URL
307
- --init-header Header for init endpoint (repeatable)
308
- --init-payload JSON payload for init
309
-
310
- Auth Endpoint (optional):
311
- --auth-endpoint Auth/token endpoint URL
312
- --auth-header Header for auth endpoint (repeatable)
313
- --auth-payload JSON payload for auth
303
+ Endpoint Override (optional — only needed if no default integration):
304
+ -e, --endpoint Bot integration config JSON string or file path.
305
+ Same shape as 'hb init --endpoint'. Overrides default.
314
306
 
315
307
  Other:
316
308
  --provider-id Provider to use (default: first available)
317
309
  --name, -n Experiment name (auto-generated if omitted)
318
310
  --lang Language (default: english). Accepts codes: en, de, es...
319
311
  --adaptive Enable adaptive mode (evolutionary attack strategy)
320
- --streaming Enable streaming mode (requires wss:// endpoint)
321
312
  --no-auto-start Create without starting (manual mode)
322
313
  --wait, -w Wait for completion
323
314
  --fail-on SEVERITY Exit non-zero if findings >= severity
@@ -336,7 +327,8 @@ Other:
336
327
  | `experiments status <id> --watch` | Watch until completion |
337
328
  | `experiments wait <id>` | Wait with progressive backoff (30s -> 60s -> 120s -> 300s) |
338
329
  | `experiments logs <id>` | List experiment logs |
339
- | `experiments report <id>` | Download HTML report |
330
+ | `experiments terminate <id>` | Stop a running experiment |
331
+ | `experiments delete <id>` | Delete experiment (with confirmation) |
340
332
 
341
333
  `status` is also available as a top-level alias — without an ID it shows the most recent experiment:
342
334
 
@@ -344,14 +336,79 @@ Other:
344
336
  hb status [experiment_id] [--watch]
345
337
  ```
346
338
 
339
+ ### Findings
340
+
341
+ Track long-term security vulnerabilities across experiments.
342
+
343
+ | Command | Description |
344
+ |---------|-------------|
345
+ | `findings` | List findings (filterable by --status, --severity) |
346
+ | `findings update <id>` | Update finding status or severity |
347
+
348
+ Finding states: **open** → **stale** (30+ days unseen) → **fixed** (resolved). Findings can also **regress** (was fixed, reappeared).
349
+
350
+ ### Coverage
351
+
352
+ | Command | Description |
353
+ |---------|-------------|
354
+ | `coverage` | Test coverage summary |
355
+ | `coverage --gaps` | Include untested categories |
356
+
357
+ ### Campaigns
358
+
359
+ Continuous security assurance with automated campaign management (ASCAM).
360
+
361
+ | Command | Description |
362
+ |---------|-------------|
363
+ | `campaigns` | Show current campaign plan |
364
+ | `campaigns break` | Stop a running campaign |
365
+
366
+ ASCAM phases: Reconnaissance → Hardening → Red Teaming → Analysis → Monitoring
367
+
368
+ ### Upload Conversation Logs
369
+
370
+ Evaluate real production conversations against security judges.
371
+
372
+ | Command | Description |
373
+ |---------|-------------|
374
+ | `upload-logs <file>` | Upload JSON conversation logs |
375
+
376
+ Options: `--tag`, `--lang`
377
+
378
+ ### API Keys
379
+
380
+ | Command | Description |
381
+ |---------|-------------|
382
+ | `api-keys list` | List API keys |
383
+ | `api-keys create` | Create new key (--name required, --scopes: admin/write/read) |
384
+ | `api-keys update <id>` | Update key name, scopes, or active state |
385
+ | `api-keys revoke <id>` | Revoke (delete) an API key |
386
+
387
+ ### Members
388
+
389
+ | Command | Description |
390
+ |---------|-------------|
391
+ | `members list` | List organisation members |
392
+ | `members invite <email>` | Invite member (--role: admin/developer) |
393
+ | `members remove <id>` | Remove member |
394
+
347
395
  ### Results & Export
348
396
 
349
397
  ```bash
350
- # View experiment results (table, json, or csv)
351
- hb logs [experiment_id] [--format table] [--verdict pass|fail] [--page N] [--size N]
398
+ # View experiment results
399
+ hb logs [experiment_id] [--format table|json|html] [--verdict pass|fail] [--page N] [--size N]
400
+
401
+ # Export branded HTML report
402
+ hb logs <experiment_id> --format=html [-o report.html]
403
+
404
+ # Security posture
405
+ hb posture [--json] [--trends]
406
+
407
+ # Test coverage
408
+ hb coverage [--gaps] [--json]
352
409
 
353
- # Security posture score
354
- hb posture [--json]
410
+ # Findings
411
+ hb findings [--status open] [--severity high] [--json]
355
412
 
356
413
  # Export guardrails configuration
357
414
  hb guardrails [--vendor humanbound|openai] [--format json|yaml] [-o FILE]
@@ -379,7 +436,7 @@ hb switch abc123
379
436
  hb init -n "Support Bot" -e ./bot-config.json
380
437
 
381
438
  # Run adversarial test (uses project's default integration)
382
- hb test -t owasp_multi_turn -l unit
439
+ hb test -t humanbound/adversarial/owasp_multi_turn -l unit
383
440
 
384
441
  # Watch and review
385
442
  hb status --watch
@@ -1,3 +1,3 @@
1
1
  """Humanbound CLI - command line interface for AI agent security testing."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.2.0"
@@ -387,12 +387,9 @@ class HumanboundClient:
387
387
  return True
388
388
 
389
389
  def _exchange_for_api_token(self) -> None:
390
- """Exchange Auth0 token for AIandMe API session token."""
391
- # Always use production API for auth (token audience must match)
392
- auth_base_url = get_auth0_audience()
393
-
390
+ """Exchange Auth0 token for API session token."""
394
391
  response = requests.get(
395
- f"{auth_base_url}/auth",
392
+ f"{self.base_url}/auth",
396
393
  headers={"Authorization": f"Bearer {self._auth0_token}"},
397
394
  timeout=DEFAULT_TIMEOUT,
398
395
  )
@@ -870,6 +867,125 @@ class HumanboundClient:
870
867
  """
871
868
  self.delete(f"providers/{provider_id}")
872
869
 
870
+ # -------------------------------------------------------------------------
871
+ # Findings Methods
872
+ # -------------------------------------------------------------------------
873
+
874
+ def list_findings(self, project_id: str, status: Optional[str] = None, severity: Optional[str] = None, page: int = 1, size: int = 50) -> dict:
875
+ """List findings for a project."""
876
+ params = {"page": page, "size": size}
877
+ if status:
878
+ params["status"] = status
879
+ if severity:
880
+ params["severity"] = severity
881
+ return self.get(f"projects/{project_id}/findings", include_project=True, params=params)
882
+
883
+ def update_finding(self, project_id: str, finding_id: str, data: dict) -> dict:
884
+ """Update a finding."""
885
+ return self.put(f"projects/{project_id}/findings/{finding_id}", data=data)
886
+
887
+ # -------------------------------------------------------------------------
888
+ # Experiment Extensions
889
+ # -------------------------------------------------------------------------
890
+
891
+ def terminate_experiment(self, experiment_id: str) -> dict:
892
+ """Terminate a running experiment."""
893
+ return self.post(f"experiments/{experiment_id}/terminate", include_project=True)
894
+
895
+ def delete_experiment(self, experiment_id: str) -> Any:
896
+ """Delete an experiment."""
897
+ return self.delete(f"experiments/{experiment_id}", include_project=True)
898
+
899
+ # -------------------------------------------------------------------------
900
+ # Project Extensions
901
+ # -------------------------------------------------------------------------
902
+
903
+ def update_project(self, project_id: str, data: dict) -> dict:
904
+ """Update a project."""
905
+ return self.put(f"projects/{project_id}", data=data)
906
+
907
+ def delete_project(self, project_id: str) -> Any:
908
+ """Delete a project."""
909
+ return self.delete(f"projects/{project_id}")
910
+
911
+ # -------------------------------------------------------------------------
912
+ # API Key Methods
913
+ # -------------------------------------------------------------------------
914
+
915
+ def list_api_keys(self, page: int = 1, limit: int = 50) -> Any:
916
+ """List API keys."""
917
+ return self.get("api-keys", params={"page": page, "limit": limit}, include_org=False)
918
+
919
+ def create_api_key(self, name: str, scopes: str = "admin") -> dict:
920
+ """Create a new API key."""
921
+ return self.post("api-keys", data={"name": name, "scopes": scopes}, include_org=False)
922
+
923
+ def delete_api_key(self, key_id: str) -> Any:
924
+ """Delete an API key."""
925
+ return self.delete(f"api-keys/{key_id}", include_org=False)
926
+
927
+ def update_api_key(self, key_id: str, data: dict) -> dict:
928
+ """Update an API key."""
929
+ return self.put(f"api-keys/{key_id}", data=data, include_org=False)
930
+
931
+ # -------------------------------------------------------------------------
932
+ # Member Methods
933
+ # -------------------------------------------------------------------------
934
+
935
+ def list_members(self) -> Any:
936
+ """List organisation members."""
937
+ return self.get("members")
938
+
939
+ def invite_member(self, email: str, access_level: str) -> dict:
940
+ """Invite a member to the organisation."""
941
+ return self.post("members", data={"email": email, "access_level": access_level})
942
+
943
+ def remove_member(self, member_id: str) -> Any:
944
+ """Remove a member from the organisation."""
945
+ return self.delete(f"members/{member_id}")
946
+
947
+ # -------------------------------------------------------------------------
948
+ # Coverage Methods
949
+ # -------------------------------------------------------------------------
950
+
951
+ def get_coverage(self, project_id: str, include_gaps: bool = False) -> dict:
952
+ """Get test coverage for a project."""
953
+ params = {"include_gaps": "true"} if include_gaps else {}
954
+ return self.get(f"projects/{project_id}/coverage", params=params)
955
+
956
+ # -------------------------------------------------------------------------
957
+ # Posture Trends Methods
958
+ # -------------------------------------------------------------------------
959
+
960
+ def get_posture_trends(self, project_id: str) -> Any:
961
+ """Get posture trend history for a project."""
962
+ return self.get(f"projects/{project_id}/posture/trends")
963
+
964
+ # -------------------------------------------------------------------------
965
+ # Campaign Methods
966
+ # -------------------------------------------------------------------------
967
+
968
+ def get_campaign_plan(self, project_id: str) -> dict:
969
+ """Get the current campaign plan for a project."""
970
+ return self.get(f"projects/{project_id}/plan")
971
+
972
+ def break_campaign(self, project_id: str, campaign_id: str) -> dict:
973
+ """Break/stop a running campaign."""
974
+ return self.post(f"projects/{project_id}/plan/break", data={"campaign_id": campaign_id})
975
+
976
+ # -------------------------------------------------------------------------
977
+ # Upload Conversations Methods
978
+ # -------------------------------------------------------------------------
979
+
980
+ def upload_conversations(self, project_id: str, conversations: list, tag: Optional[str] = None, lang: Optional[str] = None) -> dict:
981
+ """Upload conversation logs for evaluation."""
982
+ data = {"conversations": conversations}
983
+ if tag:
984
+ data["tag"] = tag
985
+ if lang:
986
+ data["lang"] = lang
987
+ return self.post(f"projects/{project_id}/datasets/conversations", data=data, include_project=True)
988
+
873
989
 
874
990
  # Import ValidationError to this module
875
991
  from .exceptions import ValidationError
@@ -0,0 +1,27 @@
1
+ """CLI command modules."""
2
+
3
+ from . import (
4
+ auth, orgs, projects, experiments, init, test, logs, posture,
5
+ guardrails, docs, providers, findings, api_keys, members,
6
+ coverage, campaigns, upload_logs,
7
+ )
8
+
9
+ __all__ = [
10
+ "auth",
11
+ "orgs",
12
+ "projects",
13
+ "experiments",
14
+ "init",
15
+ "test",
16
+ "logs",
17
+ "posture",
18
+ "guardrails",
19
+ "docs",
20
+ "providers",
21
+ "findings",
22
+ "api_keys",
23
+ "members",
24
+ "coverage",
25
+ "campaigns",
26
+ "upload_logs",
27
+ ]