From 5a63b6eaf5283bc27c175c25ec70a03ed8e11cc3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 18 Oct 2026 19:57:34 +0530 Subject: [PATCH] fix: read PRIOMPT_TOKEN, regenerate stubs from priompt/proto, scrub name - PromptClient now reads PRIOMPT_TOKEN. Precedence matches the priompt CLI: token=, then PRIOMPT_TOKEN, then the token in url=/PRIOMPT_URL. - Regenerate prompt_pb2.py from priompt/proto (e7dce07). The embedded descriptor carried the old go_package (priomptproto/gen/...); it now says github.com/priompt/proto/gen/priompt/v1. prompt_pb2_grpc.py regenerates byte-identical (after the documented import fix), and the wire paths are unchanged. - Pin the protobuf plugin to v35.1 in buf.gen.yaml so regenerated gencode keeps working with the protobuf>=7.35 floor (unpinned, it now emits 7.36.2 gencode, which rejects an older runtime). - README: use "Ada" in the example instead of a real name; document the token default. Fixes #5 Co-Authored-By: Claude Opus 5.5 --- README.md | 4 ++-- buf.gen.yaml | 4 +++- priompt_sdk/client.py | 7 +++++-- priompt_sdk/v1/prompt_pb2.py | 4 ++-- tests/test_client.py | 26 ++++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fd1d018..1cff1ea 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ client = PromptClient(host="localhost:8443") # token=... if auth is o prompt = client.get("priompt://acme/onboarding/welcome") print(prompt.template) # Hi {name}, welcome to {org}! -text = prompt.template.format(name="Sujal", org="Acme") +text = prompt.template.format(name="Ada", org="Acme") ``` What comes back from `get()`: @@ -67,7 +67,7 @@ PromptClient(host=None, token=None, tls=False, ca_cert=None, | --- | --- | | `host` | `address:port` of the server. Optional if `url` or `PRIOMPT_URL` is set | | `url` | a full `priompt://@host:port` string; defaults to `PRIOMPT_URL` | -| `token` | sent as `authorization: Bearer `; explicit value wins over the url's token | +| `token` | sent as `authorization: Bearer `; defaults to `PRIOMPT_TOKEN`, then the url's token | | `tls` / `ca_cert` | use TLS, optionally pinning a CA certificate | | `cache_ttl` | local cache lifetime in seconds (`0` = off) — repeat `get`s within the TTL never touch the network | | `nats_url` | endpoint for `subscribe()` | diff --git a/buf.gen.yaml b/buf.gen.yaml index b57b1c3..e2a85d2 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -16,7 +16,9 @@ version: v2 inputs: - directory: ../proto/proto plugins: - - remote: buf.build/protocolbuffers/python + # Pinned: v35.x emits gencode for protobuf 7.35, matching the + # protobuf>=7.35 floor in pyproject.toml. A newer plugin raises that floor. + - remote: buf.build/protocolbuffers/python:v35.1 out: . - remote: buf.build/grpc/python out: . diff --git a/priompt_sdk/client.py b/priompt_sdk/client.py index bccca3e..b649cac 100644 --- a/priompt_sdk/client.py +++ b/priompt_sdk/client.py @@ -42,8 +42,11 @@ def __init__( url=None, ): # One connection string covers local/self-host/cloud: pass url= or set - # PRIOMPT_URL (priompt://@host:port). An explicit host= wins; - # an explicit token= overrides the URL's token. + # PRIOMPT_URL (priompt://@host:port). An explicit host= wins. + # Token precedence: token=, then PRIOMPT_TOKEN, then the URL's token + # (the same order as the priompt CLI). + if token is None: + token = os.environ.get("PRIOMPT_TOKEN") or None if host is None: raw = url or os.environ.get("PRIOMPT_URL") if raw: diff --git a/priompt_sdk/v1/prompt_pb2.py b/priompt_sdk/v1/prompt_pb2.py index 06a1a39..9b9efc2 100644 --- a/priompt_sdk/v1/prompt_pb2.py +++ b/priompt_sdk/v1/prompt_pb2.py @@ -24,14 +24,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17priompt/v1/prompt.proto\x12\npriompt.v1\",\n\x12ListPromptsRequest\x12\x16\n\x06prefix\x18\x01 \x01(\tR\x06prefix\"H\n\x13ListPromptsResponse\x12\x31\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x17.priompt.v1.PromptEntryR\x07\x65ntries\"B\n\x0bPromptEntry\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12!\n\x0cversion_hash\x18\x02 \x01(\tR\x0bversionHash\"]\n\x10SetBranchRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x16\n\x06\x62ranch\x18\x02 \x01(\tR\x06\x62ranch\x12\x1f\n\x0b\x63ommit_hash\x18\x03 \x01(\tR\ncommitHash\"6\n\x11SetBranchResponse\x12!\n\x0cversion_hash\x18\x01 \x01(\tR\x0bversionHash\"\xc2\x01\n\x06\x43ommit\x12\x12\n\x04hash\x18\x01 \x01(\tR\x04hash\x12!\n\x0cversion_hash\x18\x02 \x01(\tR\x0bversionHash\x12\x16\n\x06parent\x18\x03 \x01(\tR\x06parent\x12\x18\n\x07parent2\x18\x04 \x01(\tR\x07parent2\x12\x16\n\x06\x61uthor\x18\x05 \x01(\tR\x06\x61uthor\x12\x18\n\x07message\x18\x06 \x01(\tR\x07message\x12\x1d\n\ncreated_at\x18\x07 \x01(\tR\tcreatedAt\":\n\x0eHistoryRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x16\n\x06\x62ranch\x18\x02 \x01(\tR\x06\x62ranch\"?\n\x0fHistoryResponse\x12,\n\x07\x63ommits\x18\x01 \x03(\x0b\x32\x12.priompt.v1.CommitR\x07\x63ommits\"O\n\x13\x43reateBranchRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n\x04\x66rom\x18\x03 \x01(\tR\x04\x66rom\"7\n\x14\x43reateBranchResponse\x12\x1f\n\x0b\x63ommit_hash\x18\x01 \x01(\tR\ncommitHash\"h\n\x12MergeBranchRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x12\n\x04into\x18\x02 \x01(\tR\x04into\x12\x12\n\x04\x66rom\x18\x03 \x01(\tR\x04\x66rom\x12\x18\n\x07message\x18\x04 \x01(\tR\x07message\"6\n\x13MergeBranchResponse\x12\x1f\n\x0b\x63ommit_hash\x18\x01 \x01(\tR\ncommitHash\"\\\n\x12\x44iffCommitsRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x1b\n\tfrom_hash\x18\x02 \x01(\tR\x08\x66romHash\x12\x17\n\x07to_hash\x18\x03 \x01(\tR\x06toHash\"6\n\x10GetPromptRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x10\n\x03ref\x18\x02 \x01(\tR\x03ref\"\x9b\x01\n\x11GetPromptResponse\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x1a\n\x08template\x18\x02 \x01(\tR\x08template\x12\x14\n\x05slots\x18\x03 \x03(\tR\x05slots\x12!\n\x0cversion_hash\x18\x04 \x01(\tR\x0bversionHash\x12\x1f\n\x0b\x63ommit_hash\x18\x05 \x01(\tR\ncommitHash\"\x8c\x01\n\x14PublishPromptRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x1a\n\x08template\x18\x02 \x01(\tR\x08template\x12\x14\n\x05slots\x18\x03 \x03(\tR\x05slots\x12\x18\n\x07message\x18\x04 \x01(\tR\x07message\x12\x16\n\x06\x62ranch\x18\x05 \x01(\tR\x06\x62ranch\":\n\x15PublishPromptResponse\x12!\n\x0cversion_hash\x18\x01 \x01(\tR\x0bversionHash\"H\n\x11\x44iffPromptRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12!\n\x0cnew_template\x18\x02 \x01(\tR\x0bnewTemplate\"B\n\x12\x44iffPromptResponse\x12,\n\x07\x63hanges\x18\x01 \x03(\x0b\x32\x12.priompt.v1.ChangeR\x07\x63hanges\"6\n\x06Window\x12\x16\n\x06radius\x18\x01 \x01(\x05R\x06radius\x12\x14\n\x05\x64\x65lta\x18\x02 \x01(\x01R\x05\x64\x65lta\"\xe3\x02\n\x06\x43hange\x12\x1b\n\told_start\x18\x01 \x01(\x05R\x08oldStart\x12\x17\n\x07old_end\x18\x02 \x01(\x05R\x06oldEnd\x12\x1b\n\tnew_start\x18\x03 \x01(\x05R\x08newStart\x12\x17\n\x07new_end\x18\x04 \x01(\x05R\x06newEnd\x12\x12\n\x04kind\x18\x05 \x01(\tR\x04kind\x12\x1f\n\x0bpoint_delta\x18\x06 \x01(\x01R\npointDelta\x12\"\n\x02up\x18\x07 \x03(\x0b\x32\x12.priompt.v1.WindowR\x02up\x12&\n\x04\x64own\x18\x08 \x03(\x0b\x32\x12.priompt.v1.WindowR\x04\x64own\x12\x1f\n\x0bup_boundary\x18\t \x01(\x08R\nupBoundary\x12#\n\rdown_boundary\x18\n \x01(\x08R\x0c\x64ownBoundary\x12&\n\x0e\x63lassification\x18\x0b \x01(\tR\x0e\x63lassification2\xcc\x05\n\rPromptService\x12H\n\tGetPrompt\x12\x1c.priompt.v1.GetPromptRequest\x1a\x1d.priompt.v1.GetPromptResponse\x12K\n\nDiffPrompt\x12\x1d.priompt.v1.DiffPromptRequest\x1a\x1e.priompt.v1.DiffPromptResponse\x12T\n\rPublishPrompt\x12 .priompt.v1.PublishPromptRequest\x1a!.priompt.v1.PublishPromptResponse\x12\x42\n\x07History\x12\x1a.priompt.v1.HistoryRequest\x1a\x1b.priompt.v1.HistoryResponse\x12Q\n\x0c\x43reateBranch\x12\x1f.priompt.v1.CreateBranchRequest\x1a .priompt.v1.CreateBranchResponse\x12N\n\x0bMergeBranch\x12\x1e.priompt.v1.MergeBranchRequest\x1a\x1f.priompt.v1.MergeBranchResponse\x12M\n\x0b\x44iffCommits\x12\x1e.priompt.v1.DiffCommitsRequest\x1a\x1e.priompt.v1.DiffPromptResponse\x12H\n\tSetBranch\x12\x1c.priompt.v1.SetBranchRequest\x1a\x1d.priompt.v1.SetBranchResponse\x12N\n\x0bListPrompts\x12\x1e.priompt.v1.ListPromptsRequest\x1a\x1f.priompt.v1.ListPromptsResponseB\'Z%priomptproto/gen/priompt/v1;priomptv1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17priompt/v1/prompt.proto\x12\npriompt.v1\",\n\x12ListPromptsRequest\x12\x16\n\x06prefix\x18\x01 \x01(\tR\x06prefix\"H\n\x13ListPromptsResponse\x12\x31\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x17.priompt.v1.PromptEntryR\x07\x65ntries\"B\n\x0bPromptEntry\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12!\n\x0cversion_hash\x18\x02 \x01(\tR\x0bversionHash\"]\n\x10SetBranchRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x16\n\x06\x62ranch\x18\x02 \x01(\tR\x06\x62ranch\x12\x1f\n\x0b\x63ommit_hash\x18\x03 \x01(\tR\ncommitHash\"6\n\x11SetBranchResponse\x12!\n\x0cversion_hash\x18\x01 \x01(\tR\x0bversionHash\"\xc2\x01\n\x06\x43ommit\x12\x12\n\x04hash\x18\x01 \x01(\tR\x04hash\x12!\n\x0cversion_hash\x18\x02 \x01(\tR\x0bversionHash\x12\x16\n\x06parent\x18\x03 \x01(\tR\x06parent\x12\x18\n\x07parent2\x18\x04 \x01(\tR\x07parent2\x12\x16\n\x06\x61uthor\x18\x05 \x01(\tR\x06\x61uthor\x12\x18\n\x07message\x18\x06 \x01(\tR\x07message\x12\x1d\n\ncreated_at\x18\x07 \x01(\tR\tcreatedAt\":\n\x0eHistoryRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x16\n\x06\x62ranch\x18\x02 \x01(\tR\x06\x62ranch\"?\n\x0fHistoryResponse\x12,\n\x07\x63ommits\x18\x01 \x03(\x0b\x32\x12.priompt.v1.CommitR\x07\x63ommits\"O\n\x13\x43reateBranchRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n\x04\x66rom\x18\x03 \x01(\tR\x04\x66rom\"7\n\x14\x43reateBranchResponse\x12\x1f\n\x0b\x63ommit_hash\x18\x01 \x01(\tR\ncommitHash\"h\n\x12MergeBranchRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x12\n\x04into\x18\x02 \x01(\tR\x04into\x12\x12\n\x04\x66rom\x18\x03 \x01(\tR\x04\x66rom\x12\x18\n\x07message\x18\x04 \x01(\tR\x07message\"6\n\x13MergeBranchResponse\x12\x1f\n\x0b\x63ommit_hash\x18\x01 \x01(\tR\ncommitHash\"\\\n\x12\x44iffCommitsRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x1b\n\tfrom_hash\x18\x02 \x01(\tR\x08\x66romHash\x12\x17\n\x07to_hash\x18\x03 \x01(\tR\x06toHash\"6\n\x10GetPromptRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x10\n\x03ref\x18\x02 \x01(\tR\x03ref\"\x9b\x01\n\x11GetPromptResponse\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x1a\n\x08template\x18\x02 \x01(\tR\x08template\x12\x14\n\x05slots\x18\x03 \x03(\tR\x05slots\x12!\n\x0cversion_hash\x18\x04 \x01(\tR\x0bversionHash\x12\x1f\n\x0b\x63ommit_hash\x18\x05 \x01(\tR\ncommitHash\"\x8c\x01\n\x14PublishPromptRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12\x1a\n\x08template\x18\x02 \x01(\tR\x08template\x12\x14\n\x05slots\x18\x03 \x03(\tR\x05slots\x12\x18\n\x07message\x18\x04 \x01(\tR\x07message\x12\x16\n\x06\x62ranch\x18\x05 \x01(\tR\x06\x62ranch\":\n\x15PublishPromptResponse\x12!\n\x0cversion_hash\x18\x01 \x01(\tR\x0bversionHash\"H\n\x11\x44iffPromptRequest\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12!\n\x0cnew_template\x18\x02 \x01(\tR\x0bnewTemplate\"B\n\x12\x44iffPromptResponse\x12,\n\x07\x63hanges\x18\x01 \x03(\x0b\x32\x12.priompt.v1.ChangeR\x07\x63hanges\"6\n\x06Window\x12\x16\n\x06radius\x18\x01 \x01(\x05R\x06radius\x12\x14\n\x05\x64\x65lta\x18\x02 \x01(\x01R\x05\x64\x65lta\"\xe3\x02\n\x06\x43hange\x12\x1b\n\told_start\x18\x01 \x01(\x05R\x08oldStart\x12\x17\n\x07old_end\x18\x02 \x01(\x05R\x06oldEnd\x12\x1b\n\tnew_start\x18\x03 \x01(\x05R\x08newStart\x12\x17\n\x07new_end\x18\x04 \x01(\x05R\x06newEnd\x12\x12\n\x04kind\x18\x05 \x01(\tR\x04kind\x12\x1f\n\x0bpoint_delta\x18\x06 \x01(\x01R\npointDelta\x12\"\n\x02up\x18\x07 \x03(\x0b\x32\x12.priompt.v1.WindowR\x02up\x12&\n\x04\x64own\x18\x08 \x03(\x0b\x32\x12.priompt.v1.WindowR\x04\x64own\x12\x1f\n\x0bup_boundary\x18\t \x01(\x08R\nupBoundary\x12#\n\rdown_boundary\x18\n \x01(\x08R\x0c\x64ownBoundary\x12&\n\x0e\x63lassification\x18\x0b \x01(\tR\x0e\x63lassification2\xcc\x05\n\rPromptService\x12H\n\tGetPrompt\x12\x1c.priompt.v1.GetPromptRequest\x1a\x1d.priompt.v1.GetPromptResponse\x12K\n\nDiffPrompt\x12\x1d.priompt.v1.DiffPromptRequest\x1a\x1e.priompt.v1.DiffPromptResponse\x12T\n\rPublishPrompt\x12 .priompt.v1.PublishPromptRequest\x1a!.priompt.v1.PublishPromptResponse\x12\x42\n\x07History\x12\x1a.priompt.v1.HistoryRequest\x1a\x1b.priompt.v1.HistoryResponse\x12Q\n\x0c\x43reateBranch\x12\x1f.priompt.v1.CreateBranchRequest\x1a .priompt.v1.CreateBranchResponse\x12N\n\x0bMergeBranch\x12\x1e.priompt.v1.MergeBranchRequest\x1a\x1f.priompt.v1.MergeBranchResponse\x12M\n\x0b\x44iffCommits\x12\x1e.priompt.v1.DiffCommitsRequest\x1a\x1e.priompt.v1.DiffPromptResponse\x12H\n\tSetBranch\x12\x1c.priompt.v1.SetBranchRequest\x1a\x1d.priompt.v1.SetBranchResponse\x12N\n\x0bListPrompts\x12\x1e.priompt.v1.ListPromptsRequest\x1a\x1f.priompt.v1.ListPromptsResponseB3Z1github.com/priompt/proto/gen/priompt/v1;priomptv1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'priompt.v1.prompt_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'Z%priomptproto/gen/priompt/v1;priomptv1' + _globals['DESCRIPTOR']._serialized_options = b'Z1github.com/priompt/proto/gen/priompt/v1;priomptv1' _globals['_LISTPROMPTSREQUEST']._serialized_start=39 _globals['_LISTPROMPTSREQUEST']._serialized_end=83 _globals['_LISTPROMPTSRESPONSE']._serialized_start=85 diff --git a/tests/test_client.py b/tests/test_client.py index 7d7e2be..f5e5eac 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -208,3 +208,29 @@ async def connect(url, error_cb=None, **kw): with pytest.raises(ConnectionError, match="Authorization Violation") as exc: c.subscribe("priompt://x", lambda *_: None) assert "WRONG" not in str(exc.value) # the token is not echoed + + +# --- token resolution: token=, then PRIOMPT_TOKEN, then the URL's token --- + +def _bearer(c): + return dict(c._md or [])["authorization"] if c._md else None + + +def test_reads_priompt_token_env(monkeypatch): + """#5: the client must read PRIOMPT_TOKEN like the CLI does.""" + monkeypatch.delenv("PRIOMPT_URL", raising=False) + monkeypatch.setenv("PRIOMPT_TOKEN", "envtok") + assert _bearer(PromptClient("localhost:1")) == "Bearer envtok" + + +def test_token_precedence(monkeypatch): + monkeypatch.setenv("PRIOMPT_TOKEN", "envtok") + # PRIOMPT_TOKEN beats the token inside the URL... + assert _bearer(PromptClient(url="priompt://urltok@localhost:1")) == "Bearer envtok" + # ...and an explicit token= beats both. + c = PromptClient(url="priompt://urltok@localhost:1", token="argtok") + assert _bearer(c) == "Bearer argtok" + # Without PRIOMPT_TOKEN, the URL's token is used. + monkeypatch.delenv("PRIOMPT_TOKEN") + assert _bearer(PromptClient(url="priompt://urltok@localhost:1")) == "Bearer urltok" + assert _bearer(PromptClient("localhost:1")) is None