diff --git a/agentscore_commerce/identity/__init__.py b/agentscore_commerce/identity/__init__.py index 5e6fb78..9a4f7b6 100644 --- a/agentscore_commerce/identity/__init__.py +++ b/agentscore_commerce/identity/__init__.py @@ -35,6 +35,7 @@ shipping_state_allowed, ) from agentscore_commerce.identity.signer import extract_x402_signer +from agentscore_commerce.identity.tokens import hash_operator_token from agentscore_commerce.identity.types import ( AgentIdentity, AgentMemoryHint, @@ -138,6 +139,7 @@ def _load_asgi_middleware() -> tuple[Any, Any]: "denial_reason_to_body", "extract_x402_signer", "generate_ucp_signing_key", + "hash_operator_token", "is_fixable_denial", "mpp_payment_handler", "run_gate_with_enforcement", diff --git a/agentscore_commerce/identity/tokens.py b/agentscore_commerce/identity/tokens.py new file mode 100644 index 0000000..4b57e60 --- /dev/null +++ b/agentscore_commerce/identity/tokens.py @@ -0,0 +1,20 @@ +"""Operator-token hashing. + +Plaintext operator tokens (``opc_...``) never persist on disk. Merchants hash +them before storing in DB columns and before comparing against persisted hashes. +This helper exposes the canonical hash so every consumer agrees on the shape. +""" + +from __future__ import annotations + +import hashlib + + +def hash_operator_token(plaintext: str) -> str: + """sha256 hex digest of a plaintext operator token. + + Use at every persistence boundary (INSERT) AND every comparison boundary + (SELECT WHERE operator_token_id = ...) so plaintext tokens never land in + durable storage. + """ + return hashlib.sha256(plaintext.encode("utf-8")).hexdigest() diff --git a/tests/test_tokens.py b/tests/test_tokens.py new file mode 100644 index 0000000..0bd66c9 --- /dev/null +++ b/tests/test_tokens.py @@ -0,0 +1,59 @@ +"""Tests for ``agentscore_commerce.identity.tokens.hash_operator_token``. + +The expected digests below are hardcoded — locked as the cross-language +contract with the Node sibling at ``node-commerce/tests/identity/tokens.test.ts``. +Both files reference the same fixture inputs and the same expected output bytes. +A drift in either language (algorithm swap, encoding change, accidental truncation) +fails that language's test against the locked digest. +""" + +from __future__ import annotations + +import pytest + +from agentscore_commerce.identity import hash_operator_token + +# Cross-language fixture inputs + expected digests. The digests are +# sha256(.encode("utf-8")).hexdigest() computed once and locked here so +# the Python and Node sibling tests assert against identical bytes. +# +# Parametrized so each fixture gets its own test invocation: if multiple +# fixtures drift simultaneously, every failure is reported (a for-loop inside +# one test would short-circuit on the first failure). +_FIXTURES = [ + ("opc_test", "97c30e2a512b5968772c2930705bdafff4831d672556dce26c92b83f7e58508d"), + ("opc_cross_lang_fixture", "96690dd2659bc1e33227e943d5f8a526c7c95a0ede5775a1573abab6578ca8ec"), + ("opc_anything", "e6ba517ac96ee39190c4d703b2d968fec96e87827374e56095a2f443d870730d"), + ("opc_42", "731985dd676ea0702b3e6f6cbb107eaf467319e2801e6f953f08cbcc7dd71684"), + # Non-ASCII fixture — UTF-8 encoding of "é" is 0xC3 0xA9; locks the encoding + # contract so a future implementation that drops the explicit "utf-8" arg + # still produces the same bytes. + ("opc_é", "c1dba11d60cbfc1264d115e07a74a0355b6a66ded4ee3f930024a1733ba6942f"), + # Empty-string sha256 is a canonical value documented in many specs; locking + # it here catches an implementation that silently rejects or transforms "". + ("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), +] + + +@pytest.mark.parametrize(("plaintext", "expected"), _FIXTURES, ids=[repr(p) for p, _ in _FIXTURES]) +def test_known_digest_locked(plaintext: str, expected: str) -> None: + """Each fixture input maps to the locked cross-language digest.""" + assert hash_operator_token(plaintext) == expected + + +def test_output_is_64_char_lowercase_hex() -> None: + """sha256 hex digests are always 64 characters of lowercase hex.""" + out = hash_operator_token("opc_anything") + assert len(out) == 64 + assert out == out.lower() + assert all(c in "0123456789abcdef" for c in out) + + +def test_deterministic_across_calls() -> None: + """Same input always yields the same digest (no salt, no nonce).""" + assert hash_operator_token("opc_42") == hash_operator_token("opc_42") + + +def test_distinct_inputs_distinct_outputs() -> None: + """Different plaintexts produce different digests.""" + assert hash_operator_token("opc_a") != hash_operator_token("opc_b") diff --git a/uv.lock b/uv.lock index c37f3d0..01471ff 100644 --- a/uv.lock +++ b/uv.lock @@ -424,7 +424,7 @@ wheels = [ [[package]] name = "cdp-sdk" -version = "1.45.0" +version = "1.45.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -439,9 +439,9 @@ dependencies = [ { name = "urllib3" }, { name = "web3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/f6/5f59af94b88520799f6d900df9edaf31907bd44e63309f379d616af67186/cdp_sdk-1.45.0.tar.gz", hash = "sha256:2cb3742ce40ca0b0572445914e5d656d6b05b00f8332de430d6b9e3ee1726067", size = 408683, upload-time = "2026-05-11T22:44:50.401Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/a5/181e5c9cac892c0e395de6a72b4b92ada35c68bf761469a280fc843fef4d/cdp_sdk-1.45.1.tar.gz", hash = "sha256:3b6f5b6c0cb7295907c5e4ffaf588702bb8f0f074c8555e4f4c05235a868a42b", size = 417309, upload-time = "2026-05-13T17:52:13.387Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/12/bb86d7ed976fa9c64931c73bcd68123c061cd01181913478af68df392f17/cdp_sdk-1.45.0-py3-none-any.whl", hash = "sha256:5db4a4615a158b33861b464980778d71dd80d631876b98693e027075c8b63170", size = 1065028, upload-time = "2026-05-11T22:44:48.154Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ce/2718b05cfb4f94522e8a0eb3502e7d1535d1d980cc53d921c0857c9ae0b6/cdp_sdk-1.45.1-py3-none-any.whl", hash = "sha256:29cf91e472ee4bd39d3d5631905caecd78d03460dafeb0dd26f72f9595906426", size = 1099212, upload-time = "2026-05-13T17:52:11.244Z" }, ] [[package]] @@ -2526,7 +2526,7 @@ wheels = [ [[package]] name = "requests" -version = "2.34.0" +version = "2.34.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -2534,9 +2534,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/b8/7a707d60fea4c49094e40262cc0e2ca6c768cca21587e34d3f705afec47e/requests-2.34.0.tar.gz", hash = "sha256:7d62fe92f50eb82c529b0916bb445afa1531a566fc8f35ffdc64446e771b856a", size = 142436, upload-time = "2026-05-11T19:29:51.717Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/36/7180e7f077c38108945dbbdf60fe04db681c3feb6e96419f8c6dc8723741/requests-2.34.1.tar.gz", hash = "sha256:0fc5669f2b69704449fe1552360bd2a73a54512dfd03e65529157f1513322beb", size = 142783, upload-time = "2026-05-13T19:20:24.662Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/e6/e300fce5fe83c30520607a015dabd985df3251e188d234bfe9492e17a389/requests-2.34.0-py3-none-any.whl", hash = "sha256:917520a21b767485ce7c588f4ebb917c436b24a31231b44228715eaeb5a52c60", size = 73021, upload-time = "2026-05-11T19:29:49.923Z" }, + { url = "https://files.pythonhosted.org/packages/15/5a/4a949d170476de3c04ac036b5466422fbcbf348a917d8042eedf2cac7d1b/requests-2.34.1-py3-none-any.whl", hash = "sha256:bf38a3ff993960d3dd819c08862c40b3c703306eb7c744fcd9f4ddbb95b548f0", size = 73085, upload-time = "2026-05-13T19:20:22.827Z" }, ] [[package]] @@ -2687,27 +2687,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" }, - { url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" }, - { url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" }, - { url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" }, - { url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" }, - { url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" }, - { url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" }, - { url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" }, - { url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" }, - { url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" }, - { url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" }, +version = "0.15.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/21/a7d5c126d5b557715ef81098f3db2fe20f622a039ff2e626af28d674ab80/ruff-0.15.13.tar.gz", hash = "sha256:f9d89f17f7ba7fb2ed42921f0df75da797a9a5d71bc39049e2c687cf2baf44b7", size = 4678180, upload-time = "2026-05-14T13:44:37.869Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/61/11d458dc6ac22504fd8e237b29dfd40504c7fbbcc8930402cfe51a8e63ed/ruff-0.15.13-py3-none-linux_armv6l.whl", hash = "sha256:444b580fc72fd6887e650acd3e575e18cdc79dbcf42fb4030b491057921f61f8", size = 10738279, upload-time = "2026-05-14T13:44:18.7Z" }, + { url = "https://files.pythonhosted.org/packages/86/ca/caa871ee7be718c45256fada4e16a218ee3e33f0c4a46b729a60a24912e6/ruff-0.15.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6590d009e7cb7ebf36f83dbdd44a3fa48a0994ff6f1cdc1b08006abe58f98dc7", size = 11124798, upload-time = "2026-05-14T13:44:06.427Z" }, + { url = "https://files.pythonhosted.org/packages/d3/19/43f5f2e568dddde567fc41f8471f9432c09563e19d3e617a48cfa52f8f0a/ruff-0.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1c26d2f66163deeb6e08d8b39fbbe983ce3c71cea06a6d7591cfd1421793c629", size = 10460761, upload-time = "2026-05-14T13:44:04.375Z" }, + { url = "https://files.pythonhosted.org/packages/99/df/cf938cd6de3003178f03ad7c1ea2a6c099468c03a35037985070b37e76be/ruff-0.15.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbd6f94b434f896308e4d57fb7bfde0d02b99f7a64b3bdab0fdfa6a864203a5", size = 10804451, upload-time = "2026-05-14T13:44:25.221Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7d/5d0973129b154ded2225729169d7068f26b467760b146493fde138415f23/ruff-0.15.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3259f3be4d181bda591da5db2571aed6853c6a048157756448020bc6c5cd22", size = 10534285, upload-time = "2026-05-14T13:44:08.888Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e3/6b999bbc66cd51e5f073842bc2a3995e99c5e0e72e16b15e7261f7abf57a/ruff-0.15.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae9c17e5eb4430c154e76abc25d79a318190f5a997f38fb6b114416c5319ffc9", size = 11312063, upload-time = "2026-05-14T13:44:11.274Z" }, + { url = "https://files.pythonhosted.org/packages/af/5a/642639e9f5db04f1e97fbd6e091c6fd20725bdf072fb114d00eefb9e6eb8/ruff-0.15.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e2e39bff6c341f4b577a21b801326fab0b11847f48fcaa83f00a113c9b3cb55", size = 12183079, upload-time = "2026-05-14T13:44:01.634Z" }, + { url = "https://files.pythonhosted.org/packages/19/4c/7585735f6b53b0f12de13618b2f7d250a844f018822efc899df2e7b8295f/ruff-0.15.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8d9a8e08013542e94d3220bc5b62cc3e5ef87c5f74bff367d3fac14fab013e6", size = 11440833, upload-time = "2026-05-14T13:43:59.043Z" }, + { url = "https://files.pythonhosted.org/packages/e8/31/bf1a0803d077e679cfeee5f2f67290a0fa79c7385b5d9a8c17b9db2c48f0/ruff-0.15.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc411dfebe5eebe55ce041c6ae080eb7668955e866daa2fbb16692a784f1c4ca", size = 11434486, upload-time = "2026-05-14T13:44:27.761Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4e/62c9b999875d4f14db80f277c030578f5e249c9852d65b7ac7ad0b43c041/ruff-0.15.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:768494eb08b9cee54e2fd27969966f74db5a57f6eaa7a90fcb3306af34dfc4bd", size = 11385189, upload-time = "2026-05-14T13:44:13.704Z" }, + { url = "https://files.pythonhosted.org/packages/fc/89/7e959047a104df3eb12863447c110140191fc5b6c4f379ea2e803fcdb0e4/ruff-0.15.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fb75f9a3a7e42ffe117d734494e6c5e5cb3565d66e12612cb63d0e572a41a5b6", size = 10781380, upload-time = "2026-05-14T13:43:56.734Z" }, + { url = "https://files.pythonhosted.org/packages/ff/52/5fd18f3b88cab63e88aa11516b3b4e1e5f720e5c330f8dbe5c26210f41f8/ruff-0.15.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8cb74dd33bb2f6613faf7fc03b660053b5ac4f80e706d5788c6335e2a8048d51", size = 10540605, upload-time = "2026-05-14T13:44:20.748Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e0/9e35f338990d3e41a82875ff7053ffe97541dae81c9d02143177f381d572/ruff-0.15.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7ef823f817fcd191dc934e984be9cf4094f808effa16f2542ad8e821ba02bbf2", size = 11036554, upload-time = "2026-05-14T13:44:16.256Z" }, + { url = "https://files.pythonhosted.org/packages/c2/13/070fb048c24080fba188f66371e2a92785be257ad02242066dc7255ac6e9/ruff-0.15.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f345a13937bd7f09f6f5d19fa0721b0c103e00e7f62bc67089a8e5e037719e0b", size = 11528133, upload-time = "2026-05-14T13:44:22.808Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8c/b1e1666aef7fc6555094d73ae6cd981701781ae85b97ceefc0eebd0b4668/ruff-0.15.13-py3-none-win32.whl", hash = "sha256:4044f94208b3b05ba0fc4a4abd0558cf4d6459bd18325eead7fd8cc66f909b41", size = 10721455, upload-time = "2026-05-14T13:44:35.697Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a6/870a3e8a50590bb92be184ad928c2922f088b00d9dc5c5ec7b924ee08c22/ruff-0.15.13-py3-none-win_amd64.whl", hash = "sha256:7064884d442b7d477b4e7473d12da7f08851d2b1982763c5d3f388a19468a1a4", size = 11900409, upload-time = "2026-05-14T13:44:30.389Z" }, + { url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" }, ] [[package]] @@ -2804,7 +2804,7 @@ wheels = [ [[package]] name = "solana" -version = "0.36.11" +version = "0.36.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "construct-typing" }, @@ -2813,9 +2813,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/66/b8cd6e4d95bfe46798942ace31935e7799005a4e2180869dc7bac6b75be9/solana-0.36.11.tar.gz", hash = "sha256:2fdcf483674f4b88fe6510524bf3234a5837d19fe1815aa5a285f2739d28b3a3", size = 54516, upload-time = "2026-01-03T02:11:52.243Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/66/708f3f4ec285773bf1bd785afa442d8315e8471054cce9817ee9d3a17301/solana-0.36.12.tar.gz", hash = "sha256:3f103f5b72f19d44622254c618793fd72ae683776f9676b906e2f1d2f5cda292", size = 54656, upload-time = "2026-05-14T03:45:09.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/8d/807eebf0560759ad90464060e0d1d87ff5409beb6ed56104c553a83a976a/solana-0.36.11-py3-none-any.whl", hash = "sha256:1d659decc67a40ee1e9b5ded373a076b87cf3b4bd0645e120d16d9348c2025ba", size = 64786, upload-time = "2026-01-03T02:11:50.811Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8d/70e07b289a46a1e39611bb45b9af8646566612c3a62f639f6a2c00e7af00/solana-0.36.12-py3-none-any.whl", hash = "sha256:37de51e21a81f5a90e9bb0ede393c70e70011626fd903d82d2c678b050c94c94", size = 64870, upload-time = "2026-05-14T03:45:07.913Z" }, ] [[package]] @@ -3359,16 +3359,16 @@ wheels = [ [[package]] name = "x402" -version = "2.9.0" +version = "2.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nest-asyncio" }, { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/d1/72fc451fe9e5fe999966f8c6d483cdf08ac931c04b499db8a9126cf07845/x402-2.9.0.tar.gz", hash = "sha256:2f4dad28f2010bf796e8f3057f019cc3e46616c54342f913192aaabe9affcb0b", size = 1867212, upload-time = "2026-04-27T17:14:44.276Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/61/b6e69aefdbb507c70139d13739bc4640656100671ddc395323cc566d3a86/x402-2.10.0.tar.gz", hash = "sha256:db14d9cc61b8db5a2c1372447ec42245b7889a86611c48304fe5be22d5f66b29", size = 1991574, upload-time = "2026-05-13T21:02:30.648Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/37/f0daae6f14cda3d1c74f73ebad3a0614c18add942a6928378d085e2ef931/x402-2.9.0-py3-none-any.whl", hash = "sha256:76c5e3ecb59cece7bb33f435aaef42c7e9ee66341a4963961c11152408fffc8a", size = 1942609, upload-time = "2026-04-27T17:14:42.328Z" }, + { url = "https://files.pythonhosted.org/packages/64/45/731542691157dbc1267c164a9430baa517d2c260e7e9b2bbea0279c3d4d0/x402-2.10.0-py3-none-any.whl", hash = "sha256:7e387e6d8c7fa706ffd35754da28e75b8493775d81bc6888cad0d9439106295d", size = 2080428, upload-time = "2026-05-13T21:02:28.561Z" }, ] [package.optional-dependencies]