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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywrit
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Writ — the gate before the write. Allow/deny gate with a tamper-evident audit log
5
5
  Project-URL: Homepage, https://withwrit.com
6
6
  Project-URL: Documentation, https://docs.withwrit.com
@@ -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.4"
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"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywrit
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Writ — the gate before the write. Allow/deny gate with a tamper-evident audit log
5
5
  Project-URL: Homepage, https://withwrit.com
6
6
  Project-URL: Documentation, https://docs.withwrit.com
@@ -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 request("POST", "/v1/keys", payload)
11642
+ return _request_and_print("POST", "/v1/keys", payload)
11647
11643
  if args.cmd == "invite":
11648
- return request("POST", "/v1/tenant/invites", key=args.key)
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 request(
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 request("GET", "/v1/me", key=args.key)
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 request("PUT", url, {verb: mode for verb, mode in args.set}, key=args.key)
11676
- return request("GET", url, key=args.key)
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 request("GET", "/v1/receipts", key=args.key)
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 request("GET", f"/v1/receipts/verify?limit={args.limit}", key=args.key)
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 request("POST", "/v1/tokens/verify", payload, key=args.key)
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
- code, text = request("GET", "/v1/manifest-key", key=args.key)
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 request("GET", "/v1/revoked", token=token)
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 request("POST", "/v1/revoke", payload, token=token)
11713
- return request("POST", "/v1/reinstate", payload, token=token)
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 request("POST", "/v1/sandbox", body)
11725
- return request("POST", "/v1/check", body, key=args.key)
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