veritool 1.0.0__py3-none-any.whl
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.
- bridge/__init__.py +41 -0
- bridge/policy_spec.py +43 -0
- bridge/z3_encoder.py +205 -0
- cli/__init__.py +184 -0
- cli/auto_generator.py +561 -0
- cli/round_trip.py +106 -0
- dashboard/__init__.py +0 -0
- dashboard/app.py +77 -0
- integrations/__init__.py +5 -0
- integrations/autogen.py +65 -0
- integrations/crewai.py +47 -0
- integrations/langchain.py +32 -0
- llm/__init__.py +0 -0
- llm/groq_client.py +117 -0
- llm/prompts.py +40 -0
- policy_store/__init__.py +3 -0
- policy_store/audit.py +77 -0
- policy_store/store.py +120 -0
- verifier/__init__.py +0 -0
- verifier/coordination_policy.py +174 -0
- verifier/deletion_policy.py +38 -0
- verifier/schema.py +121 -0
- verifier/tahoe_policy.py +40 -0
- verifier/verifier.py +50 -0
- veritool-1.0.0.dist-info/METADATA +144 -0
- veritool-1.0.0.dist-info/RECORD +30 -0
- veritool-1.0.0.dist-info/WHEEL +5 -0
- veritool-1.0.0.dist-info/entry_points.txt +2 -0
- veritool-1.0.0.dist-info/licenses/LICENSE +201 -0
- veritool-1.0.0.dist-info/top_level.txt +7 -0
bridge/__init__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from bridge.policy_spec import PolicySpec, NatType, StringType, FinsetType, FunctionDef, BridgeError
|
|
2
|
+
from bridge.z3_encoder import check_policy, compile_policy
|
|
3
|
+
|
|
4
|
+
TAHOE_SPEC = PolicySpec(
|
|
5
|
+
name="tahoe",
|
|
6
|
+
_policy_type="tahoe",
|
|
7
|
+
params={"model": StringType, "price": NatType},
|
|
8
|
+
functions=[
|
|
9
|
+
FunctionDef("floor_price", StringType, NatType,
|
|
10
|
+
mapping={"Tahoe": 45000, "Malibu": 25000}, default=0),
|
|
11
|
+
],
|
|
12
|
+
violation_expr="price < floor_price(model)",
|
|
13
|
+
description="Tahoe/Malibu minimum price policy",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
DELETION_SPEC = PolicySpec(
|
|
17
|
+
name="deletion",
|
|
18
|
+
_policy_type="deletion",
|
|
19
|
+
params={"target": StringType},
|
|
20
|
+
violation_expr="Not(in_scope(target))",
|
|
21
|
+
_allowed_scope=["/project/temp", "/project/output"],
|
|
22
|
+
description="File deletion frame policy requiring target in allowed scope",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def bridge_check(policy_name: str, params: dict | None = None, timeout_ms: int = 5000) -> dict:
|
|
27
|
+
spec_map = {
|
|
28
|
+
"tahoe": TAHOE_SPEC,
|
|
29
|
+
"deletion": DELETION_SPEC,
|
|
30
|
+
}
|
|
31
|
+
spec = spec_map.get(policy_name)
|
|
32
|
+
if spec is None:
|
|
33
|
+
return {"status": "error", "reason": f"Unknown policy: {policy_name}"}
|
|
34
|
+
return check_policy(spec, params=params, timeout_ms=timeout_ms)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"PolicySpec", "NatType", "StringType", "FinsetType", "FunctionDef", "BridgeError",
|
|
39
|
+
"check_policy", "compile_policy",
|
|
40
|
+
"TAHOE_SPEC", "DELETION_SPEC", "bridge_check",
|
|
41
|
+
]
|
bridge/policy_spec.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BridgeError(Exception):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class TypeSpec:
|
|
11
|
+
name: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
NatType = TypeSpec("Nat")
|
|
15
|
+
StringType = TypeSpec("String")
|
|
16
|
+
BoolType = TypeSpec("Bool")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class FinsetType:
|
|
21
|
+
elem_type: TypeSpec
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class FunctionDef:
|
|
26
|
+
name: str
|
|
27
|
+
arg_type: TypeSpec
|
|
28
|
+
return_type: TypeSpec
|
|
29
|
+
mapping: dict[str, Any] = field(default_factory=dict)
|
|
30
|
+
default: Any = 0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class PolicySpec:
|
|
35
|
+
name: str
|
|
36
|
+
params: dict[str, TypeSpec] = field(default_factory=dict)
|
|
37
|
+
functions: list[FunctionDef] = field(default_factory=list)
|
|
38
|
+
violation_expr: str = ""
|
|
39
|
+
description: str = ""
|
|
40
|
+
_tool_name: str = ""
|
|
41
|
+
_allowed_scope: list[str] = field(default_factory=list)
|
|
42
|
+
_param_name: str = "value"
|
|
43
|
+
_policy_type: str = "generic"
|
bridge/z3_encoder.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from z3 import (
|
|
4
|
+
Solver, Int, Bool, String, StringVal,
|
|
5
|
+
Function, IntSort, BoolSort, StringSort,
|
|
6
|
+
ForAll, Const, Implies, And, Or,
|
|
7
|
+
sat, unknown,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from bridge.policy_spec import (
|
|
11
|
+
PolicySpec, FunctionDef, FinsetType,
|
|
12
|
+
NatType, StringType, BoolType,
|
|
13
|
+
BridgeError,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
_SORT_MAP = {
|
|
18
|
+
NatType: IntSort(),
|
|
19
|
+
StringType: StringSort(),
|
|
20
|
+
BoolType: BoolSort(),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _z3_sort(t):
|
|
25
|
+
if isinstance(t, FinsetType):
|
|
26
|
+
return _SORT_MAP.get(t.elem_type)
|
|
27
|
+
return _SORT_MAP.get(t)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def compile_policy(spec: PolicySpec, timeout_ms: int = 5000) -> Solver:
|
|
31
|
+
s = Solver()
|
|
32
|
+
s.set("timeout", timeout_ms)
|
|
33
|
+
|
|
34
|
+
ptype = spec._policy_type if spec._policy_type != "generic" else spec.name
|
|
35
|
+
|
|
36
|
+
if ptype in ("price_floor", "tahoe"):
|
|
37
|
+
_add_floor_price_defs(s, spec)
|
|
38
|
+
elif ptype in ("file_access", "deletion"):
|
|
39
|
+
allowed = getattr(spec, "_allowed_scope", ["/project/temp", "/project/output"])
|
|
40
|
+
_add_in_scope_defs(s, allowed)
|
|
41
|
+
elif ptype == "sql_safety":
|
|
42
|
+
_add_allowed_patterns(s, spec)
|
|
43
|
+
elif ptype == "rate_limit":
|
|
44
|
+
_add_rate_limit_defs(s, spec)
|
|
45
|
+
elif ptype == "role_hours":
|
|
46
|
+
_add_role_hours_defs(s, spec)
|
|
47
|
+
elif ptype == "api_access":
|
|
48
|
+
_add_api_access_defs(s, spec)
|
|
49
|
+
else:
|
|
50
|
+
raise BridgeError(f"Unknown policy type: {ptype}")
|
|
51
|
+
return s
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _add_floor_price_defs(solver, spec: PolicySpec):
|
|
55
|
+
if not spec.functions:
|
|
56
|
+
return
|
|
57
|
+
fn = spec.functions[0]
|
|
58
|
+
sort = _z3_sort(fn.arg_type)
|
|
59
|
+
ret_sort = _z3_sort(fn.return_type)
|
|
60
|
+
if sort is None or ret_sort is None:
|
|
61
|
+
raise BridgeError(f"Unsupported type in function {fn.name}")
|
|
62
|
+
|
|
63
|
+
floor_z3 = Function(fn.name, sort, ret_sort)
|
|
64
|
+
for k, v in fn.mapping.items():
|
|
65
|
+
solver.add(floor_z3(StringVal(k)) == v)
|
|
66
|
+
solver.add(floor_z3(StringVal("")) == fn.default)
|
|
67
|
+
return floor_z3
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _add_in_scope_defs(solver, allowed_paths: list[str]):
|
|
71
|
+
in_scope = Function("in_scope", StringSort(), BoolSort())
|
|
72
|
+
for p in allowed_paths:
|
|
73
|
+
solver.add(in_scope(StringVal(p)) == True)
|
|
74
|
+
return in_scope
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _add_allowed_patterns(solver, spec: PolicySpec):
|
|
78
|
+
if not spec.functions:
|
|
79
|
+
return
|
|
80
|
+
fn_def = spec.functions[0]
|
|
81
|
+
allowed_fn = Function(fn_def.name, StringSort(), BoolSort())
|
|
82
|
+
for q, val in fn_def.mapping.items():
|
|
83
|
+
solver.add(allowed_fn(StringVal(q)) == (val if isinstance(val, bool) else val == True))
|
|
84
|
+
_add_default_false(solver, allowed_fn, fn_def)
|
|
85
|
+
return allowed_fn
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _add_rate_limit_defs(solver, spec: PolicySpec):
|
|
89
|
+
if not spec.functions:
|
|
90
|
+
return
|
|
91
|
+
fn = spec.functions[0]
|
|
92
|
+
max_fn = Function(fn.name, StringSort(), IntSort())
|
|
93
|
+
for k, v in fn.mapping.items():
|
|
94
|
+
solver.add(max_fn(StringVal(k)) == v)
|
|
95
|
+
solver.add(max_fn(StringVal("")) == fn.default)
|
|
96
|
+
return max_fn
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _add_default_false(solver, z3_fn, fn_def):
|
|
100
|
+
x = Const(f"_{z3_fn.name()}_x", StringSort())
|
|
101
|
+
conditions = [x != StringVal(k) for k in fn_def.mapping]
|
|
102
|
+
if conditions:
|
|
103
|
+
solver.add(ForAll([x], Implies(And(*conditions), z3_fn(x) == False)))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _add_role_hours_defs(solver, spec: PolicySpec):
|
|
107
|
+
if not spec.functions:
|
|
108
|
+
return
|
|
109
|
+
fn_def = spec.functions[0]
|
|
110
|
+
blocked_fn = Function(fn_def.name, StringSort(), BoolSort())
|
|
111
|
+
for action, val in fn_def.mapping.items():
|
|
112
|
+
solver.add(blocked_fn(StringVal(action)) == (val if isinstance(val, bool) else True))
|
|
113
|
+
_add_default_false(solver, blocked_fn, fn_def)
|
|
114
|
+
return blocked_fn
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _add_api_access_defs(solver, spec: PolicySpec):
|
|
118
|
+
if len(spec.functions) >= 1:
|
|
119
|
+
fn_def = spec.functions[0]
|
|
120
|
+
ep_fn = Function(fn_def.name, StringSort(), BoolSort())
|
|
121
|
+
for ep, val in fn_def.mapping.items():
|
|
122
|
+
solver.add(ep_fn(StringVal(ep)) == (val if isinstance(val, bool) else True))
|
|
123
|
+
_add_default_false(solver, ep_fn, fn_def)
|
|
124
|
+
if len(spec.functions) >= 2:
|
|
125
|
+
fn_def = spec.functions[1]
|
|
126
|
+
method_fn = Function(fn_def.name, StringSort(), BoolSort())
|
|
127
|
+
for m, val in fn_def.mapping.items():
|
|
128
|
+
solver.add(method_fn(StringVal(m)) == (val if isinstance(val, bool) else True))
|
|
129
|
+
_add_default_false(solver, method_fn, fn_def)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def check_policy(spec: PolicySpec, params: dict | None = None, timeout_ms: int = 5000) -> dict:
|
|
133
|
+
solver = compile_policy(spec, timeout_ms)
|
|
134
|
+
ptype = spec._policy_type if spec._policy_type != "generic" else spec.name
|
|
135
|
+
|
|
136
|
+
if params:
|
|
137
|
+
if ptype in ("price_floor", "tahoe"):
|
|
138
|
+
model_val = params.get("model", "")
|
|
139
|
+
price_val = params.get("price", 0)
|
|
140
|
+
solver.add(String("model") == StringVal(model_val))
|
|
141
|
+
solver.add(Int("price") == price_val)
|
|
142
|
+
floor_z3 = Function("floor_price", StringSort(), IntSort())
|
|
143
|
+
solver.add(Int("price") < floor_z3(String("model")))
|
|
144
|
+
|
|
145
|
+
elif ptype in ("file_access", "deletion"):
|
|
146
|
+
target_val = params.get("target", "")
|
|
147
|
+
solver.add(String("target") == StringVal(target_val))
|
|
148
|
+
in_scope = Function("in_scope", StringSort(), BoolSort())
|
|
149
|
+
solver.add(in_scope(String("target")) == False)
|
|
150
|
+
|
|
151
|
+
elif ptype == "sql_safety":
|
|
152
|
+
query_val = params.get("query", "")
|
|
153
|
+
solver.add(String("query") == StringVal(query_val))
|
|
154
|
+
allowed_fn = Function("allowed_query_pattern", StringSort(), BoolSort())
|
|
155
|
+
solver.add(allowed_fn(String("query")) == False)
|
|
156
|
+
|
|
157
|
+
elif ptype == "rate_limit":
|
|
158
|
+
api_key_val = params.get("api_key", "")
|
|
159
|
+
current_count_val = params.get("current_count", 0)
|
|
160
|
+
solver.add(String("api_key") == StringVal(api_key_val))
|
|
161
|
+
solver.add(Int("current_count") == current_count_val)
|
|
162
|
+
max_fn = Function("max_per_minute", StringSort(), IntSort())
|
|
163
|
+
solver.add(Int("current_count") >= max_fn(String("api_key")))
|
|
164
|
+
|
|
165
|
+
elif ptype == "role_hours":
|
|
166
|
+
role_val = params.get("role", "")
|
|
167
|
+
hour_val = params.get("hour", 0)
|
|
168
|
+
action_val = params.get("action", "")
|
|
169
|
+
solver.add(String("role") == StringVal(role_val))
|
|
170
|
+
solver.add(Int("hour") == hour_val)
|
|
171
|
+
solver.add(String("action") == StringVal(action_val))
|
|
172
|
+
blocked_fn = Function("admin_blocked_actions", StringSort(), BoolSort())
|
|
173
|
+
solver.add(String("role") == StringVal("admin"))
|
|
174
|
+
solver.add(Int("hour") > 22)
|
|
175
|
+
solver.add(blocked_fn(String("action")) == True)
|
|
176
|
+
|
|
177
|
+
elif ptype == "api_access":
|
|
178
|
+
endpoint_val = params.get("endpoint", "")
|
|
179
|
+
method_val = params.get("method", "")
|
|
180
|
+
solver.add(String("endpoint") == StringVal(endpoint_val))
|
|
181
|
+
solver.add(String("method") == StringVal(method_val))
|
|
182
|
+
ep_fn = Function("allowed_endpoint", StringSort(), BoolSort())
|
|
183
|
+
method_fn = Function("allowed_method", StringSort(), BoolSort())
|
|
184
|
+
solver.add(Or(ep_fn(String("endpoint")) == False,
|
|
185
|
+
method_fn(String("method")) == False))
|
|
186
|
+
|
|
187
|
+
result = solver.check()
|
|
188
|
+
if result == sat:
|
|
189
|
+
m = solver.model()
|
|
190
|
+
witness = {}
|
|
191
|
+
for d in m.decls():
|
|
192
|
+
val = m[d]
|
|
193
|
+
if val is not None:
|
|
194
|
+
try:
|
|
195
|
+
witness[d.name()] = val.as_long()
|
|
196
|
+
except Exception:
|
|
197
|
+
try:
|
|
198
|
+
witness[d.name()] = str(val)
|
|
199
|
+
except Exception:
|
|
200
|
+
witness[d.name()] = repr(val)
|
|
201
|
+
return {"status": "violation", "witness": witness}
|
|
202
|
+
elif result == unknown:
|
|
203
|
+
return {"status": "unknown", "reason": "Z3 timed out or incomplete"}
|
|
204
|
+
else:
|
|
205
|
+
return {"status": "permitted"}
|
cli/__init__.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from cli.auto_generator import AutoGenerator
|
|
6
|
+
from cli.round_trip import round_trip_verify
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
prog="veritool",
|
|
12
|
+
description="VeriTool — Formal verification framework for LLM tool-calling",
|
|
13
|
+
)
|
|
14
|
+
sub = parser.add_subparsers(dest="command")
|
|
15
|
+
|
|
16
|
+
p_run = sub.add_parser("run", help="Start verifier with specified policies")
|
|
17
|
+
p_run.add_argument("policies", nargs="+", help="Policy names or YAML files")
|
|
18
|
+
|
|
19
|
+
p_check = sub.add_parser("check", help="One-shot check a tool call")
|
|
20
|
+
p_check.add_argument("tool_call", help="JSON string or file path")
|
|
21
|
+
|
|
22
|
+
p_create = sub.add_parser("create", help="Generate policy from natural language")
|
|
23
|
+
p_create.add_argument("description", help="Policy description in plain English")
|
|
24
|
+
|
|
25
|
+
p_test = sub.add_parser("test", help="Run policy test suite")
|
|
26
|
+
p_test.add_argument("policy", nargs="?", help="Policy name (omit for all)")
|
|
27
|
+
|
|
28
|
+
p_status = sub.add_parser("status", help="Show runtime status and metrics")
|
|
29
|
+
p_hot = sub.add_parser("hot-reload", help="Deploy policy without restart")
|
|
30
|
+
p_hot.add_argument("policy_file", help="Path to policy YAML file")
|
|
31
|
+
p_rollback = sub.add_parser("rollback", help="Revert to previous policy version")
|
|
32
|
+
p_rollback.add_argument("version", help="Version to rollback to")
|
|
33
|
+
|
|
34
|
+
p_dashboard = sub.add_parser("dashboard", help="Launch monitoring dashboard")
|
|
35
|
+
p_dashboard.add_argument("--port", type=int, default=8501)
|
|
36
|
+
|
|
37
|
+
p_verify = sub.add_parser("verify", help="CI/CD — run all checks before deploy")
|
|
38
|
+
p_wrap = sub.add_parser("wrap", help="Auto-wrap a supported framework")
|
|
39
|
+
p_wrap.add_argument("framework", choices=["langchain", "crewai", "autogen"])
|
|
40
|
+
|
|
41
|
+
args = parser.parse_args()
|
|
42
|
+
|
|
43
|
+
if args.command == "create":
|
|
44
|
+
_cmd_create(args.description)
|
|
45
|
+
elif args.command == "check":
|
|
46
|
+
_cmd_check(args.tool_call)
|
|
47
|
+
elif args.command == "test":
|
|
48
|
+
_cmd_test(args.policy)
|
|
49
|
+
elif args.command == "run":
|
|
50
|
+
_cmd_run(args.policies)
|
|
51
|
+
elif args.command == "status":
|
|
52
|
+
_cmd_status()
|
|
53
|
+
elif args.command == "hot-reload":
|
|
54
|
+
_cmd_hot_reload(args.policy_file)
|
|
55
|
+
elif args.command == "rollback":
|
|
56
|
+
_cmd_rollback(args.version)
|
|
57
|
+
elif args.command == "dashboard":
|
|
58
|
+
_cmd_dashboard(args.port)
|
|
59
|
+
elif args.command == "verify":
|
|
60
|
+
_cmd_verify()
|
|
61
|
+
elif args.command == "wrap":
|
|
62
|
+
_cmd_wrap(args.framework)
|
|
63
|
+
else:
|
|
64
|
+
parser.print_help()
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _cmd_create(description: str):
|
|
69
|
+
gen = AutoGenerator()
|
|
70
|
+
result = gen.generate(description)
|
|
71
|
+
if result["status"] == "ok":
|
|
72
|
+
print(f" Policy '{result['policy_name']}' created")
|
|
73
|
+
for artifact in result["artifacts"]:
|
|
74
|
+
print(f" ✓ Generated {artifact}")
|
|
75
|
+
rtv = round_trip_verify(result["policy_name"], result["spec"])
|
|
76
|
+
if rtv["passed"]:
|
|
77
|
+
print(f" ✓ Round-trip verification: PASSED")
|
|
78
|
+
if rtv.get("details"):
|
|
79
|
+
for d in rtv["details"]:
|
|
80
|
+
print(f" → {d}")
|
|
81
|
+
else:
|
|
82
|
+
print(f" ✗ Round-trip verification: FAILED — {rtv.get('error', '')}")
|
|
83
|
+
else:
|
|
84
|
+
print(f" ✗ Error: {result.get('error', 'Generation failed')}")
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _cmd_check(tool_call: str):
|
|
89
|
+
try:
|
|
90
|
+
raw = json.loads(tool_call)
|
|
91
|
+
except json.JSONDecodeError:
|
|
92
|
+
try:
|
|
93
|
+
with open(tool_call) as f:
|
|
94
|
+
raw = json.load(f)
|
|
95
|
+
except (FileNotFoundError, json.JSONDecodeError):
|
|
96
|
+
print(" ✗ Error: Provide valid JSON string or path to JSON file")
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
from orchestrator import evaluate_tool_call
|
|
100
|
+
result = evaluate_tool_call(json.dumps(raw))
|
|
101
|
+
print(json.dumps(result, indent=2))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _cmd_test(policy: str | None):
|
|
105
|
+
import subprocess, sys as _sys
|
|
106
|
+
cmd = [_sys.executable, "-m", "pytest", "tests/", "-v"]
|
|
107
|
+
if policy:
|
|
108
|
+
test_file = f"tests/test_{policy}.py"
|
|
109
|
+
cmd = [_sys.executable, "-m", "pytest", test_file, "-v"]
|
|
110
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
111
|
+
print(result.stdout)
|
|
112
|
+
if result.returncode != 0:
|
|
113
|
+
print(result.stderr)
|
|
114
|
+
_sys.exit(result.returncode)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _cmd_run(policies: list[str]):
|
|
118
|
+
from verifier.verifier import Verifier
|
|
119
|
+
v = Verifier()
|
|
120
|
+
print(f" Verifier running with {len(policies)} policy(ies): {', '.join(policies)}")
|
|
121
|
+
for p in policies:
|
|
122
|
+
if p not in v._policies and p != "all":
|
|
123
|
+
print(f" ⚠ Policy '{p}' not registered")
|
|
124
|
+
print(f" Listening for tool calls...")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _cmd_status():
|
|
128
|
+
from pathlib import Path
|
|
129
|
+
from policy_store.store import PolicyStore
|
|
130
|
+
store = PolicyStore(Path("policy_store"))
|
|
131
|
+
store.load()
|
|
132
|
+
print(f" Policy Store: {'healthy' if store.policies else 'empty'}")
|
|
133
|
+
print(f" Active policies: {len(store.policies)}")
|
|
134
|
+
for name in store.policies:
|
|
135
|
+
print(f" - {name}")
|
|
136
|
+
from config import POLICY_ROUTES
|
|
137
|
+
print(f" Routes: {len(POLICY_ROUTES)}")
|
|
138
|
+
for tool, policy in POLICY_ROUTES.items():
|
|
139
|
+
print(f" {tool} → {policy}")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _cmd_hot_reload(policy_file: str):
|
|
143
|
+
from pathlib import Path
|
|
144
|
+
from policy_store.store import PolicyStore
|
|
145
|
+
store = PolicyStore(Path("policy_store"))
|
|
146
|
+
store.hot_reload(Path(policy_file))
|
|
147
|
+
print(f" ✓ Hot-reload complete: {policy_file}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _cmd_rollback(version: str):
|
|
151
|
+
from pathlib import Path
|
|
152
|
+
from policy_store.store import PolicyStore
|
|
153
|
+
store = PolicyStore(Path("policy_store"))
|
|
154
|
+
store.rollback(version)
|
|
155
|
+
print(f" ✓ Rolled back to {version}")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _cmd_dashboard(port: int):
|
|
159
|
+
print(f" Launching dashboard on port {port}...")
|
|
160
|
+
print(f" Run: streamlit run dashboard/app.py --server.port={port}")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _cmd_verify():
|
|
164
|
+
import subprocess, sys as _sys
|
|
165
|
+
print(" Running verification checks...")
|
|
166
|
+
r1 = subprocess.run([_sys.executable, "-m", "pytest", "tests/", "-x", "-q"], capture_output=True, text=True)
|
|
167
|
+
print(r1.stdout)
|
|
168
|
+
if r1.returncode != 0:
|
|
169
|
+
print(" ✗ Tests failed")
|
|
170
|
+
print(r1.stderr)
|
|
171
|
+
_sys.exit(1)
|
|
172
|
+
r2 = subprocess.run(["lean", "Lean/Policy.lean"], capture_output=True, text=True)
|
|
173
|
+
if r2.returncode == 0:
|
|
174
|
+
print(" ✓ Lean theorem compiles")
|
|
175
|
+
else:
|
|
176
|
+
print(" ✗ Lean theorem failed")
|
|
177
|
+
print(r2.stderr)
|
|
178
|
+
_sys.exit(1)
|
|
179
|
+
print(" ✓ All checks passed")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _cmd_wrap(framework: str):
|
|
183
|
+
print(f" Wrapping {framework}...")
|
|
184
|
+
print(f" Import veritool.integrations.{framework} and apply middleware")
|