walla-cli 0.1.8__tar.gz → 0.1.9__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.
Files changed (107) hide show
  1. {walla_cli-0.1.8 → walla_cli-0.1.9}/PKG-INFO +13 -5
  2. {walla_cli-0.1.8 → walla_cli-0.1.9}/README.md +12 -4
  3. walla_cli-0.1.9/docs/assets/walla-demo-poster.jpg +0 -0
  4. walla_cli-0.1.9/docs/assets/walla-demo.mp4 +0 -0
  5. {walla_cli-0.1.8 → walla_cli-0.1.9}/pyproject.toml +1 -1
  6. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_sell.py +28 -0
  7. walla_cli-0.1.9/uv.lock +2644 -0
  8. walla_cli-0.1.9/walla/__init__.py +7 -0
  9. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/sell.py +31 -3
  10. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/banner.py +12 -8
  11. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/main.py +2 -3
  12. walla_cli-0.1.8/walla/__init__.py +0 -7
  13. {walla_cli-0.1.8 → walla_cli-0.1.9}/.cursor/rules/quality.mdc +0 -0
  14. {walla_cli-0.1.8 → walla_cli-0.1.9}/.github/workflows/ci.yml +0 -0
  15. {walla_cli-0.1.8 → walla_cli-0.1.9}/.gitignore +0 -0
  16. {walla_cli-0.1.8 → walla_cli-0.1.9}/AGENTS.md +0 -0
  17. {walla_cli-0.1.8 → walla_cli-0.1.9}/LICENSE +0 -0
  18. {walla_cli-0.1.8 → walla_cli-0.1.9}/docs/WIRE.md +0 -0
  19. {walla_cli-0.1.8 → walla_cli-0.1.9}/docs/assets/walla-github-banner.jpg +0 -0
  20. {walla_cli-0.1.8 → walla_cli-0.1.9}/docs/code_quality.md +0 -0
  21. {walla_cli-0.1.8 → walla_cli-0.1.9}/docs/data_engineering_standards.md +0 -0
  22. {walla_cli-0.1.8 → walla_cli-0.1.9}/docs/mcp.md +0 -0
  23. {walla_cli-0.1.8 → walla_cli-0.1.9}/docs/objective_function.md +0 -0
  24. {walla_cli-0.1.8 → walla_cli-0.1.9}/docs/rate_limit_analysis.md +0 -0
  25. {walla_cli-0.1.8 → walla_cli-0.1.9}/fixtures/categories.json +0 -0
  26. {walla_cli-0.1.8 → walla_cli-0.1.9}/fixtures/item_detail.json +0 -0
  27. {walla_cli-0.1.8 → walla_cli-0.1.9}/fixtures/offer_buyer_request.json +0 -0
  28. {walla_cli-0.1.8 → walla_cli-0.1.9}/fixtures/search_bicicleta.json +0 -0
  29. {walla_cli-0.1.8 → walla_cli-0.1.9}/fixtures/sell_item_request.json +0 -0
  30. {walla_cli-0.1.8 → walla_cli-0.1.9}/fixtures/wire_contracts.json +0 -0
  31. {walla_cli-0.1.8 → walla_cli-0.1.9}/scripts/check_code_quality.py +0 -0
  32. {walla_cli-0.1.8 → walla_cli-0.1.9}/scripts/dogfood_run.py +0 -0
  33. {walla_cli-0.1.8 → walla_cli-0.1.9}/scripts/probe_rate.py +0 -0
  34. {walla_cli-0.1.8 → walla_cli-0.1.9}/skills/walla/SKILL.md +0 -0
  35. {walla_cli-0.1.8 → walla_cli-0.1.9}/skills/walla-buy/SKILL.md +0 -0
  36. {walla_cli-0.1.8 → walla_cli-0.1.9}/skills/walla-sell/SKILL.md +0 -0
  37. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_account.py +0 -0
  38. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_account_more.py +0 -0
  39. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_catch.py +0 -0
  40. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_chat_open.py +0 -0
  41. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_cli_json.py +0 -0
  42. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_core.py +0 -0
  43. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_desk.py +0 -0
  44. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_edges.py +0 -0
  45. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_export.py +0 -0
  46. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_http_more.py +0 -0
  47. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_live.py +0 -0
  48. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_negotiate.py +0 -0
  49. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_parsers.py +0 -0
  50. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_polite.py +0 -0
  51. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_regressions.py +0 -0
  52. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_search_api.py +0 -0
  53. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_session_store.py +0 -0
  54. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_shortlist.py +0 -0
  55. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_verdict.py +0 -0
  56. {walla_cli-0.1.8 → walla_cli-0.1.9}/tests/test_wire_contracts.py +0 -0
  57. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/__init__.py +0 -0
  58. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/actions.py +0 -0
  59. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/chat.py +0 -0
  60. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/cookies.py +0 -0
  61. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/desk.py +0 -0
  62. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/inbox.py +0 -0
  63. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/login.py +0 -0
  64. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/offer_wire.py +0 -0
  65. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/prompt.py +0 -0
  66. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/sell_wire.py +0 -0
  67. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/account/session_store.py +0 -0
  68. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/__init__.py +0 -0
  69. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/catch.py +0 -0
  70. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/cmd_account.py +0 -0
  71. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/cmd_desk.py +0 -0
  72. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/cmd_look.py +0 -0
  73. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/cmd_ops.py +0 -0
  74. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/cmd_sell.py +0 -0
  75. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/cli/console.py +0 -0
  76. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/core/__init__.py +0 -0
  77. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/core/envelope.py +0 -0
  78. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/core/errors.py +0 -0
  79. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/core/exceptions.py +0 -0
  80. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/core/human_fix.py +0 -0
  81. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/core/instruct.py +0 -0
  82. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/core/path.py +0 -0
  83. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/http/__init__.py +0 -0
  84. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/http/client.py +0 -0
  85. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/http/headers.py +0 -0
  86. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/http/polite.py +0 -0
  87. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/__init__.py +0 -0
  88. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/converge.py +0 -0
  89. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/export.py +0 -0
  90. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/export_pdf.py +0 -0
  91. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/negotiate.py +0 -0
  92. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/profile_store.py +0 -0
  93. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/pursuits.py +0 -0
  94. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/shortlist.py +0 -0
  95. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/stack.py +0 -0
  96. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/verdict.py +0 -0
  97. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/hunter/watches.py +0 -0
  98. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/mcp/__init__.py +0 -0
  99. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/mcp/_entry.py +0 -0
  100. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/mcp/server.py +0 -0
  101. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/models/__init__.py +0 -0
  102. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/models/account.py +0 -0
  103. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/models/listing.py +0 -0
  104. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/models/profile.py +0 -0
  105. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/search/__init__.py +0 -0
  106. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/search/api.py +0 -0
  107. {walla_cli-0.1.8 → walla_cli-0.1.9}/walla/search/parse.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: walla-cli
