specstream-client 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.
@@ -0,0 +1,14 @@
1
+ """SPECSTREAM Python SDK generated against finder.cross-source.v1 OpenAPI."""
2
+
3
+ from specstream_client.client import DEFAULT_BASE_URL, Client, SpecstreamError
4
+ from specstream_client.families import FAMILIES, FAMILY_SET, assert_family
5
+
6
+ __all__ = [
7
+ "Client",
8
+ "DEFAULT_BASE_URL",
9
+ "FAMILIES",
10
+ "FAMILY_SET",
11
+ "SpecstreamError",
12
+ "assert_family",
13
+ ]
14
+ __version__ = "1.0.0"
@@ -0,0 +1,3 @@
1
+ from specstream_client.cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,32 @@
1
+ """Require an explicit signed-work authorization before any client HTTP call."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from typing import Any
7
+
8
+
9
+ def require_work_authorization(value: Any) -> None:
10
+ required = {"quote_id", "quote_hash", "idempotency_key", "confirm_max_cents"}
11
+ if (
12
+ not isinstance(value, dict)
13
+ or not required <= value.keys()
14
+ or set(value) - required - {"authority_id"}
15
+ ):
16
+ raise ValueError(
17
+ "Provide the exact quote identity, signature, maximum cents and request key"
18
+ )
19
+ if not isinstance(value["quote_id"], str) or not value["quote_id"]:
20
+ raise ValueError("An explicit quote identity is required")
21
+ if not isinstance(value["quote_hash"], str) or not re.fullmatch(
22
+ r"[0-9a-f]{64}", value["quote_hash"]
23
+ ):
24
+ raise ValueError("The reviewed quote signature is required")
25
+ key = value["idempotency_key"]
26
+ if not isinstance(key, str) or not 8 <= len(key) <= 128:
27
+ raise ValueError("Use a stable request key containing 8 to 128 characters")
28
+ maximum = value["confirm_max_cents"]
29
+ if type(maximum) is not int or maximum < 0:
30
+ raise ValueError("The approved maximum must be an explicit nonnegative integer in cents")
31
+ if value.get("authority_id") is not None and not isinstance(value["authority_id"], str):
32
+ raise ValueError("Authority must be an explicit existing identifier")
@@ -0,0 +1,139 @@
1
+ """SPECSTREAM console: inspect a Spec, prepare a Find, and read its Delivery."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ import sys
9
+ from collections.abc import Sequence
10
+ from typing import Any, TextIO
11
+
12
+ from specstream_client.client import DEFAULT_BASE_URL, Client, SpecstreamError
13
+ from specstream_client.families import FAMILIES
14
+ from specstream_client.lifecycle_commands import COMMANDS
15
+
16
+ PROG = "specstream"
17
+ DESC = (
18
+ "The web. To your spec. Compile the request, quote-capacity-work, then "
19
+ "authorize-capacity-work with the exact approved maximum and a stable key. "
20
+ "Read the same work, evidence and balance from every interface. "
21
+ )
22
+
23
+
24
+ def _parser() -> argparse.ArgumentParser:
25
+ parser = argparse.ArgumentParser(prog=PROG, description=DESC)
26
+ parser.add_argument(
27
+ "--base-url", default=os.environ.get("SPECSTREAM_BASE_URL", DEFAULT_BASE_URL)
28
+ )
29
+ parser.add_argument("--api-key", default=os.environ.get("SPECSTREAM_API_KEY", ""))
30
+ sub = parser.add_subparsers(dest="cmd")
31
+ compile_cmd = sub.add_parser("compile", help="Interpret a brief as an editable Spec")
32
+ compile_cmd.add_argument("q")
33
+ compile_cmd.add_argument("--locale", default="en-US")
34
+ edit = sub.add_parser(
35
+ "edit-spec", help="Validate exact edited AST JSON, including required facts"
36
+ )
37
+ edit.add_argument(
38
+ "--ast", required=True, type=argparse.FileType("r"), help="AST JSON file or -"
39
+ )
40
+ options = sub.add_parser("spec-options", help="Read supported conditions and return facts")
41
+ options.add_argument("target", choices=FAMILIES)
42
+ for command, help_text in (
43
+ ("get", "Inspect an owned Find"),
44
+ ("rows", "Read Matches with evidence"),
45
+ ("revisit", "Read one page of retained-target outcomes"),
46
+ ("export", "Request a Delivery download link"),
47
+ ):
48
+ item = sub.add_parser(command, help=help_text)
49
+ item.add_argument("--find-id", required=True)
50
+ if command == "rows":
51
+ item.add_argument("--cursor", default=None)
52
+ item.add_argument("--limit", type=int, default=100)
53
+ if command == "revisit":
54
+ item.add_argument("--after", default="")
55
+ item.add_argument("--limit", type=int, default=100)
56
+ if command == "export":
57
+ item.add_argument("--format", default="csv", choices=("csv", "xlsx", "json", "html"))
58
+ sub.add_parser("families", help="Print supported public targets")
59
+ for name, contract in COMMANDS.items():
60
+ command = sub.add_parser(name, help=contract["summary"])
61
+ for argument in contract["arguments"]:
62
+ command.add_argument("--" + argument.replace("_", "-"), required=True)
63
+ if contract["body"]:
64
+ command.add_argument(
65
+ "--request",
66
+ required=contract["body_required"],
67
+ type=argparse.FileType("r"),
68
+ help="Exact request JSON file or - for stdin",
69
+ )
70
+ for parameter, kind in contract["query"].items():
71
+ options: dict[str, Any] = {"default": None}
72
+ if kind == "boolean":
73
+ options["action"] = argparse.BooleanOptionalAction
74
+ else:
75
+ options["type"] = int if kind == "integer" else str
76
+ command.add_argument("--" + parameter.replace("_", "-"), **options)
77
+ return parser
78
+
79
+
80
+ def _dump(payload: Any, out: TextIO) -> None:
81
+ json.dump(payload, out, indent=2, sort_keys=True)
82
+ out.write("\n")
83
+
84
+
85
+ def main(
86
+ argv: Sequence[str] | None = None,
87
+ out: TextIO | None = None,
88
+ err: TextIO | None = None,
89
+ ) -> int:
90
+ stdout, stderr = out or sys.stdout, err or sys.stderr
91
+ parser = _parser()
92
+ ns = parser.parse_args(list(argv) if argv is not None else None)
93
+ if not ns.cmd:
94
+ parser.print_help(stdout)
95
+ return 0
96
+ try:
97
+ if ns.cmd == "families":
98
+ result: Any = list(FAMILIES)
99
+ else:
100
+ client = Client(api_key=ns.api_key, base_url=ns.base_url)
101
+ if ns.cmd in COMMANDS:
102
+ contract = COMMANDS[ns.cmd]
103
+ arguments = [getattr(ns, argument) for argument in contract["arguments"]]
104
+ if contract["body"]:
105
+ body = None
106
+ if ns.request is not None:
107
+ with ns.request as source:
108
+ body = json.load(source)
109
+ if not isinstance(body, dict):
110
+ raise ValueError("Request must be a JSON object")
111
+ arguments.append(body)
112
+ options = {name: getattr(ns, name) for name in contract["query"]}
113
+ result = getattr(client, contract["method"])(*arguments, **options)
114
+ elif ns.cmd == "compile":
115
+ result = client.compile(ns.q, locale=ns.locale)
116
+ elif ns.cmd == "edit-spec":
117
+ with ns.ast as source:
118
+ ast = json.load(source)
119
+ if not isinstance(ast, dict):
120
+ raise ValueError("AST must be a JSON object")
121
+ result = client.edit_spec(ast)
122
+ elif ns.cmd == "spec-options":
123
+ result = client.spec_options(ns.target)
124
+ elif ns.cmd == "get":
125
+ result = client.get_find(ns.find_id)
126
+ elif ns.cmd == "rows":
127
+ result = client.rows(ns.find_id, cursor=ns.cursor, limit=ns.limit)
128
+ elif ns.cmd == "revisit":
129
+ result = client.revisit(ns.find_id, after=ns.after, limit=ns.limit)
130
+ else:
131
+ result = client.export(ns.find_id, format=ns.format)
132
+ _dump(result, stdout)
133
+ return 0
134
+ except SpecstreamError as exc:
135
+ print(str(exc), file=stderr)
136
+ return 1
137
+ except (ValueError, OSError) as exc:
138
+ print(str(exc), file=stderr)
139
+ return 2