uro-cli 0.4.0__tar.gz → 0.5.0__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.5
2
2
  Name: uro-cli
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Uro Engine reference client — play, dry-run, and dev tools against the engine
5
5
  Project-URL: Homepage, https://github.com/cupskeee/uro
6
6
  Project-URL: Repository, https://github.com/cupskeee/uro
@@ -12,8 +12,8 @@ Requires-Python: >=3.12
12
12
  Requires-Dist: python-dotenv>=1.0
13
13
  Requires-Dist: rich>=13.9
14
14
  Requires-Dist: typer>=0.27.2
15
- Requires-Dist: uro-core[llm,postgres]==0.4.0
16
- Requires-Dist: uro-server==0.4.0
15
+ Requires-Dist: uro-core[llm,postgres]==0.5.0
16
+ Requires-Dist: uro-server==0.5.0
17
17
  Requires-Dist: websockets>=16.1
18
18
  Description-Content-Type: text/markdown
19
19
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "uro-cli"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Uro Engine reference client — play, dry-run, and dev tools against the engine"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -13,8 +13,8 @@ requires-python = ">=3.12"
13
13
  # pair a new uro-cli with an old uro-core; `just release` bumps all three together, and the
14
14
  # `{workspace = true}` sources make a drifted pin fail `uv sync` locally.
15
15
  dependencies = [
16
- "uro-core[postgres,llm]==0.4.0",
17
- "uro-server==0.4.0",
16
+ "uro-core[postgres,llm]==0.5.0",
17
+ "uro-server==0.5.0",
18
18
  "typer>=0.27.2",
19
19
  "rich>=13.9",
20
20
  "websockets>=16.1",
@@ -53,6 +53,10 @@ provider_app = typer.Typer(
53
53
  no_args_is_help=True,
54
54
  help="Model connections — the DB-backed LLM provider registry (D-47, docs/20).",
55
55
  )
56
+ outbox_app = typer.Typer(
57
+ no_args_is_help=True,
58
+ help="The transactional outbox — publish committed beats to other processes (D-57).",
59
+ )
56
60
  app.add_typer(db_app, name="db")
57
61
  app.add_typer(world_app, name="world")
58
62
  app.add_typer(branch_app, name="branch")
@@ -60,6 +64,7 @@ app.add_typer(campaign_app, name="campaign")
60
64
  app.add_typer(codex_app, name="codex")
61
65
  app.add_typer(token_app, name="token")
62
66
  app.add_typer(provider_app, name="provider")
67
+ app.add_typer(outbox_app, name="outbox")
63
68
 
64
69
  # The engine roles a connection can back (resolved by the ProviderRouter) — shared with the server.
65
70
  _ROLES = tuple(sorted(ROLES))
@@ -733,6 +738,67 @@ def _server_post(server: str, path: str, token: str, body: dict[str, Any]) -> di
733
738
  raise typer.Exit(1) from exc
734
739
 
735
740
 
741
+ @outbox_app.command("status")
742
+ def outbox_status() -> None:
743
+ """How many committed beats are waiting to be published."""
744
+
745
+ async def _run() -> None:
746
+ store = build_store()
747
+ await connect_store(store)
748
+ try:
749
+ typer.echo(f"pending: {await store.outbox_pending()}")
750
+ finally:
751
+ await store.close()
752
+
753
+ _run_async(_run)
754
+
755
+
756
+ @outbox_app.command("relay")
757
+ def outbox_relay(
758
+ nats_url: str = typer.Option(..., "--nats", help="NATS server URL, e.g. nats://localhost:4222"),
759
+ stream: str = typer.Option("URO", help="JetStream stream (created if missing)"),
760
+ subject_prefix: str = typer.Option("uro", help="subjects are <prefix>.<world>.<branch>"),
761
+ once: bool = typer.Option(False, "--once", help="drain what is pending, then exit"),
762
+ interval: float = typer.Option(1.0, help="seconds between polls (without --once)"),
763
+ batch: int = typer.Option(100, min=1, help="commits claimed per transaction"),
764
+ ) -> None:
765
+ """Publish committed beats to NATS JetStream (D-57). At-least-once from the outbox; JetStream
766
+ drops a re-sent commit (Nats-Msg-Id = commit id). Run ONE relay to keep each branch in commit
767
+ order (there is no global order across branches); several are safe — no row is published
768
+ twice — but may interleave a branch. A publish error is retried with backoff (`--once` exits
769
+ instead)."""
770
+ try:
771
+ from uro_core.adapters.nats import NatsJetStreamPublisher
772
+ except ImportError as exc:
773
+ typer.echo("the NATS relay needs `pip install 'uro-core[nats]'`", err=True)
774
+ raise typer.Exit(1) from exc
775
+ from uro_core.outbox import relay_forever, relay_outbox
776
+
777
+ async def _run() -> None:
778
+ store = build_store()
779
+ await connect_store(store)
780
+ publisher = NatsJetStreamPublisher(nats_url, stream=stream, subject_prefix=subject_prefix)
781
+ await publisher.connect()
782
+ try:
783
+ if once: # a one-shot drain: an error exits non-zero (right for scripts and cron)
784
+ sent = await relay_outbox(store, publisher, limit=batch)
785
+ typer.echo(f"published {sent} commit(s)")
786
+ return
787
+ # the daemon: a publish error is logged and retried with backoff, never fatal
788
+ await relay_forever(
789
+ store,
790
+ publisher,
791
+ interval=interval,
792
+ limit=batch,
793
+ on_published=lambda n: typer.echo(f"published {n} commit(s)"),
794
+ )
795
+ finally:
796
+ await publisher.close()
797
+ await store.close()
798
+
799
+ _run_async(_run)
800
+
801
+
736
802
  @token_app.command("mint")
737
803
  def token_mint(
738
804
  campaign_id: str,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes