pywrit 0.1.4__tar.gz → 0.1.5__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.
- {pywrit-0.1.4/pywrit.egg-info → pywrit-0.1.5}/PKG-INFO +1 -1
- {pywrit-0.1.4 → pywrit-0.1.5}/pyproject.toml +1 -1
- {pywrit-0.1.4 → pywrit-0.1.5/pywrit.egg-info}/PKG-INFO +1 -1
- {pywrit-0.1.4 → pywrit-0.1.5}/writ_cli/cli.py +29 -38
- {pywrit-0.1.4 → pywrit-0.1.5}/LICENSE +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/README.md +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/pywrit.egg-info/SOURCES.txt +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/pywrit.egg-info/dependency_links.txt +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/pywrit.egg-info/entry_points.txt +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/pywrit.egg-info/requires.txt +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/pywrit.egg-info/top_level.txt +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/setup.cfg +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/tests/test_scan_tsjs.py +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/writ_cli/__init__.py +0 -0
- {pywrit-0.1.4 → pywrit-0.1.5}/writ_cli/risk.py +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
# Distribution name: pywrit (approved by Chad 2026-09-27).
|
|
7
7
|
# The bare name "writ" is taken on PyPI.
|
|
8
8
|
name = "pywrit"
|
|
9
|
-
version = "0.1.
|
|
9
|
+
version = "0.1.5"
|
|
10
10
|
description = "Writ — the gate before the write. Allow/deny gate with a tamper-evident audit log"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.9"
|
|
@@ -21,24 +21,6 @@ BASE = os.environ.get(
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def request(method, path, payload=None, key=None, token=None):
|
|
25
|
-
headers = {"content-type": "application/json"}
|
|
26
|
-
if key:
|
|
27
|
-
headers["authorization"] = "Bearer " + key
|
|
28
|
-
elif token:
|
|
29
|
-
headers["authorization"] = token
|
|
30
|
-
data = None if payload is None else json.dumps(payload).encode()
|
|
31
|
-
req = urllib.request.Request(BASE + path, data=data, headers=headers, method=method)
|
|
32
|
-
try:
|
|
33
|
-
with urllib.request.urlopen(req, timeout=20) as resp:
|
|
34
|
-
body = resp.read().decode()
|
|
35
|
-
print(body)
|
|
36
|
-
return 0
|
|
37
|
-
except urllib.error.HTTPError as exc:
|
|
38
|
-
print(exc.read().decode(), file=sys.stderr)
|
|
39
|
-
return exc.code
|
|
40
|
-
|
|
41
|
-
|
|
42
24
|
def stream_cmd(args):
|
|
43
25
|
"""Tail the tenant audit log via the SSE stream, reconnecting on each
|
|
44
26
|
long-poll expiry. Prints one line per receipt."""
|
|
@@ -11227,6 +11209,20 @@ def request(method, path, payload=None, key=None, token=None):
|
|
|
11227
11209
|
return exc.code, exc.read().decode()
|
|
11228
11210
|
|
|
11229
11211
|
|
|
11212
|
+
def _request_and_print(method, path, payload=None, key=None, token=None):
|
|
11213
|
+
"""Call request(), print the response, and return a clean exit code.
|
|
11214
|
+
|
|
11215
|
+
Success bodies go to stdout; error bodies go to stderr. This is the
|
|
11216
|
+
helper all simple dispatchers should use so the CLI exits 0 on success.
|
|
11217
|
+
"""
|
|
11218
|
+
code, text = request(method, path, payload, key=key, token=token)
|
|
11219
|
+
if code == 0:
|
|
11220
|
+
print(text)
|
|
11221
|
+
return 0
|
|
11222
|
+
print(text, file=sys.stderr)
|
|
11223
|
+
return code
|
|
11224
|
+
|
|
11225
|
+
|
|
11230
11226
|
# ---------------------------------------------------------------------------
|
|
11231
11227
|
# Agent Action Report
|
|
11232
11228
|
# ---------------------------------------------------------------------------
|
|
@@ -11643,14 +11639,14 @@ def main():
|
|
|
11643
11639
|
payload["tenantName"] = args.tenant_name.strip()
|
|
11644
11640
|
if args.invite_code.strip():
|
|
11645
11641
|
payload["inviteCode"] = args.invite_code.strip()
|
|
11646
|
-
return
|
|
11642
|
+
return _request_and_print("POST", "/v1/keys", payload)
|
|
11647
11643
|
if args.cmd == "invite":
|
|
11648
|
-
return
|
|
11644
|
+
return _request_and_print("POST", "/v1/tenant/invites", key=args.key)
|
|
11649
11645
|
if args.cmd == "grant":
|
|
11650
11646
|
if not args.sponsor_token:
|
|
11651
11647
|
print("error: sponsor token required (--sponsor-token or WRIT_SPONSOR_TOKEN)", file=sys.stderr)
|
|
11652
11648
|
return 2
|
|
11653
|
-
return
|
|
11649
|
+
return _request_and_print(
|
|
11654
11650
|
"POST",
|
|
11655
11651
|
"/v1/grants",
|
|
11656
11652
|
{
|
|
@@ -11664,7 +11660,7 @@ def main():
|
|
|
11664
11660
|
token="Bearer " + args.sponsor_token,
|
|
11665
11661
|
)
|
|
11666
11662
|
if args.cmd == "me":
|
|
11667
|
-
return
|
|
11663
|
+
return _request_and_print("GET", "/v1/me", key=args.key)
|
|
11668
11664
|
if args.cmd == "policy":
|
|
11669
11665
|
url = "/v1/policy" if not args.env else f"/v1/policies/environments/{args.env}"
|
|
11670
11666
|
if args.set:
|
|
@@ -11672,45 +11668,40 @@ def main():
|
|
|
11672
11668
|
if mode not in MODES:
|
|
11673
11669
|
print(f"error: mode must be one of {', '.join(MODES)}", file=sys.stderr)
|
|
11674
11670
|
return 2
|
|
11675
|
-
return
|
|
11676
|
-
return
|
|
11671
|
+
return _request_and_print("PUT", url, {verb: mode for verb, mode in args.set}, key=args.key)
|
|
11672
|
+
return _request_and_print("GET", url, key=args.key)
|
|
11677
11673
|
if args.cmd == "receipts":
|
|
11678
|
-
return
|
|
11674
|
+
return _request_and_print("GET", "/v1/receipts", key=args.key)
|
|
11679
11675
|
if args.cmd == "stream":
|
|
11680
11676
|
return stream_cmd(args)
|
|
11681
11677
|
if args.cmd == "verify-chain":
|
|
11682
|
-
return
|
|
11678
|
+
return _request_and_print("GET", f"/v1/receipts/verify?limit={args.limit}", key=args.key)
|
|
11683
11679
|
if args.cmd == "token-verify":
|
|
11684
11680
|
payload = {"token": args.token}
|
|
11685
11681
|
for field in ("verb", "target", "purpose"):
|
|
11686
11682
|
value = getattr(args, field)
|
|
11687
11683
|
if value is not None:
|
|
11688
11684
|
payload[field] = value
|
|
11689
|
-
return
|
|
11685
|
+
return _request_and_print("POST", "/v1/tokens/verify", payload, key=args.key)
|
|
11690
11686
|
if args.cmd == "scan":
|
|
11691
11687
|
return scan_cmd(args)
|
|
11692
11688
|
if args.cmd == "report":
|
|
11693
11689
|
return report_cmd(args)
|
|
11694
11690
|
if args.cmd == "manifest-key":
|
|
11695
|
-
|
|
11696
|
-
if code == 0:
|
|
11697
|
-
print(text)
|
|
11698
|
-
return 0
|
|
11699
|
-
print(text, file=sys.stderr)
|
|
11700
|
-
return code
|
|
11691
|
+
return _request_and_print("GET", "/v1/manifest-key", key=args.key)
|
|
11701
11692
|
if args.cmd in ("revoke", "reinstate", "revoked"):
|
|
11702
11693
|
if not args.sponsor_token:
|
|
11703
11694
|
print("error: sponsor token required (--sponsor-token or WRIT_SPONSOR_TOKEN)", file=sys.stderr)
|
|
11704
11695
|
return 2
|
|
11705
11696
|
token = "Bearer " + args.sponsor_token
|
|
11706
11697
|
if args.cmd == "revoked":
|
|
11707
|
-
return
|
|
11698
|
+
return _request_and_print("GET", "/v1/revoked", token=token)
|
|
11708
11699
|
payload = {"sponsorId": args.sponsor, "agentId": args.agent}
|
|
11709
11700
|
if args.cmd == "revoke":
|
|
11710
11701
|
if args.reason:
|
|
11711
11702
|
payload["reason"] = args.reason
|
|
11712
|
-
return
|
|
11713
|
-
return
|
|
11703
|
+
return _request_and_print("POST", "/v1/revoke", payload, token=token)
|
|
11704
|
+
return _request_and_print("POST", "/v1/reinstate", payload, token=token)
|
|
11714
11705
|
body = {
|
|
11715
11706
|
"sponsorId": args.sponsor,
|
|
11716
11707
|
"agentId": args.agent,
|
|
@@ -11721,8 +11712,8 @@ def main():
|
|
|
11721
11712
|
if args.cmd == "check" and args.environment:
|
|
11722
11713
|
body["environment"] = args.environment
|
|
11723
11714
|
if args.cmd == "sandbox":
|
|
11724
|
-
return
|
|
11725
|
-
return
|
|
11715
|
+
return _request_and_print("POST", "/v1/sandbox", body)
|
|
11716
|
+
return _request_and_print("POST", "/v1/check", body, key=args.key)
|
|
11726
11717
|
|
|
11727
11718
|
|
|
11728
11719
|
if __name__ == "__main__":
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|