humanbound 2.0.3__tar.gz → 2.0.4__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.
- {humanbound-2.0.3 → humanbound-2.0.4}/PKG-INFO +9 -2
- {humanbound-2.0.3 → humanbound-2.0.4}/README.md +6 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound.egg-info/PKG-INFO +9 -2
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound.egg-info/SOURCES.txt +4 -5
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound.egg-info/requires.txt +2 -1
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/__init__.py +0 -4
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/api_keys.py +5 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/assessments.py +3 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/auth.py +3 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/campaigns.py +3 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/connect.py +53 -246
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/experiments.py +8 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/findings.py +20 -2
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/firewall.py +2 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/guardrails.py +2 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/logs.py +4 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/members.py +4 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/monitor.py +2 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/orgs.py +6 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/posture.py +17 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/projects.py +8 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/providers.py +5 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/redteam.py +2 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/report.py +2 -0
- humanbound-2.0.4/humanbound_cli/commands/telemetry.py +62 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/test.py +111 -43
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/webhooks.py +8 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/config.py +19 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/main.py +15 -2
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/mcp_server.py +1 -2
- humanbound-2.0.4/humanbound_cli/telemetry/__init__.py +86 -0
- humanbound-2.0.4/humanbound_cli/telemetry/client.py +176 -0
- humanbound-2.0.4/humanbound_cli/telemetry/consent.py +140 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/pyproject.toml +6 -3
- humanbound-2.0.3/humanbound_cli/commands/scan.py +0 -3
- humanbound-2.0.3/humanbound_cli/commands/sentinel.py +0 -1039
- humanbound-2.0.3/humanbound_cli/commands/upload_logs.py +0 -126
- humanbound-2.0.3/humanbound_cli/connectors/__init__.py +0 -3
- humanbound-2.0.3/humanbound_cli/connectors/microsoft.py +0 -1691
- {humanbound-2.0.3 → humanbound-2.0.4}/LICENSE +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound/bot.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound/callbacks.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound/orchestrators.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound/py.typed +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound/runner.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound/schemas.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound.egg-info/dependency_links.txt +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound.egg-info/entry_points.txt +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound.egg-info/top_level.txt +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/adapters/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/adapters/promptfoo.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/adapters/pyrit.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/client.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/_report_helper.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/completion.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/config_cmd.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/docs.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/commands/mcp.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/bot.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/callbacks.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/compliance.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/llm/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/llm/azureopenai.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/llm/claude.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/llm/gemini.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/llm/grok.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/llm/ollama.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/llm/openai.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/local_runner.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/base.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/behavioral_qa/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/behavioral_qa/config.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/behavioral_qa/generator.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/behavioral_qa/judge.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/behavioral_qa/orchestrator.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_agentic/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_agentic/config.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_agentic/generator.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_agentic/judge.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_agentic/orchestrator.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_single_turn/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_single_turn/config.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_single_turn/generator.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_single_turn/judge.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/orchestrators/owasp_single_turn/orchestrator.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/platform_runner.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/presenter.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/runner.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/schemas.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/engine/scope.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/exceptions.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/extractors/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/extractors/openapi.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/extractors/repo.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/py.typed +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/pytest_plugin/__init__.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/pytest_plugin/fixtures.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/pytest_plugin/report.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/report.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/report_builder.py +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/templates/compliance/banking.yaml +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/templates/compliance/ecommerce.yaml +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/templates/compliance/eu-ai-act.yaml +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/templates/compliance/healthcare.yaml +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/templates/compliance/insurance.yaml +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/templates/compliance/legal.yaml +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/templates/logo.svg +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/humanbound_cli/templates/report_base.html +0 -0
- {humanbound-2.0.3 → humanbound-2.0.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: humanbound
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Humanbound — open-source AI agent red-team engine, SDK, and CLI.
|
|
5
5
|
Author-email: Humanbound <hello@humanbound.ai>
|
|
6
6
|
Maintainer-email: Demetris Gerogiannis <hello@humanbound.ai>, Kostas Siabanis <hello@humanbound.ai>
|
|
@@ -34,8 +34,8 @@ Requires-Dist: rich>=13.0.0
|
|
|
34
34
|
Requires-Dist: requests>=2.32.0
|
|
35
35
|
Requires-Dist: pyyaml>=6.0.0
|
|
36
36
|
Requires-Dist: pydantic>=2.0
|
|
37
|
-
Requires-Dist: msal>=1.31.0
|
|
38
37
|
Requires-Dist: pyperclip>=1.8.0
|
|
38
|
+
Requires-Dist: posthog>=3.0
|
|
39
39
|
Provides-Extra: engine
|
|
40
40
|
Requires-Dist: openai>=1.0.0; extra == "engine"
|
|
41
41
|
Requires-Dist: anthropic>=0.20.0; extra == "engine"
|
|
@@ -58,6 +58,7 @@ Requires-Dist: mypy>=1.11; extra == "dev"
|
|
|
58
58
|
Requires-Dist: build>=1.2; extra == "dev"
|
|
59
59
|
Requires-Dist: twine>=5.0; extra == "dev"
|
|
60
60
|
Requires-Dist: pre-commit>=3.8; extra == "dev"
|
|
61
|
+
Requires-Dist: mkdocs>=1.6; extra == "dev"
|
|
61
62
|
Dynamic: license-file
|
|
62
63
|
|
|
63
64
|
<p align="center">
|
|
@@ -188,6 +189,12 @@ loop, release process, and CLA requirement (see [CLA.md](./CLA.md)).
|
|
|
188
189
|
- 🔒 [Report a security issue](./SECURITY.md) — **not via public Issues**
|
|
189
190
|
- 💬 [Join Discord](https://discord.gg/gQyXjVBF)
|
|
190
191
|
|
|
192
|
+
## Telemetry
|
|
193
|
+
|
|
194
|
+
The `hb` CLI sends anonymous usage data to help us improve it.
|
|
195
|
+
Disable with `hb telemetry disable`, `HB_TELEMETRY_DISABLED=1`, or
|
|
196
|
+
`DO_NOT_TRACK=1`. Full disclosure: [PRIVACY.md](./PRIVACY.md).
|
|
197
|
+
|
|
191
198
|
## License
|
|
192
199
|
|
|
193
200
|
[Apache-2.0](./LICENSE). Free to use in any context — commercial or
|
|
@@ -126,6 +126,12 @@ loop, release process, and CLA requirement (see [CLA.md](./CLA.md)).
|
|
|
126
126
|
- 🔒 [Report a security issue](./SECURITY.md) — **not via public Issues**
|
|
127
127
|
- 💬 [Join Discord](https://discord.gg/gQyXjVBF)
|
|
128
128
|
|
|
129
|
+
## Telemetry
|
|
130
|
+
|
|
131
|
+
The `hb` CLI sends anonymous usage data to help us improve it.
|
|
132
|
+
Disable with `hb telemetry disable`, `HB_TELEMETRY_DISABLED=1`, or
|
|
133
|
+
`DO_NOT_TRACK=1`. Full disclosure: [PRIVACY.md](./PRIVACY.md).
|
|
134
|
+
|
|
129
135
|
## License
|
|
130
136
|
|
|
131
137
|
[Apache-2.0](./LICENSE). Free to use in any context — commercial or
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: humanbound
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Humanbound — open-source AI agent red-team engine, SDK, and CLI.
|
|
5
5
|
Author-email: Humanbound <hello@humanbound.ai>
|
|
6
6
|
Maintainer-email: Demetris Gerogiannis <hello@humanbound.ai>, Kostas Siabanis <hello@humanbound.ai>
|
|
@@ -34,8 +34,8 @@ Requires-Dist: rich>=13.0.0
|
|
|
34
34
|
Requires-Dist: requests>=2.32.0
|
|
35
35
|
Requires-Dist: pyyaml>=6.0.0
|
|
36
36
|
Requires-Dist: pydantic>=2.0
|
|
37
|
-
Requires-Dist: msal>=1.31.0
|
|
38
37
|
Requires-Dist: pyperclip>=1.8.0
|
|
38
|
+
Requires-Dist: posthog>=3.0
|
|
39
39
|
Provides-Extra: engine
|
|
40
40
|
Requires-Dist: openai>=1.0.0; extra == "engine"
|
|
41
41
|
Requires-Dist: anthropic>=0.20.0; extra == "engine"
|
|
@@ -58,6 +58,7 @@ Requires-Dist: mypy>=1.11; extra == "dev"
|
|
|
58
58
|
Requires-Dist: build>=1.2; extra == "dev"
|
|
59
59
|
Requires-Dist: twine>=5.0; extra == "dev"
|
|
60
60
|
Requires-Dist: pre-commit>=3.8; extra == "dev"
|
|
61
|
+
Requires-Dist: mkdocs>=1.6; extra == "dev"
|
|
61
62
|
Dynamic: license-file
|
|
62
63
|
|
|
63
64
|
<p align="center">
|
|
@@ -188,6 +189,12 @@ loop, release process, and CLA requirement (see [CLA.md](./CLA.md)).
|
|
|
188
189
|
- 🔒 [Report a security issue](./SECURITY.md) — **not via public Issues**
|
|
189
190
|
- 💬 [Join Discord](https://discord.gg/gQyXjVBF)
|
|
190
191
|
|
|
192
|
+
## Telemetry
|
|
193
|
+
|
|
194
|
+
The `hb` CLI sends anonymous usage data to help us improve it.
|
|
195
|
+
Disable with `hb telemetry disable`, `HB_TELEMETRY_DISABLED=1`, or
|
|
196
|
+
`DO_NOT_TRACK=1`. Full disclosure: [PRIVACY.md](./PRIVACY.md).
|
|
197
|
+
|
|
191
198
|
## License
|
|
192
199
|
|
|
193
200
|
[Apache-2.0](./LICENSE). Free to use in any context — commercial or
|
|
@@ -50,13 +50,9 @@ humanbound_cli/commands/projects.py
|
|
|
50
50
|
humanbound_cli/commands/providers.py
|
|
51
51
|
humanbound_cli/commands/redteam.py
|
|
52
52
|
humanbound_cli/commands/report.py
|
|
53
|
-
humanbound_cli/commands/
|
|
54
|
-
humanbound_cli/commands/sentinel.py
|
|
53
|
+
humanbound_cli/commands/telemetry.py
|
|
55
54
|
humanbound_cli/commands/test.py
|
|
56
|
-
humanbound_cli/commands/upload_logs.py
|
|
57
55
|
humanbound_cli/commands/webhooks.py
|
|
58
|
-
humanbound_cli/connectors/__init__.py
|
|
59
|
-
humanbound_cli/connectors/microsoft.py
|
|
60
56
|
humanbound_cli/engine/__init__.py
|
|
61
57
|
humanbound_cli/engine/bot.py
|
|
62
58
|
humanbound_cli/engine/callbacks.py
|
|
@@ -97,6 +93,9 @@ humanbound_cli/extractors/repo.py
|
|
|
97
93
|
humanbound_cli/pytest_plugin/__init__.py
|
|
98
94
|
humanbound_cli/pytest_plugin/fixtures.py
|
|
99
95
|
humanbound_cli/pytest_plugin/report.py
|
|
96
|
+
humanbound_cli/telemetry/__init__.py
|
|
97
|
+
humanbound_cli/telemetry/client.py
|
|
98
|
+
humanbound_cli/telemetry/consent.py
|
|
100
99
|
humanbound_cli/templates/logo.svg
|
|
101
100
|
humanbound_cli/templates/report_base.html
|
|
102
101
|
humanbound_cli/templates/compliance/banking.yaml
|
|
@@ -3,8 +3,8 @@ rich>=13.0.0
|
|
|
3
3
|
requests>=2.32.0
|
|
4
4
|
pyyaml>=6.0.0
|
|
5
5
|
pydantic>=2.0
|
|
6
|
-
msal>=1.31.0
|
|
7
6
|
pyperclip>=1.8.0
|
|
7
|
+
posthog>=3.0
|
|
8
8
|
|
|
9
9
|
[dev]
|
|
10
10
|
pytest>=7.0.0
|
|
@@ -15,6 +15,7 @@ mypy>=1.11
|
|
|
15
15
|
build>=1.2
|
|
16
16
|
twine>=5.0
|
|
17
17
|
pre-commit>=3.8
|
|
18
|
+
mkdocs>=1.6
|
|
18
19
|
|
|
19
20
|
[engine]
|
|
20
21
|
openai>=1.0.0
|
|
@@ -23,9 +23,7 @@ from . import (
|
|
|
23
23
|
projects,
|
|
24
24
|
providers,
|
|
25
25
|
report,
|
|
26
|
-
sentinel,
|
|
27
26
|
test,
|
|
28
|
-
upload_logs,
|
|
29
27
|
webhooks,
|
|
30
28
|
)
|
|
31
29
|
|
|
@@ -50,8 +48,6 @@ __all__ = [
|
|
|
50
48
|
"api_keys",
|
|
51
49
|
"members",
|
|
52
50
|
"campaigns",
|
|
53
|
-
"upload_logs",
|
|
54
|
-
"sentinel",
|
|
55
51
|
"completion",
|
|
56
52
|
"connect",
|
|
57
53
|
"report",
|
|
@@ -10,6 +10,7 @@ from rich.panel import Panel
|
|
|
10
10
|
from rich.prompt import Confirm
|
|
11
11
|
from rich.table import Table
|
|
12
12
|
|
|
13
|
+
from .. import telemetry
|
|
13
14
|
from ..client import HumanboundClient
|
|
14
15
|
from ..exceptions import APIError, NotAuthenticatedError
|
|
15
16
|
|
|
@@ -38,6 +39,7 @@ def _list_keys(as_json: bool):
|
|
|
38
39
|
client = HumanboundClient()
|
|
39
40
|
|
|
40
41
|
if not client.is_authenticated():
|
|
42
|
+
telemetry.fire_gated_command_hit()
|
|
41
43
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
42
44
|
raise SystemExit(1)
|
|
43
45
|
|
|
@@ -106,6 +108,7 @@ def create_key(name: str, scopes: str):
|
|
|
106
108
|
client = HumanboundClient()
|
|
107
109
|
|
|
108
110
|
if not client.is_authenticated():
|
|
111
|
+
telemetry.fire_gated_command_hit()
|
|
109
112
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
110
113
|
raise SystemExit(1)
|
|
111
114
|
|
|
@@ -152,6 +155,7 @@ def update_key(key_id: str, name: str, scopes: str, active):
|
|
|
152
155
|
client = HumanboundClient()
|
|
153
156
|
|
|
154
157
|
if not client.is_authenticated():
|
|
158
|
+
telemetry.fire_gated_command_hit()
|
|
155
159
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
156
160
|
raise SystemExit(1)
|
|
157
161
|
|
|
@@ -198,6 +202,7 @@ def revoke_key(key_id: str, force: bool):
|
|
|
198
202
|
client = HumanboundClient()
|
|
199
203
|
|
|
200
204
|
if not client.is_authenticated():
|
|
205
|
+
telemetry.fire_gated_command_hit()
|
|
201
206
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
202
207
|
raise SystemExit(1)
|
|
203
208
|
|
|
@@ -9,6 +9,7 @@ from rich.console import Console
|
|
|
9
9
|
from rich.panel import Panel
|
|
10
10
|
from rich.table import Table
|
|
11
11
|
|
|
12
|
+
from .. import telemetry
|
|
12
13
|
from ..client import HumanboundClient
|
|
13
14
|
from ..exceptions import APIError, NotAuthenticatedError
|
|
14
15
|
|
|
@@ -53,6 +54,7 @@ def assessments_group(ctx, page, size, as_json):
|
|
|
53
54
|
client = HumanboundClient()
|
|
54
55
|
|
|
55
56
|
if not client.is_authenticated():
|
|
57
|
+
telemetry.fire_gated_command_hit()
|
|
56
58
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
57
59
|
raise SystemExit(1)
|
|
58
60
|
|
|
@@ -132,6 +134,7 @@ def show_assessment(assessment_id: str, as_json: bool):
|
|
|
132
134
|
client = HumanboundClient()
|
|
133
135
|
|
|
134
136
|
if not client.is_authenticated():
|
|
137
|
+
telemetry.fire_gated_command_hit()
|
|
135
138
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
136
139
|
raise SystemExit(1)
|
|
137
140
|
|
|
@@ -6,6 +6,7 @@ import click
|
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
from rich.panel import Panel
|
|
8
8
|
|
|
9
|
+
from .. import telemetry
|
|
9
10
|
from ..client import HumanboundClient
|
|
10
11
|
from ..config import DEFAULT_BASE_URL
|
|
11
12
|
from ..exceptions import AuthenticationError
|
|
@@ -44,6 +45,8 @@ def login(base_url: str, port: int, force: bool):
|
|
|
44
45
|
console.print("Starting authentication...")
|
|
45
46
|
client.login(callback_port=port)
|
|
46
47
|
|
|
48
|
+
telemetry.identify_from_credentials()
|
|
49
|
+
|
|
47
50
|
# Auto-select default organisation and resolve name
|
|
48
51
|
org_display = "not set"
|
|
49
52
|
if client.default_organisation_id:
|
|
@@ -10,6 +10,7 @@ from rich.panel import Panel
|
|
|
10
10
|
from rich.prompt import Confirm
|
|
11
11
|
from rich.table import Table
|
|
12
12
|
|
|
13
|
+
from .. import telemetry
|
|
13
14
|
from ..client import HumanboundClient
|
|
14
15
|
from ..exceptions import APIError, NotAuthenticatedError
|
|
15
16
|
|
|
@@ -40,6 +41,7 @@ def campaigns_group(ctx, as_json):
|
|
|
40
41
|
client = HumanboundClient()
|
|
41
42
|
|
|
42
43
|
if not client.is_authenticated():
|
|
44
|
+
telemetry.fire_gated_command_hit()
|
|
43
45
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
44
46
|
raise SystemExit(1)
|
|
45
47
|
|
|
@@ -136,6 +138,7 @@ def terminate_campaign(force: bool):
|
|
|
136
138
|
client = HumanboundClient()
|
|
137
139
|
|
|
138
140
|
if not client.is_authenticated():
|
|
141
|
+
telemetry.fire_gated_command_hit()
|
|
139
142
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
140
143
|
raise SystemExit(1)
|
|
141
144
|
|
|
@@ -13,6 +13,7 @@ import click
|
|
|
13
13
|
from rich.console import Console
|
|
14
14
|
from rich.panel import Panel
|
|
15
15
|
|
|
16
|
+
from .. import telemetry
|
|
16
17
|
from ..client import HumanboundClient
|
|
17
18
|
from ..exceptions import APIError, NotAuthenticatedError
|
|
18
19
|
from .test import _load_integration, _resolve_context
|
|
@@ -226,6 +227,30 @@ def _get_source_description(prompt: str, endpoint: str, repo: str, openapi: str)
|
|
|
226
227
|
return ", ".join(sources)
|
|
227
228
|
|
|
228
229
|
|
|
230
|
+
def _resolve_init_mode(
|
|
231
|
+
endpoint: str | None,
|
|
232
|
+
prompt: str | None,
|
|
233
|
+
repo: str | None,
|
|
234
|
+
openapi: str | None,
|
|
235
|
+
) -> str:
|
|
236
|
+
"""Derive the `init` telemetry mode from the CLI flags."""
|
|
237
|
+
if endpoint:
|
|
238
|
+
return "endpoint"
|
|
239
|
+
if prompt:
|
|
240
|
+
return "text"
|
|
241
|
+
if repo or openapi:
|
|
242
|
+
return "agentic"
|
|
243
|
+
return "none"
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _fire_init_event(mode: str, success: bool, duration_ms: int) -> None:
|
|
247
|
+
"""Emit the `init` telemetry event. Safe to call from try/finally."""
|
|
248
|
+
telemetry.capture(
|
|
249
|
+
"init",
|
|
250
|
+
{"mode": mode, "success": success, "duration_ms": duration_ms},
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
229
254
|
def _print_next(suggestions: list):
|
|
230
255
|
"""Print Next: suggestions block."""
|
|
231
256
|
console.print("\n[dim]Next:[/dim]")
|
|
@@ -297,24 +322,11 @@ def _derive_agent_name(endpoint: str) -> str:
|
|
|
297
322
|
|
|
298
323
|
|
|
299
324
|
@click.command("connect")
|
|
300
|
-
@click.option("--endpoint", "-e", help="Agent config JSON or file path
|
|
301
|
-
@click.option(
|
|
302
|
-
"--vendor",
|
|
303
|
-
"-v",
|
|
304
|
-
type=click.Choice(["microsoft"]),
|
|
305
|
-
help="Cloud vendor to scan (platform path)",
|
|
306
|
-
)
|
|
325
|
+
@click.option("--endpoint", "-e", help="Agent config JSON or file path")
|
|
307
326
|
@click.option("--name", "-n", help="Project name (optional, auto-generated)")
|
|
308
|
-
@click.option(
|
|
309
|
-
|
|
310
|
-
)
|
|
311
|
-
@click.option("--repo", "-r", type=click.Path(exists=True), help="Repository path (agent path)")
|
|
312
|
-
@click.option(
|
|
313
|
-
"--openapi", "-o", type=click.Path(exists=True), help="OpenAPI spec file (agent path)"
|
|
314
|
-
)
|
|
315
|
-
@click.option("--tenant", help="Azure tenant ID (platform path, bypasses browser)")
|
|
316
|
-
@click.option("--client-id", "client_id", help="Service principal client ID (platform path)")
|
|
317
|
-
@click.option("--client-secret", "client_secret", help="Service principal secret (platform path)")
|
|
327
|
+
@click.option("--prompt", "-p", type=click.Path(exists=True), help="System prompt file")
|
|
328
|
+
@click.option("--repo", "-r", type=click.Path(exists=True), help="Repository path")
|
|
329
|
+
@click.option("--openapi", "-o", type=click.Path(exists=True), help="OpenAPI spec file")
|
|
318
330
|
@click.option(
|
|
319
331
|
"--context",
|
|
320
332
|
"-c",
|
|
@@ -331,63 +343,47 @@ def _derive_agent_name(endpoint: str) -> str:
|
|
|
331
343
|
@click.option("--timeout", "-t", type=int, default=SCAN_TIMEOUT, help="Request timeout in seconds")
|
|
332
344
|
def connect_command(
|
|
333
345
|
endpoint,
|
|
334
|
-
vendor,
|
|
335
346
|
name,
|
|
336
347
|
prompt,
|
|
337
348
|
repo,
|
|
338
349
|
openapi,
|
|
339
|
-
tenant,
|
|
340
|
-
client_id,
|
|
341
|
-
client_secret,
|
|
342
350
|
context,
|
|
343
351
|
level,
|
|
344
352
|
yes,
|
|
345
353
|
timeout,
|
|
346
354
|
):
|
|
347
|
-
"""Connect your AI agent
|
|
355
|
+
"""Connect your AI agent.
|
|
348
356
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
\b
|
|
352
|
-
Agent path (--endpoint):
|
|
353
|
-
hb connect --endpoint ./bot-config.json
|
|
354
|
-
Probes your agent, extracts scope, creates project, runs first test.
|
|
355
|
-
|
|
356
|
-
\b
|
|
357
|
-
Platform path (--vendor):
|
|
358
|
-
hb connect --vendor microsoft
|
|
359
|
-
Scans cloud for shadow AI, evaluates 39 signals, saves to inventory.
|
|
357
|
+
Probes your agent, extracts scope, creates a project, and runs the first test.
|
|
360
358
|
|
|
361
359
|
\b
|
|
362
360
|
Examples:
|
|
363
361
|
hb connect --endpoint ./config.json
|
|
364
362
|
hb connect --endpoint ./config.json --prompt ./system.txt
|
|
365
|
-
hb connect --vendor microsoft
|
|
366
|
-
hb connect --vendor microsoft --tenant abc-123 --client-id x --client-secret y
|
|
367
363
|
"""
|
|
368
|
-
|
|
369
|
-
has_platform_flags = any([vendor, tenant, client_id, client_secret])
|
|
364
|
+
import time
|
|
370
365
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
)
|
|
375
|
-
raise SystemExit(1)
|
|
366
|
+
start = time.monotonic()
|
|
367
|
+
mode = _resolve_init_mode(endpoint, prompt, repo, openapi)
|
|
368
|
+
success = False
|
|
376
369
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
370
|
+
try:
|
|
371
|
+
has_agent_flags = any([endpoint, prompt, repo, openapi])
|
|
372
|
+
|
|
373
|
+
if has_agent_flags:
|
|
374
|
+
_connect_agent(endpoint, name, prompt, repo, openapi, context, level, yes, timeout)
|
|
375
|
+
else:
|
|
376
|
+
console.print("[yellow]Specify a source:[/yellow]")
|
|
377
|
+
console.print()
|
|
378
|
+
console.print(" hb connect --endpoint ./bot-config.json")
|
|
379
|
+
console.print()
|
|
380
|
+
console.print("[dim]Use --endpoint, --prompt, --repo, or --openapi.[/dim]")
|
|
381
|
+
raise SystemExit(1)
|
|
382
|
+
|
|
383
|
+
success = True
|
|
384
|
+
finally:
|
|
385
|
+
duration_ms = int((time.monotonic() - start) * 1000)
|
|
386
|
+
_fire_init_event(mode=mode, success=success, duration_ms=duration_ms)
|
|
391
387
|
|
|
392
388
|
|
|
393
389
|
# -- Agent path ----------------------------------------------------------------
|
|
@@ -593,6 +589,7 @@ def _connect_agent_platform(
|
|
|
593
589
|
)
|
|
594
590
|
|
|
595
591
|
except NotAuthenticatedError:
|
|
592
|
+
telemetry.fire_gated_command_hit()
|
|
596
593
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
597
594
|
raise SystemExit(1)
|
|
598
595
|
except APIError as e:
|
|
@@ -738,196 +735,6 @@ def _print_platform_note():
|
|
|
738
735
|
)
|
|
739
736
|
|
|
740
737
|
|
|
741
|
-
# -- Platform path -------------------------------------------------------------
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
def _connect_platform(vendor, name, tenant, client_id, client_secret, yes, timeout):
|
|
745
|
-
"""Platform path: scan -> assess -> auto-save -> show posture."""
|
|
746
|
-
from .discover import (
|
|
747
|
-
_display_auth_error,
|
|
748
|
-
_display_device_code,
|
|
749
|
-
_display_evaluations,
|
|
750
|
-
_display_persist_summary,
|
|
751
|
-
_display_results,
|
|
752
|
-
_get_connector,
|
|
753
|
-
)
|
|
754
|
-
|
|
755
|
-
client = HumanboundClient()
|
|
756
|
-
|
|
757
|
-
if not client.is_authenticated():
|
|
758
|
-
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
759
|
-
raise SystemExit(1)
|
|
760
|
-
|
|
761
|
-
if not client.organisation_id:
|
|
762
|
-
console.print("[yellow]No organisation selected.[/yellow]")
|
|
763
|
-
console.print("Use 'hb switch <id>' to select an organisation first.")
|
|
764
|
-
raise SystemExit(1)
|
|
765
|
-
|
|
766
|
-
# Default vendor
|
|
767
|
-
if not vendor:
|
|
768
|
-
if any([tenant, client_id, client_secret]):
|
|
769
|
-
vendor = "microsoft"
|
|
770
|
-
else:
|
|
771
|
-
console.print("[red]--vendor is required for platform path.[/red]")
|
|
772
|
-
console.print("[dim]Example: hb connect --vendor microsoft[/dim]")
|
|
773
|
-
raise SystemExit(1)
|
|
774
|
-
|
|
775
|
-
# Validate service principal flags (all-or-none)
|
|
776
|
-
sp_flags = [tenant, client_id, client_secret]
|
|
777
|
-
if any(sp_flags) and not all(sp_flags):
|
|
778
|
-
console.print(
|
|
779
|
-
"[red]Service principal auth requires all three: --tenant, --client-id, --client-secret[/red]"
|
|
780
|
-
)
|
|
781
|
-
raise SystemExit(1)
|
|
782
|
-
|
|
783
|
-
console.print()
|
|
784
|
-
console.print(
|
|
785
|
-
Panel(
|
|
786
|
-
"[bold]AI Service Discovery[/bold]\n\n"
|
|
787
|
-
f"Vendor: [bold]{vendor}[/bold]\n"
|
|
788
|
-
"Mode: [bold]connect[/bold] (scan + assess + save)\n\n"
|
|
789
|
-
"This will:\n"
|
|
790
|
-
" 1. Sign in to your cloud tenant\n"
|
|
791
|
-
" 2. Scan for AI services [dim](read-only)[/dim]\n"
|
|
792
|
-
" 3. Assess against 38 security signals\n"
|
|
793
|
-
" 4. Save results to your AI inventory",
|
|
794
|
-
border_style="blue",
|
|
795
|
-
)
|
|
796
|
-
)
|
|
797
|
-
console.print()
|
|
798
|
-
|
|
799
|
-
if not yes:
|
|
800
|
-
if not click.confirm("Proceed with discovery?", default=True):
|
|
801
|
-
console.print("[dim]Cancelled.[/dim]")
|
|
802
|
-
return
|
|
803
|
-
|
|
804
|
-
console.print()
|
|
805
|
-
|
|
806
|
-
try:
|
|
807
|
-
# -- Authenticate ------------------------------------------------------
|
|
808
|
-
connector = _get_connector(vendor, verbose=False)
|
|
809
|
-
|
|
810
|
-
if all(sp_flags):
|
|
811
|
-
# Service principal auth (non-interactive)
|
|
812
|
-
console.print("[dim]Authenticating with service principal...[/dim]")
|
|
813
|
-
try:
|
|
814
|
-
connector.authenticate_sp(
|
|
815
|
-
tenant_id=tenant,
|
|
816
|
-
client_id=client_id,
|
|
817
|
-
client_secret=client_secret,
|
|
818
|
-
)
|
|
819
|
-
except AttributeError:
|
|
820
|
-
console.print(
|
|
821
|
-
"[yellow]Service principal auth not yet supported for this vendor.[/yellow]"
|
|
822
|
-
)
|
|
823
|
-
console.print("[dim]Use browser auth instead: hb connect --vendor microsoft[/dim]")
|
|
824
|
-
raise SystemExit(1)
|
|
825
|
-
except PermissionError as e:
|
|
826
|
-
console.print(f"[red]Authentication failed:[/red] {e}")
|
|
827
|
-
raise SystemExit(1)
|
|
828
|
-
console.print("[green]Authenticated via service principal.[/green]\n")
|
|
829
|
-
else:
|
|
830
|
-
# Browser device-code flow
|
|
831
|
-
try:
|
|
832
|
-
connector.authenticate(callback=_display_device_code)
|
|
833
|
-
except PermissionError as e:
|
|
834
|
-
_display_auth_error(str(e))
|
|
835
|
-
raise SystemExit(1)
|
|
836
|
-
console.print("[green]Signed in successfully.[/green]\n")
|
|
837
|
-
|
|
838
|
-
# -- Scan --------------------------------------------------------------
|
|
839
|
-
with console.status("[bold blue]Scanning for AI services..."):
|
|
840
|
-
services, metadata = connector.discover()
|
|
841
|
-
|
|
842
|
-
if not services:
|
|
843
|
-
status = metadata.get("status", "unknown")
|
|
844
|
-
if status == "failed":
|
|
845
|
-
console.print("[red]Discovery failed.[/red] Could not query any APIs.")
|
|
846
|
-
else:
|
|
847
|
-
console.print("[yellow]No AI services found.[/yellow]")
|
|
848
|
-
return
|
|
849
|
-
|
|
850
|
-
console.print(f"Found [bold]{len(services)}[/bold] AI services. Analysing...\n")
|
|
851
|
-
|
|
852
|
-
# -- Analyse -----------------------------------------------------------
|
|
853
|
-
org_id = client.organisation_id
|
|
854
|
-
payload = {
|
|
855
|
-
"vendor": vendor,
|
|
856
|
-
"services": services,
|
|
857
|
-
"sources_metadata": {
|
|
858
|
-
"status": metadata.get("status", "unknown"),
|
|
859
|
-
"apis_queried": metadata.get("apis_queried", []),
|
|
860
|
-
"apis_failed": metadata.get("apis_failed", []),
|
|
861
|
-
"permissions_missing": metadata.get("permissions_missing", []),
|
|
862
|
-
},
|
|
863
|
-
"topology": metadata.get("topology", {}),
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
with console.status("[bold blue]Analysing discovered services..."):
|
|
867
|
-
analysis = client.post(
|
|
868
|
-
f"organisations/{org_id}/analyse",
|
|
869
|
-
data=payload,
|
|
870
|
-
include_org=False,
|
|
871
|
-
timeout=timeout,
|
|
872
|
-
)
|
|
873
|
-
|
|
874
|
-
# -- Overlay evaluator risk onto services for consistent display -------
|
|
875
|
-
evals = analysis.get("evaluations", [])
|
|
876
|
-
if evals:
|
|
877
|
-
eval_risk_map = {
|
|
878
|
-
ev["service_name"]: ev["risk_level"]
|
|
879
|
-
for ev in evals
|
|
880
|
-
if "service_name" in ev and "risk_level" in ev
|
|
881
|
-
}
|
|
882
|
-
for svc in analysis.get("services", []):
|
|
883
|
-
eval_rl = eval_risk_map.get(svc.get("name"))
|
|
884
|
-
if eval_rl:
|
|
885
|
-
svc["risk"] = eval_rl
|
|
886
|
-
|
|
887
|
-
new_by_risk = {}
|
|
888
|
-
for svc in analysis.get("services", []):
|
|
889
|
-
r = svc.get("risk", "unknown")
|
|
890
|
-
new_by_risk[r] = new_by_risk.get(r, 0) + 1
|
|
891
|
-
if "summary" in analysis:
|
|
892
|
-
analysis["summary"]["by_risk"] = new_by_risk
|
|
893
|
-
|
|
894
|
-
# -- Display -----------------------------------------------------------
|
|
895
|
-
_display_results(analysis, metadata)
|
|
896
|
-
|
|
897
|
-
evaluations = analysis.get("evaluations", [])
|
|
898
|
-
posture_estimate = analysis.get("posture_estimate")
|
|
899
|
-
if evaluations:
|
|
900
|
-
_display_evaluations(evaluations, posture_estimate)
|
|
901
|
-
|
|
902
|
-
# -- Auto-save (always persist in connect mode) ------------------------
|
|
903
|
-
nonce = analysis.get("nonce")
|
|
904
|
-
if nonce:
|
|
905
|
-
with console.status("[bold blue]Saving to inventory..."):
|
|
906
|
-
persist_result = client.persist_discovery(nonce)
|
|
907
|
-
_display_persist_summary(persist_result)
|
|
908
|
-
else:
|
|
909
|
-
console.print(
|
|
910
|
-
"\n[yellow]Cannot persist:[/yellow] server did not return a session token."
|
|
911
|
-
)
|
|
912
|
-
|
|
913
|
-
# -- Next suggestions --------------------------------------------------
|
|
914
|
-
_print_next(
|
|
915
|
-
[
|
|
916
|
-
("hb inventory", "Browse all assets"),
|
|
917
|
-
("hb posture --org", "Full org posture (3 dimensions)"),
|
|
918
|
-
("hb report --org", "Org-wide report"),
|
|
919
|
-
("hb discover --report", "Export HTML report"),
|
|
920
|
-
]
|
|
921
|
-
)
|
|
922
|
-
|
|
923
|
-
except NotAuthenticatedError:
|
|
924
|
-
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
925
|
-
raise SystemExit(1)
|
|
926
|
-
except APIError as e:
|
|
927
|
-
console.print(f"[red]Error:[/red] {e}")
|
|
928
|
-
raise SystemExit(1)
|
|
929
|
-
|
|
930
|
-
|
|
931
738
|
# -- Monitoring recommendation -------------------------------------------------
|
|
932
739
|
|
|
933
740
|
# Regulations that require or strongly recommend continuous AI monitoring
|
|
@@ -11,6 +11,7 @@ from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
|
11
11
|
from rich.prompt import Confirm
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
|
|
14
|
+
from .. import telemetry
|
|
14
15
|
from ..client import HumanboundClient
|
|
15
16
|
from ..exceptions import APIError, NotAuthenticatedError
|
|
16
17
|
|
|
@@ -84,6 +85,7 @@ def _list_experiments(page: int, size: int):
|
|
|
84
85
|
console.print(f"\n[dim]Page {page} of more. Use --page to navigate.[/dim]")
|
|
85
86
|
|
|
86
87
|
except NotAuthenticatedError:
|
|
88
|
+
telemetry.fire_gated_command_hit()
|
|
87
89
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
88
90
|
raise SystemExit(1)
|
|
89
91
|
except APIError as e:
|
|
@@ -149,6 +151,7 @@ def show_experiment(experiment_id: str):
|
|
|
149
151
|
console.print(f" {i}. {insight.get('explanation', '')[:80]}...")
|
|
150
152
|
|
|
151
153
|
except NotAuthenticatedError:
|
|
154
|
+
telemetry.fire_gated_command_hit()
|
|
152
155
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
153
156
|
raise SystemExit(1)
|
|
154
157
|
except APIError as e:
|
|
@@ -227,6 +230,7 @@ def experiment_status(experiment_id: str, watch: bool, interval: int, show_all:
|
|
|
227
230
|
_show_failure_details(client, experiment_id)
|
|
228
231
|
|
|
229
232
|
except NotAuthenticatedError:
|
|
233
|
+
telemetry.fire_gated_command_hit()
|
|
230
234
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
231
235
|
raise SystemExit(1)
|
|
232
236
|
except APIError as e:
|
|
@@ -315,6 +319,7 @@ def _poll_all_experiments(client: HumanboundClient):
|
|
|
315
319
|
cycle += 1
|
|
316
320
|
|
|
317
321
|
except NotAuthenticatedError:
|
|
322
|
+
telemetry.fire_gated_command_hit()
|
|
318
323
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
319
324
|
raise SystemExit(1)
|
|
320
325
|
except APIError as e:
|
|
@@ -424,6 +429,7 @@ def experiment_wait(experiment_id: str, timeout: int):
|
|
|
424
429
|
poll_interval = min(poll_interval * 2, max_interval)
|
|
425
430
|
|
|
426
431
|
except NotAuthenticatedError:
|
|
432
|
+
telemetry.fire_gated_command_hit()
|
|
427
433
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
428
434
|
raise SystemExit(1)
|
|
429
435
|
except APIError as e:
|
|
@@ -467,6 +473,7 @@ def terminate_experiment(experiment_id: str):
|
|
|
467
473
|
console.print(f"[dim]ID: {experiment_id}[/dim]")
|
|
468
474
|
|
|
469
475
|
except NotAuthenticatedError:
|
|
476
|
+
telemetry.fire_gated_command_hit()
|
|
470
477
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
471
478
|
raise SystemExit(1)
|
|
472
479
|
except APIError as e:
|
|
@@ -508,6 +515,7 @@ def delete_experiment(experiment_id: str, force: bool):
|
|
|
508
515
|
console.print(f"[dim]{exp_name} ({experiment_id})[/dim]")
|
|
509
516
|
|
|
510
517
|
except NotAuthenticatedError:
|
|
518
|
+
telemetry.fire_gated_command_hit()
|
|
511
519
|
console.print("[red]Not authenticated.[/red] Run 'hb login' first.")
|
|
512
520
|
raise SystemExit(1)
|
|
513
521
|
except APIError as e:
|