3
- Version: 0.1.8
3
+ Version: 0.1.9
4
4
  Summary: Wallapop.es CLI - look · offer · talk · sell for humans and agents
5
5
  Project-URL: Homepage, https://github.com/berendgort/wallapop-cli
6
6
  Project-URL: Repository, https://github.com/berendgort/wallapop-cli
@@ -55,12 +55,20 @@ Description-Content-Type: text/markdown
55
55
  Dr. Berend Gort · www.berendgort.dev
56
56
  ```
57
57
 
58
- ![walla banner](docs/assets/walla-github-banner.jpg)
58
+ ![walla demo](docs/assets/walla-demo.mp4)
59
59
 
60
- **walla** is an unofficial Wallapop.es **CLI + MCP + Python library** for Cursor
61
- agents and humans: search listings, negotiate, publish a listing, and follow the inbox.
60
+ Demo music: [Funkee Monkeee](https://mixkit.co/free-stock-music/funkee-monkeee-1140/) by Michael Ramir C.
61
+ ([Mixkit Stock Music Free License](https://mixkit.co/license/#musicFree)).
62
62
 
63
- Not affiliated with Wallapop. Respect ToS and rate limits.
63
+ **walla** turns Claude, GPT, or Cursor into a Wallapop helper that actually
64
+ *does* the work for you. You drop a few photos and answer a couple of
65
+ questions; the agent publishes the listing. When buyers message, it reads
66
+ the chats, replies for you, and negotiates down to your minimum price —
67
+ then shows you the best deal. You only open the Wallapop app to accept.
68
+ No typing chats. No babysitting the inbox.
69
+
70
+ Unofficial Wallapop.es CLI + MCP + Python library. Not affiliated with
71
+ Wallapop. Respect ToS and rate limits.
64
72
 
65
73
  ### What this unlocks
66
74
 
@@ -11,12 +11,20 @@
11
11
  Dr. Berend Gort · www.berendgort.dev
12
12
  ```
13
13
 
14
- ![walla banner](docs/assets/walla-github-banner.jpg)
14
+ ![walla demo](docs/assets/walla-demo.mp4)
15
15
 
