gabriel / muse public
envelope.py python
86 lines 3.0 KB
Raw
sha256:057be43e401106b2b4d877e8028ae67d16a966562d7429707b71a5eaacd5027b docs(mwp-master): tick all ACs green; mark MWP-7 complete w… Sonnet 4.6 20 days ago
1 """Standard JSON envelope for all muse CLI commands.
2
3 Every ``muse`` command that emits JSON includes these six fields so agents
4 can parse results uniformly without knowing anything about the specific command:
5
6 ``muse_version`` — the installed muse package version (e.g. ``"0.2.0rc3"``).
7 ``schema`` — per-endpoint integer, starts at ``1``, only incremented on
8 breaking shape changes. Agents can branch on ``schema < 2``.
9 ``exit_code`` — process exit code embedded in the payload (0 = success).
10 ``duration_ms`` — wall-clock milliseconds for the operation (float).
11 ``timestamp`` — ISO-8601 UTC timestamp when the response was produced.
12 ``warnings`` — list of non-fatal warning strings that would otherwise
13 be lost when stderr is discarded in JSON mode.
14
15 Usage::
16
17 from muse.core.envelope import make_envelope
18 from muse.core.timing import start_timer
19
20 elapsed = start_timer()
21 # ... do work, collect warnings ...
22 out = {
23 **make_envelope(elapsed, exit_code=0, warnings=warns),
24 "symbols": [...], # domain-specific fields follow
25 }
26 print(json.dumps(out))
27 """
28
29 import datetime
30 from collections.abc import Callable
31 from typing import TypedDict
32
33 from muse import __version__
34 from muse.core.types import JsonValue as JsonValue # re-exported for convenience
35
36 class EnvelopeJson(TypedDict):
37 """Base TypedDict for every muse JSON response.
38
39 Inherit from this in every command-specific TypedDict so the full
40 wire shape is self-documenting and MuseHub can import it as the
41 canonical contract base::
42
43 class _MyCommandJson(EnvelopeJson):
44 my_field: str
45 count: int
46 """
47
48 muse_version: str
49 schema: int
50 exit_code: int
51 duration_ms: float
52 timestamp: str
53 warnings: list[str]
54
55 def make_envelope(
56 elapsed: Callable[[], float],
57 *,
58 exit_code: int = 0,
59 schema: int = 1,
60 warnings: list[str] | None = None,
61 ) -> EnvelopeJson:
62 """Return the six standard envelope fields for a muse JSON response.
63
64 Args:
65 elapsed: Callable returned by :func:`muse.core.timing.start_timer`.
66 Called once to capture ``duration_ms``.
67 exit_code: Process exit code to embed (default ``0``).
68 schema: Per-endpoint schema integer (default ``1``). Increment
69 only on breaking shape changes.
70 warnings: Optional list of non-fatal warning strings. Defaults to
71 an empty list.
72
73 Returns:
74 A plain ``dict`` with exactly six keys: ``muse_version``, ``schema``,
75 ``exit_code``, ``duration_ms``, ``timestamp``, ``warnings``.
76 """
77 now = datetime.datetime.now(datetime.timezone.utc)
78 ts = now.strftime("%Y-%m-%dT%H:%M:%S.") + f"{now.microsecond // 1000:03d}Z"
79 return {
80 "muse_version": __version__,
81 "schema": schema,
82 "exit_code": exit_code,
83 "duration_ms": elapsed(),
84 "timestamp": ts,
85 "warnings": warnings if warnings is not None else [],
86 }
File History 1 commit
sha256:057be43e401106b2b4d877e8028ae67d16a966562d7429707b71a5eaacd5027b docs(mwp-master): tick all ACs green; mark MWP-7 complete w… Sonnet 4.6 20 days ago