strathon-cli 1.0.1__tar.gz → 1.1.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.
- strathon_cli-1.1.0/PKG-INFO +94 -0
- strathon_cli-1.1.0/README.md +63 -0
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/pyproject.toml +13 -4
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/strathon_cli/__init__.py +1 -1
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/strathon_cli/main.py +251 -13
- strathon_cli-1.1.0/strathon_cli.egg-info/PKG-INFO +94 -0
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/strathon_cli.egg-info/SOURCES.txt +1 -0
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/strathon_cli.egg-info/requires.txt +1 -0
- strathon_cli-1.0.1/PKG-INFO +0 -21
- strathon_cli-1.0.1/strathon_cli.egg-info/PKG-INFO +0 -21
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/setup.cfg +0 -0
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/strathon_cli/client.py +0 -0
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/strathon_cli.egg-info/dependency_links.txt +0 -0
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/strathon_cli.egg-info/entry_points.txt +0 -0
- {strathon_cli-1.0.1 → strathon_cli-1.1.0}/strathon_cli.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strathon-cli
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: CLI for Strathon, the open-source AI agent firewall.
|
|
5
|
+
Author-email: Strathon <hello@getstrathon.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://getstrathon.com
|
|
8
|
+
Project-URL: Repository, https://github.com/strathon/strathon
|
|
9
|
+
Project-URL: Documentation, https://getstrathon.com/docs
|
|
10
|
+
Project-URL: Issues, https://github.com/strathon/strathon/issues
|
|
11
|
+
Project-URL: Discord, https://discord.gg/Ta9XRmh4H
|
|
12
|
+
Keywords: ai,agents,firewall,security,cli,llm,compliance
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
Requires-Dist: click>=8.1
|
|
28
|
+
Requires-Dist: httpx>=0.27
|
|
29
|
+
Requires-Dist: rich>=13.0
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
|
|
32
|
+
# Strathon CLI
|
|
33
|
+
|
|
34
|
+
Command-line interface for [Strathon](https://github.com/strathon/strathon), the open-source AI agent firewall.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install strathon-cli
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Set your API key
|
|
46
|
+
export STRATHON_API_KEY=stra_...
|
|
47
|
+
|
|
48
|
+
# Policy management
|
|
49
|
+
strathon policies list
|
|
50
|
+
strathon policies create --name "block-email" \
|
|
51
|
+
--expr 'attrs["gen_ai.tool.name"] == "send_email"' --action block
|
|
52
|
+
strathon policies create --template block-prompt-injection
|
|
53
|
+
strathon policies create --from-english "block all shell commands"
|
|
54
|
+
strathon policies import policies.yaml
|
|
55
|
+
strathon policies test --name my-policy --last 100
|
|
56
|
+
|
|
57
|
+
# Traces and spans
|
|
58
|
+
strathon traces list --last 1h
|
|
59
|
+
strathon traces tree <trace-id>
|
|
60
|
+
strathon spans search --tool send_email --limit 50
|
|
61
|
+
|
|
62
|
+
# Operations
|
|
63
|
+
strathon halts create --scope project --reason "Emergency"
|
|
64
|
+
strathon budgets list
|
|
65
|
+
strathon approvals list --pending
|
|
66
|
+
|
|
67
|
+
# Compliance and audit
|
|
68
|
+
strathon compliance export --format sarif
|
|
69
|
+
strathon audit list --last 24h
|
|
70
|
+
|
|
71
|
+
# Administration
|
|
72
|
+
strathon admin list-users
|
|
73
|
+
strathon admin reset-password --email user@company.com
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Every command supports `--json` for scripting and CI pipelines.
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
| Variable | Required | Default |
|
|
81
|
+
|----------|----------|---------|
|
|
82
|
+
| `STRATHON_API_KEY` | Yes | |
|
|
83
|
+
| `STRATHON_ENDPOINT` | No | `http://localhost:4318` |
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
- [CLI reference](https://getstrathon.com/docs/cli)
|
|
88
|
+
- [Policy engine](https://getstrathon.com/docs/intervention)
|
|
89
|
+
- [CEL reference](https://getstrathon.com/docs/cel-reference)
|
|
90
|
+
- [GitHub](https://github.com/strathon/strathon)
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT. See [LICENSE](https://github.com/strathon/strathon/blob/main/LICENSE).
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Strathon CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for [Strathon](https://github.com/strathon/strathon), the open-source AI agent firewall.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install strathon-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Set your API key
|
|
15
|
+
export STRATHON_API_KEY=stra_...
|
|
16
|
+
|
|
17
|
+
# Policy management
|
|
18
|
+
strathon policies list
|
|
19
|
+
strathon policies create --name "block-email" \
|
|
20
|
+
--expr 'attrs["gen_ai.tool.name"] == "send_email"' --action block
|
|
21
|
+
strathon policies create --template block-prompt-injection
|
|
22
|
+
strathon policies create --from-english "block all shell commands"
|
|
23
|
+
strathon policies import policies.yaml
|
|
24
|
+
strathon policies test --name my-policy --last 100
|
|
25
|
+
|
|
26
|
+
# Traces and spans
|
|
27
|
+
strathon traces list --last 1h
|
|
28
|
+
strathon traces tree <trace-id>
|
|
29
|
+
strathon spans search --tool send_email --limit 50
|
|
30
|
+
|
|
31
|
+
# Operations
|
|
32
|
+
strathon halts create --scope project --reason "Emergency"
|
|
33
|
+
strathon budgets list
|
|
34
|
+
strathon approvals list --pending
|
|
35
|
+
|
|
36
|
+
# Compliance and audit
|
|
37
|
+
strathon compliance export --format sarif
|
|
38
|
+
strathon audit list --last 24h
|
|
39
|
+
|
|
40
|
+
# Administration
|
|
41
|
+
strathon admin list-users
|
|
42
|
+
strathon admin reset-password --email user@company.com
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Every command supports `--json` for scripting and CI pipelines.
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
| Variable | Required | Default |
|
|
50
|
+
|----------|----------|---------|
|
|
51
|
+
| `STRATHON_API_KEY` | Yes | |
|
|
52
|
+
| `STRATHON_ENDPOINT` | No | `http://localhost:4318` |
|
|
53
|
+
|
|
54
|
+
## Documentation
|
|
55
|
+
|
|
56
|
+
- [CLI reference](https://getstrathon.com/docs/cli)
|
|
57
|
+
- [Policy engine](https://getstrathon.com/docs/intervention)
|
|
58
|
+
- [CEL reference](https://getstrathon.com/docs/cel-reference)
|
|
59
|
+
- [GitHub](https://github.com/strathon/strathon)
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT. See [LICENSE](https://github.com/strathon/strathon/blob/main/LICENSE).
|
|
@@ -4,25 +4,32 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "strathon-cli"
|
|
7
|
-
version = "1.0
|
|
8
|
-
description = "
|
|
7
|
+
version = "1.1.0"
|
|
8
|
+
description = "CLI for Strathon, the open-source AI agent firewall."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = {text = "MIT"}
|
|
12
|
-
authors = [{name = "Strathon", email = "
|
|
13
|
-
keywords = ["ai", "agents", "firewall", "security", "cli"]
|
|
12
|
+
authors = [{name = "Strathon", email = "hello@getstrathon.com"}]
|
|
13
|
+
keywords = ["ai", "agents", "firewall", "security", "cli", "llm", "compliance"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 4 - Beta",
|
|
16
16
|
"Environment :: Console",
|
|
17
17
|
"Intended Audience :: Developers",
|
|
18
18
|
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
19
20
|
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
20
25
|
"Topic :: Security",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
21
27
|
]
|
|
22
28
|
dependencies = [
|
|
23
29
|
"click>=8.1",
|
|
24
30
|
"httpx>=0.27",
|
|
25
31
|
"rich>=13.0",
|
|
32
|
+
"pyyaml>=6.0",
|
|
26
33
|
]
|
|
27
34
|
|
|
28
35
|
[project.scripts]
|
|
@@ -32,3 +39,5 @@ strathon = "strathon_cli.main:cli"
|
|
|
32
39
|
Homepage = "https://getstrathon.com"
|
|
33
40
|
Repository = "https://github.com/strathon/strathon"
|
|
34
41
|
Documentation = "https://getstrathon.com/docs"
|
|
42
|
+
Issues = "https://github.com/strathon/strathon/issues"
|
|
43
|
+
Discord = "https://discord.gg/Ta9XRmh4H"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Usage:
|
|
4
4
|
strathon policies list
|
|
5
|
-
strathon policies create --name "block
|
|
5
|
+
strathon policies create --name "block-email" --expr ... --action block
|
|
6
6
|
strathon policies delete <id>
|
|
7
7
|
strathon traces list
|
|
8
8
|
strathon spans search --q "send_email"
|
|
@@ -87,16 +87,113 @@ def policies_list(as_json: bool):
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
@policies.command("create")
|
|
90
|
-
@click.option("--name",
|
|
91
|
-
@click.option("--expr",
|
|
92
|
-
@click.option("--
|
|
93
|
-
|
|
90
|
+
@click.option("--name", default=None, help="Policy name")
|
|
91
|
+
@click.option("--expr", default=None, help="CEL match expression")
|
|
92
|
+
@click.option("--template", default=None,
|
|
93
|
+
help="Create from a built-in template (e.g. block-prompt-injection)")
|
|
94
|
+
@click.option("--from-english", "from_english", default=None,
|
|
95
|
+
help="Describe policy in plain English")
|
|
96
|
+
@click.option("--action", default=None,
|
|
97
|
+
type=click.Choice(
|
|
98
|
+
["block", "steer", "throttle",
|
|
99
|
+
"log", "alert", "require_approval"]),
|
|
94
100
|
help="Enforcement action")
|
|
95
101
|
@click.option("--shadow", is_flag=True, help="Create as shadow policy")
|
|
96
102
|
@click.option("--priority", default=0, help="Priority (higher = first)")
|
|
97
103
|
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
98
|
-
def policies_create(name, expr, action, shadow, priority, as_json):
|
|
99
|
-
"""Create a new policy.
|
|
104
|
+
def policies_create(name, expr, template, from_english, action, shadow, priority, as_json):
|
|
105
|
+
"""Create a new policy.
|
|
106
|
+
|
|
107
|
+
Three modes:
|
|
108
|
+
|
|
109
|
+
\b
|
|
110
|
+
--expr Provide a CEL expression directly
|
|
111
|
+
--template Create from a built-in template by name
|
|
112
|
+
--from-english Describe the policy in plain English
|
|
113
|
+
|
|
114
|
+
With --template, --name and --action are optional (the template provides defaults).
|
|
115
|
+
With --from-english, the generated CEL is shown for confirmation before creating.
|
|
116
|
+
"""
|
|
117
|
+
modes = sum(1 for x in (expr, template, from_english) if x is not None)
|
|
118
|
+
if modes == 0:
|
|
119
|
+
raise click.UsageError("Provide one of: --expr, --template, or --from-english")
|
|
120
|
+
if modes > 1:
|
|
121
|
+
raise click.UsageError("Only one of --expr, --template, or --from-english can be used")
|
|
122
|
+
|
|
123
|
+
if template:
|
|
124
|
+
# Fetch template from the receiver and create from it.
|
|
125
|
+
templates_resp = api_get("/v1/policy-templates")
|
|
126
|
+
templates_list = templates_resp.get("data", [])
|
|
127
|
+
match = None
|
|
128
|
+
for t in templates_list:
|
|
129
|
+
slug = t.get("slug") or t.get("name", "").lower().replace(" ", "-")
|
|
130
|
+
if slug == template or t.get("name", "").lower() == template.lower():
|
|
131
|
+
match = t
|
|
132
|
+
break
|
|
133
|
+
if not match:
|
|
134
|
+
available = [t.get("slug") or t.get("name", "").lower().replace(" ", "-")
|
|
135
|
+
for t in templates_list]
|
|
136
|
+
click.echo(f"Template '{template}' not found.", err=True)
|
|
137
|
+
if available:
|
|
138
|
+
click.echo(f"Available: {', '.join(available)}", err=True)
|
|
139
|
+
raise SystemExit(1)
|
|
140
|
+
|
|
141
|
+
body = {
|
|
142
|
+
"name": name or match.get("name", template),
|
|
143
|
+
"match_expression": match.get("match_expression", ""),
|
|
144
|
+
"action": action or match.get("action", "block"),
|
|
145
|
+
"shadow": shadow,
|
|
146
|
+
"priority": priority,
|
|
147
|
+
}
|
|
148
|
+
result = api_post("/v1/policies", json=body)
|
|
149
|
+
if as_json:
|
|
150
|
+
click.echo(json_mod.dumps(result, indent=2))
|
|
151
|
+
else:
|
|
152
|
+
click.echo(f"Created policy {result.get('id', '')} from template '{template}'")
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
if from_english:
|
|
156
|
+
# Ask the receiver to generate CEL from English description.
|
|
157
|
+
try:
|
|
158
|
+
gen_result = api_post("/v1/policies/generate", json={"description": from_english})
|
|
159
|
+
except (SystemExit, Exception):
|
|
160
|
+
click.echo("AI policy generation failed.", err=True)
|
|
161
|
+
click.echo("Ensure STRATHON_AI_API_KEY is set on the receiver.", err=True)
|
|
162
|
+
click.echo("Manual reference: getstrathon.com/docs/cel-reference", err=True)
|
|
163
|
+
raise SystemExit(1)
|
|
164
|
+
generated_expr = gen_result.get("match_expression", "")
|
|
165
|
+
generated_action = gen_result.get("action", "block")
|
|
166
|
+
generated_name = gen_result.get("name", from_english[:50])
|
|
167
|
+
|
|
168
|
+
console.print(f"\n [bold]Description:[/] {from_english}")
|
|
169
|
+
console.print(f" [bold]Generated CEL:[/] {generated_expr}")
|
|
170
|
+
console.print(f" [bold]Action:[/] {action or generated_action}")
|
|
171
|
+
console.print()
|
|
172
|
+
|
|
173
|
+
if not click.confirm(" Create this policy?"):
|
|
174
|
+
click.echo("Aborted.")
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
body = {
|
|
178
|
+
"name": name or generated_name,
|
|
179
|
+
"match_expression": generated_expr,
|
|
180
|
+
"action": action or generated_action,
|
|
181
|
+
"shadow": shadow,
|
|
182
|
+
"priority": priority,
|
|
183
|
+
}
|
|
184
|
+
result = api_post("/v1/policies", json=body)
|
|
185
|
+
if as_json:
|
|
186
|
+
click.echo(json_mod.dumps(result, indent=2))
|
|
187
|
+
else:
|
|
188
|
+
click.echo(f"Created policy {result.get('id', '')} ({body['name']})")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
# Direct --expr mode (original behavior).
|
|
192
|
+
if not name:
|
|
193
|
+
raise click.UsageError("--name is required when using --expr")
|
|
194
|
+
if not action:
|
|
195
|
+
raise click.UsageError("--action is required when using --expr")
|
|
196
|
+
|
|
100
197
|
body = {
|
|
101
198
|
"name": name,
|
|
102
199
|
"match_expression": expr,
|
|
@@ -112,6 +209,133 @@ def policies_create(name, expr, action, shadow, priority, as_json):
|
|
|
112
209
|
click.echo(f"Created policy {result.get('id', '')} ({name})")
|
|
113
210
|
|
|
114
211
|
|
|
212
|
+
@policies.command("import")
|
|
213
|
+
@click.argument("filepath", type=click.Path(exists=True))
|
|
214
|
+
@click.option("--dry-run", is_flag=True, help="Validate without creating")
|
|
215
|
+
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
216
|
+
def policies_import(filepath, dry_run, as_json):
|
|
217
|
+
"""Bulk import policies from a YAML or JSON file.
|
|
218
|
+
|
|
219
|
+
\b
|
|
220
|
+
Expected format (YAML):
|
|
221
|
+
policies:
|
|
222
|
+
- name: block-email
|
|
223
|
+
match_expression: 'attrs["gen_ai.tool.name"] == "send_email"'
|
|
224
|
+
action: block
|
|
225
|
+
- name: log-shell
|
|
226
|
+
match_expression: 'attrs["gen_ai.tool.name"] == "run_shell"'
|
|
227
|
+
action: log
|
|
228
|
+
"""
|
|
229
|
+
import yaml # noqa: E402 — lazy import, yaml is optional dep
|
|
230
|
+
|
|
231
|
+
with open(filepath) as f:
|
|
232
|
+
if filepath.endswith((".yaml", ".yml")):
|
|
233
|
+
data = yaml.safe_load(f)
|
|
234
|
+
else:
|
|
235
|
+
data = json_mod.load(f)
|
|
236
|
+
|
|
237
|
+
items = data.get("policies", [])
|
|
238
|
+
if not items:
|
|
239
|
+
click.echo("No policies found in file.", err=True)
|
|
240
|
+
raise SystemExit(1)
|
|
241
|
+
|
|
242
|
+
results = []
|
|
243
|
+
for i, p in enumerate(items):
|
|
244
|
+
p_name = p.get("name", f"imported-{i}")
|
|
245
|
+
p_expr = p.get("match_expression", "")
|
|
246
|
+
p_action = p.get("action", "block")
|
|
247
|
+
if not p_expr:
|
|
248
|
+
click.echo(f"Skipping '{p_name}': no match_expression", err=True)
|
|
249
|
+
continue
|
|
250
|
+
|
|
251
|
+
if dry_run:
|
|
252
|
+
results.append({"name": p_name, "status": "valid"})
|
|
253
|
+
continue
|
|
254
|
+
|
|
255
|
+
body = {
|
|
256
|
+
"name": p_name,
|
|
257
|
+
"match_expression": p_expr,
|
|
258
|
+
"action": p_action,
|
|
259
|
+
"shadow": p.get("shadow", False),
|
|
260
|
+
"priority": p.get("priority", 0),
|
|
261
|
+
}
|
|
262
|
+
result = api_post("/v1/policies", json=body)
|
|
263
|
+
results.append(result)
|
|
264
|
+
|
|
265
|
+
if as_json:
|
|
266
|
+
click.echo(json_mod.dumps(results, indent=2))
|
|
267
|
+
else:
|
|
268
|
+
verb = "validated" if dry_run else "imported"
|
|
269
|
+
click.echo(f"{verb} {len(results)} policies from {filepath}")
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@policies.command("test")
|
|
273
|
+
@click.option("--name", required=True, help="Policy name to test")
|
|
274
|
+
@click.option("--last", "last_n", default=100, type=int,
|
|
275
|
+
help="Number of recent traces to test against (default: 100)")
|
|
276
|
+
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
277
|
+
def policies_test(name, last_n, as_json):
|
|
278
|
+
"""Test a policy against recent traces (dry run).
|
|
279
|
+
|
|
280
|
+
Fetches the last N traces and shows which spans would have matched
|
|
281
|
+
the named policy. Useful for validating a policy before enabling it.
|
|
282
|
+
"""
|
|
283
|
+
# Get the policy by name.
|
|
284
|
+
policies_resp = api_get("/v1/policies")
|
|
285
|
+
policy_list = policies_resp.get("policies", [])
|
|
286
|
+
policy = None
|
|
287
|
+
for p in policy_list:
|
|
288
|
+
if p.get("name") == name:
|
|
289
|
+
policy = p
|
|
290
|
+
break
|
|
291
|
+
if not policy:
|
|
292
|
+
click.echo(f"Policy '{name}' not found.", err=True)
|
|
293
|
+
raise SystemExit(1)
|
|
294
|
+
|
|
295
|
+
# Fetch recent traces.
|
|
296
|
+
traces_resp = api_get("/v1/traces", params={"limit": last_n})
|
|
297
|
+
traces = traces_resp.get("traces", [])
|
|
298
|
+
|
|
299
|
+
if not traces:
|
|
300
|
+
click.echo("No traces found to test against.")
|
|
301
|
+
return
|
|
302
|
+
|
|
303
|
+
# Ask the receiver to evaluate the policy against these traces.
|
|
304
|
+
test_result = api_post("/v1/policies/simulate", json={
|
|
305
|
+
"match_expression": policy.get("match_expression", ""),
|
|
306
|
+
"hours": 24,
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
matches = test_result.get("matches", [])
|
|
310
|
+
|
|
311
|
+
if as_json:
|
|
312
|
+
click.echo(json_mod.dumps(test_result, indent=2))
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
click.echo(f"Policy: {name} ({policy.get('action', 'block')})")
|
|
316
|
+
click.echo(f"Tested against: {test_result.get('traces_tested', len(traces))} traces, "
|
|
317
|
+
f"{test_result.get('spans_tested', 0)} spans")
|
|
318
|
+
click.echo(f"Matches: {len(matches)}")
|
|
319
|
+
click.echo()
|
|
320
|
+
|
|
321
|
+
if matches:
|
|
322
|
+
table = Table(title="Matched Spans")
|
|
323
|
+
table.add_column("Trace ID", style="dim")
|
|
324
|
+
table.add_column("Span Name")
|
|
325
|
+
table.add_column("Tool")
|
|
326
|
+
table.add_column("Timestamp")
|
|
327
|
+
for m in matches[:20]:
|
|
328
|
+
table.add_row(
|
|
329
|
+
m.get("trace_id", "")[:12],
|
|
330
|
+
m.get("span_name", ""),
|
|
331
|
+
m.get("tool_name", ""),
|
|
332
|
+
m.get("timestamp", ""),
|
|
333
|
+
)
|
|
334
|
+
console.print(table)
|
|
335
|
+
if len(matches) > 20:
|
|
336
|
+
click.echo(f" ... and {len(matches) - 20} more (use --json for full list)")
|
|
337
|
+
|
|
338
|
+
|
|
115
339
|
@policies.command("get")
|
|
116
340
|
@click.argument("policy_id")
|
|
117
341
|
@click.option("--json", "as_json", is_flag=True)
|
|
@@ -507,17 +731,31 @@ def compliance():
|
|
|
507
731
|
|
|
508
732
|
|
|
509
733
|
@compliance.command("export")
|
|
510
|
-
@click.option("--json", "
|
|
511
|
-
|
|
734
|
+
@click.option("--format", "fmt", type=click.Choice(["json", "sarif"]), default="json",
|
|
735
|
+
help="Output format. 'sarif' emits a SARIF 2.1.0 log.")
|
|
736
|
+
@click.option("--output", "-o", "output", type=click.Path(), default=None,
|
|
737
|
+
help="Write the package to a file instead of stdout.")
|
|
738
|
+
@click.option("--json", "as_json", is_flag=True,
|
|
739
|
+
help="Print the raw JSON package (alias for --format json -o -).")
|
|
740
|
+
def compliance_export(fmt, output, as_json):
|
|
512
741
|
"""Generate EU AI Act compliance evidence package."""
|
|
513
|
-
result = api_post("/v1/compliance/export")
|
|
514
|
-
|
|
515
742
|
if as_json:
|
|
516
|
-
|
|
743
|
+
fmt = "json"
|
|
744
|
+
result = api_post("/v1/compliance/export", json={"format": fmt})
|
|
745
|
+
|
|
746
|
+
# SARIF (or explicit JSON to a file / stdout): emit the document verbatim.
|
|
747
|
+
if fmt == "sarif" or output or as_json:
|
|
748
|
+
text = json_mod.dumps(result, indent=2)
|
|
749
|
+
if output:
|
|
750
|
+
with open(output, "w", encoding="utf-8") as fh:
|
|
751
|
+
fh.write(text)
|
|
752
|
+
console.print(f"[green]Wrote {fmt.upper()} package to {output}[/]")
|
|
753
|
+
else:
|
|
754
|
+
click.echo(text)
|
|
517
755
|
return
|
|
518
756
|
|
|
519
757
|
recs = result.get("recommendations", [])
|
|
520
|
-
articles = result.get("
|
|
758
|
+
articles = result.get("articles", {})
|
|
521
759
|
|
|
522
760
|
click.echo("EU AI Act Compliance Export")
|
|
523
761
|
click.echo("=" * 40)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strathon-cli
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: CLI for Strathon, the open-source AI agent firewall.
|
|
5
|
+
Author-email: Strathon <hello@getstrathon.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://getstrathon.com
|
|
8
|
+
Project-URL: Repository, https://github.com/strathon/strathon
|
|
9
|
+
Project-URL: Documentation, https://getstrathon.com/docs
|
|
10
|
+
Project-URL: Issues, https://github.com/strathon/strathon/issues
|
|
11
|
+
Project-URL: Discord, https://discord.gg/Ta9XRmh4H
|
|
12
|
+
Keywords: ai,agents,firewall,security,cli,llm,compliance
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
Requires-Dist: click>=8.1
|
|
28
|
+
Requires-Dist: httpx>=0.27
|
|
29
|
+
Requires-Dist: rich>=13.0
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
|
|
32
|
+
# Strathon CLI
|
|
33
|
+
|
|
34
|
+
Command-line interface for [Strathon](https://github.com/strathon/strathon), the open-source AI agent firewall.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install strathon-cli
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Set your API key
|
|
46
|
+
export STRATHON_API_KEY=stra_...
|
|
47
|
+
|
|
48
|
+
# Policy management
|
|
49
|
+
strathon policies list
|
|
50
|
+
strathon policies create --name "block-email" \
|
|
51
|
+
--expr 'attrs["gen_ai.tool.name"] == "send_email"' --action block
|
|
52
|
+
strathon policies create --template block-prompt-injection
|
|
53
|
+
strathon policies create --from-english "block all shell commands"
|
|
54
|
+
strathon policies import policies.yaml
|
|
55
|
+
strathon policies test --name my-policy --last 100
|
|
56
|
+
|
|
57
|
+
# Traces and spans
|
|
58
|
+
strathon traces list --last 1h
|
|
59
|
+
strathon traces tree <trace-id>
|
|
60
|
+
strathon spans search --tool send_email --limit 50
|
|
61
|
+
|
|
62
|
+
# Operations
|
|
63
|
+
strathon halts create --scope project --reason "Emergency"
|
|
64
|
+
strathon budgets list
|
|
65
|
+
strathon approvals list --pending
|
|
66
|
+
|
|
67
|
+
# Compliance and audit
|
|
68
|
+
strathon compliance export --format sarif
|
|
69
|
+
strathon audit list --last 24h
|
|
70
|
+
|
|
71
|
+
# Administration
|
|
72
|
+
strathon admin list-users
|
|
73
|
+
strathon admin reset-password --email user@company.com
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Every command supports `--json` for scripting and CI pipelines.
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
| Variable | Required | Default |
|
|
81
|
+
|----------|----------|---------|
|
|
82
|
+
| `STRATHON_API_KEY` | Yes | |
|
|
83
|
+
| `STRATHON_ENDPOINT` | No | `http://localhost:4318` |
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
- [CLI reference](https://getstrathon.com/docs/cli)
|
|
88
|
+
- [Policy engine](https://getstrathon.com/docs/intervention)
|
|
89
|
+
- [CEL reference](https://getstrathon.com/docs/cel-reference)
|
|
90
|
+
- [GitHub](https://github.com/strathon/strathon)
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT. See [LICENSE](https://github.com/strathon/strathon/blob/main/LICENSE).
|
strathon_cli-1.0.1/PKG-INFO
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: strathon-cli
|
|
3
|
-
Version: 1.0.1
|
|
4
|
-
Summary: Command-line interface for Strathon — the open-source AI agent firewall
|
|
5
|
-
Author-email: Strathon <security@getstrathon.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://getstrathon.com
|
|
8
|
-
Project-URL: Repository, https://github.com/strathon/strathon
|
|
9
|
-
Project-URL: Documentation, https://getstrathon.com/docs
|
|
10
|
-
Keywords: ai,agents,firewall,security,cli
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Topic :: Security
|
|
17
|
-
Requires-Python: >=3.10
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
Requires-Dist: click>=8.1
|
|
20
|
-
Requires-Dist: httpx>=0.27
|
|
21
|
-
Requires-Dist: rich>=13.0
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: strathon-cli
|
|
3
|
-
Version: 1.0.1
|
|
4
|
-
Summary: Command-line interface for Strathon — the open-source AI agent firewall
|
|
5
|
-
Author-email: Strathon <security@getstrathon.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://getstrathon.com
|
|
8
|
-
Project-URL: Repository, https://github.com/strathon/strathon
|
|
9
|
-
Project-URL: Documentation, https://getstrathon.com/docs
|
|
10
|
-
Keywords: ai,agents,firewall,security,cli
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Topic :: Security
|
|
17
|
-
Requires-Python: >=3.10
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
Requires-Dist: click>=8.1
|
|
20
|
-
Requires-Dist: httpx>=0.27
|
|
21
|
-
Requires-Dist: rich>=13.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|