16
- **walla** is an unofficial Wallapop.es **CLI + MCP + Python library** for Cursor
17
- agents and humans: search listings, negotiate, publish a listing, and follow the inbox.
16
+ Demo music: [Funkee Monkeee](https://mixkit.co/free-stock-music/funkee-monkeee-1140/) by Michael Ramir C.
17
+ ([Mixkit Stock Music Free License](https://mixkit.co/license/#musicFree)).
18
18
 
19
- Not affiliated with Wallapop. Respect ToS and rate limits.
19
+ **walla** turns Claude, GPT, or Cursor into a Wallapop helper that actually
20
+ *does* the work for you. You drop a few photos and answer a couple of
21
+ questions; the agent publishes the listing. When buyers message, it reads
22
+ the chats, replies for you, and negotiates down to your minimum price —
23
+ then shows you the best deal. You only open the Wallapop app to accept.
24
+ No typing chats. No babysitting the inbox.
25
+
26
+ Unofficial Wallapop.es CLI + MCP + Python library. Not affiliated with
27
+ Wallapop. Respect ToS and rate limits.
20
28
 
21
29
  ### What this unlocks
22
30
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "walla-cli"
3
- version = "0.1.8"
3
+ version = "0.1.9"
4
4
  description = "Wallapop.es CLI - look · offer · talk · sell for humans and agents"
5
5
  authors = [{ name = "Dr. Berend Gort", email = "berend.gort@gmail.com" }]
6
6
  readme = "README.md"
@@ -14,6 +14,7 @@ from walla.account.sell_wire import (
14
14
  build_item_body,
15
15
  missing_sell_fields,
16
16
  )
17
+ from walla.core.exceptions import WallaHTTPError
17
18
 
18
19
  ROOT = Path(__file__).resolve().parents[1]
19
20
  FIXTURE = ROOT / "fixtures" / "sell_item_request.json"
@@ -130,6 +131,9 @@ def test_publish_listing_mocked(tmp_path: Path) -> None:
130
131
  {"step": {"id": "listing"}},
131
132
  ]
132
133
  fake_http.get.return_value = {}
134
+ listing = MagicMock()
135
+ listing.url = "https://es.wallapop.com/item/test-item-99"
136
+ listing.web_slug = "test-item-99"
133
137
  with (
134
138
  patch("walla.account.sell._auth_client", return_value=fake_http),
135
139
  patch("walla.account.sell.upload_pictures", return_value=1),
@@ -137,6 +141,7 @@ def test_publish_listing_mocked(tmp_path: Path) -> None:
137
141
  "walla.account.sell.create_listing",
138
142
  return_value={"id": "abc123", "flags": {}},
139
143
  ),
144
+ patch("walla.account.sell.get_item", return_value=listing),
140
145
  ):
141
146
  out = publish_listing(
142
147
  [photo],
@@ -150,6 +155,7 @@ def test_publish_listing_mocked(tmp_path: Path) -> None:
150
155
  )
151
156
  assert out["id"] == "abc123"
152
157
  assert out["photos"] == 1
158
+ assert out["url"] == "https://es.wallapop.com/item/test-item-99"
153
159
  assert fake_http.post.call_count == 5
154
160
 
155
161
 
@@ -217,6 +223,9 @@ def test_publish_listing_two_photos(tmp_path: Path) -> None:
217
223
  fake_resp.text = ""
218
224
  fake_session = MagicMock()
219
225
  fake_session.request.return_value = fake_resp
226
+ listing = MagicMock()
227
+ listing.url = "https://es.wallapop.com/item/test-item-99"
228
+ listing.web_slug = "test-item-99"
220
229
  with (
221
230
  patch("walla.account.sell._auth_client", return_value=fake_http),
222
231
  patch("walla.account.sell.upload_pictures", return_value=2),
@@ -224,6 +233,7 @@ def test_publish_listing_two_photos(tmp_path: Path) -> None:
224
233
  "walla.account.sell.create_listing",
225
234
  return_value={"id": "abc123", "flags": {}},
226
235
  ),
236
+ patch("walla.account.sell.get_item", return_value=listing),
227
237
  patch("walla.account.sell._auth_headers", return_value={}),
228
238
  patch("walla.account.sell.requests.Session", return_value=fake_session),
229
239
  patch("walla.account.sell.CurlMime") as mime_cls,
@@ -243,9 +253,27 @@ def test_publish_listing_two_photos(tmp_path: Path) -> None:
243
253
  weight_kg=2,
244
254
  )
245
255
  assert out["photos"] == 2
256
+ assert out["url"] == "https://es.wallapop.com/item/test-item-99"
246
257
  fake_session.request.assert_called()
247
258
 
248
259
 
260
+ def test_public_item_url_uses_slug_not_hash() -> None:
261
+ from walla.account.sell import _public_item_url
262
+
263
+ listing = MagicMock()
264
+ listing.url = ""
265
+ listing.web_slug = "escritorio-madera-1309402890"
266
+ with patch("walla.account.sell.get_item", return_value=listing):
267
+ assert _public_item_url("x6qq0n82pe6y").endswith(
268
+ "/item/escritorio-madera-1309402890"
269
+ )
270
+ with patch(
271
+ "walla.account.sell.get_item",
272
+ side_effect=WallaHTTPError("down", status_code=500),
273
+ ):
274
+ assert _public_item_url("x6qq0n82pe6y").endswith("/item/x6qq0n82pe6y")
275
+
276
+
249
277
  def test_assert_item_body_errors() -> None:
250
278
  with pytest.raises(ValueError, match="missing"):
251
279
  assert_item_body({})