walla-cli 0.1.6__tar.gz → 0.1.8__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.
- {walla_cli-0.1.6 → walla_cli-0.1.8}/AGENTS.md +5 -1
- {walla_cli-0.1.6 → walla_cli-0.1.8}/PKG-INFO +32 -32
- {walla_cli-0.1.6 → walla_cli-0.1.8}/README.md +30 -30
- {walla_cli-0.1.6 → walla_cli-0.1.8}/docs/WIRE.md +33 -5
- {walla_cli-0.1.6 → walla_cli-0.1.8}/docs/objective_function.md +1 -1
- walla_cli-0.1.8/fixtures/offer_buyer_request.json +19 -0
- walla_cli-0.1.8/fixtures/sell_item_request.json +51 -0
- walla_cli-0.1.8/fixtures/wire_contracts.json +230 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/pyproject.toml +2 -2
- walla_cli-0.1.8/scripts/check_code_quality.py +296 -0
- walla_cli-0.1.8/skills/walla/SKILL.md +34 -0
- walla_cli-0.1.8/skills/walla-buy/SKILL.md +35 -0
- walla_cli-0.1.8/skills/walla-sell/SKILL.md +41 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_account.py +16 -19
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_account_more.py +28 -9
- walla_cli-0.1.8/tests/test_chat_open.py +67 -0
- walla_cli-0.1.8/tests/test_desk.py +196 -0
- walla_cli-0.1.8/tests/test_live.py +159 -0
- walla_cli-0.1.8/tests/test_regressions.py +132 -0
- walla_cli-0.1.8/tests/test_sell.py +306 -0
- walla_cli-0.1.8/tests/test_wire_contracts.py +382 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/__init__.py +1 -1
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/account/actions.py +10 -11
- walla_cli-0.1.8/walla/account/chat.py +136 -0
- walla_cli-0.1.8/walla/account/desk.py +230 -0
- walla_cli-0.1.8/walla/account/inbox.py +151 -0
- walla_cli-0.1.8/walla/account/offer_wire.py +49 -0
- walla_cli-0.1.8/walla/account/sell.py +220 -0
- walla_cli-0.1.8/walla/account/sell_wire.py +144 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/cli/cmd_account.py +24 -2
- walla_cli-0.1.8/walla/cli/cmd_desk.py +37 -0
- walla_cli-0.1.8/walla/cli/cmd_sell.py +130 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/cli/main.py +4 -2
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/core/instruct.py +41 -18
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/core/path.py +5 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/http/headers.py +5 -2
- walla_cli-0.1.8/walla/hunter/converge.py +221 -0
- walla_cli-0.1.8/walla/hunter/pursuits.py +70 -0
- walla_cli-0.1.8/walla/hunter/stack.py +55 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/mcp/server.py +11 -0
- walla_cli-0.1.6/scripts/check_code_quality.py +0 -124
- walla_cli-0.1.6/skills/walla/SKILL.md +0 -42
- walla_cli-0.1.6/tests/test_live.py +0 -68
- walla_cli-0.1.6/walla/account/inbox.py +0 -147
- {walla_cli-0.1.6 → walla_cli-0.1.8}/.cursor/rules/quality.mdc +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/.github/workflows/ci.yml +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/.gitignore +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/LICENSE +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/docs/assets/walla-github-banner.jpg +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/docs/code_quality.md +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/docs/data_engineering_standards.md +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/docs/mcp.md +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/docs/rate_limit_analysis.md +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/fixtures/categories.json +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/fixtures/item_detail.json +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/fixtures/search_bicicleta.json +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/scripts/dogfood_run.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/scripts/probe_rate.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_catch.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_cli_json.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_core.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_edges.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_export.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_http_more.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_negotiate.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_parsers.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_polite.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_search_api.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_session_store.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_shortlist.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/tests/test_verdict.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/account/__init__.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/account/cookies.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/account/login.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/account/prompt.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/account/session_store.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/cli/__init__.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/cli/banner.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/cli/catch.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/cli/cmd_look.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/cli/cmd_ops.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/cli/console.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/core/__init__.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/core/envelope.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/core/errors.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/core/exceptions.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/core/human_fix.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/http/__init__.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/http/client.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/http/polite.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/hunter/__init__.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/hunter/export.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/hunter/export_pdf.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/hunter/negotiate.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/hunter/profile_store.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/hunter/shortlist.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/hunter/verdict.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/hunter/watches.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/mcp/__init__.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/mcp/_entry.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/models/__init__.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/models/account.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/models/listing.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/models/profile.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/search/__init__.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/search/api.py +0 -0
- {walla_cli-0.1.6 → walla_cli-0.1.8}/walla/search/parse.py +0 -0
|
@@ -57,7 +57,11 @@ Ranks: models 10 → exceptions 15 → http 20 → core 30 → search/account/hu
|
|
|
57
57
|
|
|
58
58
|
- **Do** keep files <= 250 LOC.
|
|
59
59
|
- **Do** fail closed; typed `error_type`.
|
|
60
|
-
- **Do** require `--yes` for say/offer.
|
|
60
|
+
- **Do** require `--yes` for bare say/offer/sell/unsell. `pursue` and `desk` send negotiation chat without a per-message prompt. Never pay.
|
|
61
61
|
- **Don't** commit session.json, cookies, or tokens.
|
|
62
62
|
- **Don't** invent wire shapes without a fixture.
|
|
63
|
+
- **Don't** post buyer offers as `{item_id, amount, currency}` (HTTP 400).
|
|
64
|
+
Use `walla.account.offer_wire.build_offer_body` / `fixtures/offer_buyer_request.json`.
|
|
65
|
+
- **Don't** create listings without `Accept: application/vnd.upload-v2+json`
|
|
66
|
+
(HTTP 405). Use `walla sell` / `fixtures/sell_item_request.json`.
|
|
63
67
|
- **Don't** require `.env` credentials; login is cookie paste.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: walla-cli
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Wallapop.es CLI - look · offer · talk for humans and agents
|
|
3
|
+
Version: 0.1.8
|
|
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
|
|
7
7
|
Project-URL: Issues, https://github.com/berendgort/wallapop-cli/issues
|
|
@@ -51,14 +51,14 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
██║███╗██║ ██╔══██║ ██║ ██║ ██╔══██║ ╚════╝ ██║ ██║ ██║
|
|
52
52
|
╚███╔███╔╝ ██║ ██║ ███████╗ ███████╗ ██║ ██║ ╚██████╗ ███████╗ ██║
|
|
53
53
|
╚══╝╚══╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
|
|
54
|
-
look · offer · talk
|
|
54
|
+
look · offer · talk · sell
|
|
55
55
|
Dr. Berend Gort · www.berendgort.dev
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|

|
|
59
59
|
|
|
60
60
|
**walla** is an unofficial Wallapop.es **CLI + MCP + Python library** for Cursor
|
|
61
|
-
agents and humans: search listings,
|
|
61
|
+
agents and humans: search listings, negotiate, publish a listing, and follow the inbox.
|
|
62
62
|
|
|
63
63
|
Not affiliated with Wallapop. Respect ToS and rate limits.
|
|
64
64
|
|
|
@@ -67,27 +67,26 @@ Not affiliated with Wallapop. Respect ToS and rate limits.
|
|
|
67
67
|
You tell Cursor: *“Find me a used kite near Barcelona under €400, message
|
|
68
68
|
the seller, and offer if it’s a GRAB.”*
|
|
69
69
|
|
|
70
|
-
The agent runs `walla` — ranks listings **GRAB / LOOK / PASS**,
|
|
71
|
-
|
|
70
|
+
The agent runs `walla` — ranks listings **GRAB / LOOK / PASS**, messages the
|
|
71
|
+
shortlist, and follows replies until a price is agreed. You close the deal
|
|
72
|
+
in the Wallapop app.
|
|
72
73
|
|
|
73
74
|
```bash
|
|
74
75
|
walla setup --lat 41.39 --lon 2.17 --km 30 --label Barcelona \
|
|
75
76
|
--budget 400 --aggression fair --must "kite cabrinha"
|
|
76
|
-
walla search "tabla kite Cabrinha" --max 40 --json
|
|
77
|
-
# agent: GRAB · Cabrinha Moto 10m · €320 · 4 km
|
|
78
|
-
# https://es.wallapop.com/item/...
|
|
79
|
-
|
|
80
|
-
walla search "…" --export md,csv,html,pdf --out ./shortlist --json
|
|
81
|
-
# or later: walla export --format md,csv,html,pdf --out ./shortlist --json
|
|
82
|
-
|
|
83
77
|
walla login # paste session cookie once
|
|
84
|
-
walla
|
|
85
|
-
walla
|
|
86
|
-
walla offer <id> --eur 280 --yes # only after you approve; never pays
|
|
78
|
+
walla pursue "tabla kite Cabrinha" --json
|
|
79
|
+
walla desk --json # one inbox read; replies; stack.best
|
|
87
80
|
```
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
Sell is the same idea: answer the photo questions, publish once, then desk
|
|
83
|
+
talks to buyers.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
walla sell ./shot.jpg --json
|
|
87
|
+
walla sell ./shot.jpg --title "…" --desc "…" --eur 40 \
|
|
88
|
+
--category 10105 --root 12579 --floor 35 --yes --json
|
|
89
|
+
```
|
|
91
90
|
|
|
92
91
|
**Quality bar (enforced in CI):**
|
|
93
92
|
|
|
@@ -139,26 +138,25 @@ Optional MCP:
|
|
|
139
138
|
| Ask | Command |
|
|
140
139
|
|-----|---------|
|
|
141
140
|
| Mandate | `walla setup --budget 400 --aggression fair --must "kite"` |
|
|
142
|
-
| Look | `walla search "tabla kite" --max 40 --json`
|
|
143
|
-
|
|
|
144
|
-
|
|
|
145
|
-
|
|
|
141
|
+
| Look | `walla search "tabla kite" --max 40 --json` |
|
|
142
|
+
| Buy | `walla pursue "<query>" --json` then `walla desk --json` |
|
|
143
|
+
| Sell | `walla sell <photos> --json`, then the same command with `--yes` |
|
|
144
|
+
| Close | Human pays or accepts in the Wallapop app |
|
|
146
145
|
|
|
147
|
-
### HITL
|
|
146
|
+
### HITL
|
|
148
147
|
|
|
149
148
|
| Step | Agent alone | Ask human |
|
|
150
149
|
|------|-------------|-----------|
|
|
151
150
|
| Search inside mandate | yes | if query vague or budget unset |
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
| `offer --yes` | never; refuse if EUR > budget | yes to that exact EUR |
|
|
156
|
-
| Pay / buy / reserve | **never** | human finishes in the Wallapop app |
|
|
151
|
+
| `pursue` / `desk` | yes, including the chat messages | never for each text |
|
|
152
|
+
| Publish or delete a listing | no | `--yes` once the questions are answered |
|
|
153
|
+
| Pay / accept / meet | **never** | human finishes in the Wallapop app |
|
|
157
154
|
|
|
158
155
|
Rules:
|
|
159
156
|
|
|
160
|
-
-
|
|
161
|
-
-
|
|
157
|
+
- `pursue` and `desk` send negotiation chat. Do not ask the human to send.
|
|
158
|
+
- Bare `say` / `offer` / `sell` / `unsell` still need `--yes`.
|
|
159
|
+
- `--yes` never completes a purchase or accepts a sale.
|
|
162
160
|
- If they only asked to look, do not send.
|
|
163
161
|
- Ambiguous match or multiple GRABs: ask once with candidates.
|
|
164
162
|
- Always present Wallapop shortlists ranked best-first with a markdown link on every item URL.
|
|
@@ -187,9 +185,11 @@ walla doctor --json
|
|
|
187
185
|
| `walla search` / `item` / `categories` | Look |
|
|
188
186
|
| `walla watch add\|list\|check` | Local saved searches |
|
|
189
187
|
| `walla login` / `logout` / `whoami` | Session (paste cookie; optional `--password`) |
|
|
188
|
+
| `walla pursue` / `desk` | Message a shortlist and follow the inbox |
|
|
190
189
|
| `walla negotiate` | Win-win Spanish draft (never sends) |
|
|
191
|
-
| `walla inbox` / `thread` / `say` | Talk |
|
|
192
|
-
| `walla offer` |
|
|
190
|
+
| `walla inbox` / `thread` / `say` | Talk (`say` needs `--yes`) |
|
|
191
|
+
| `walla offer` | Formal offer (needs `--yes`) |
|
|
192
|
+
| `walla sell` / `unsell` | Publish or delete a listing (`--yes`) |
|
|
193
193
|
| `walla fav list\|add\|rm` | Favorites |
|
|
194
194
|
| `walla instruct` / `doctor` | Agent recipe + health |
|
|
195
195
|
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
██║███╗██║ ██╔══██║ ██║ ██║ ██╔══██║ ╚════╝ ██║ ██║ ██║
|
|
8
8
|
╚███╔███╔╝ ██║ ██║ ███████╗ ███████╗ ██║ ██║ ╚██████╗ ███████╗ ██║
|
|
9
9
|
╚══╝╚══╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
|
|
10
|
-
look · offer · talk
|
|
10
|
+
look · offer · talk · sell
|
|
11
11
|
Dr. Berend Gort · www.berendgort.dev
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|

|
|
15
15
|
|
|
16
16
|
**walla** is an unofficial Wallapop.es **CLI + MCP + Python library** for Cursor
|
|
17
|
-
agents and humans: search listings,
|
|
17
|
+
agents and humans: search listings, negotiate, publish a listing, and follow the inbox.
|
|
18
18
|
|
|
19
19
|
Not affiliated with Wallapop. Respect ToS and rate limits.
|
|
20
20
|
|
|
@@ -23,27 +23,26 @@ Not affiliated with Wallapop. Respect ToS and rate limits.
|
|
|
23
23
|
You tell Cursor: *“Find me a used kite near Barcelona under €400, message
|
|
24
24
|
the seller, and offer if it’s a GRAB.”*
|
|
25
25
|
|
|
26
|
-
The agent runs `walla` — ranks listings **GRAB / LOOK / PASS**,
|
|
27
|
-
|
|
26
|
+
The agent runs `walla` — ranks listings **GRAB / LOOK / PASS**, messages the
|
|
27
|
+
shortlist, and follows replies until a price is agreed. You close the deal
|
|
28
|
+
in the Wallapop app.
|
|
28
29
|
|
|
29
30
|
```bash
|
|
30
31
|
walla setup --lat 41.39 --lon 2.17 --km 30 --label Barcelona \
|
|
31
32
|
--budget 400 --aggression fair --must "kite cabrinha"
|
|
32
|
-
walla search "tabla kite Cabrinha" --max 40 --json
|
|
33
|
-
# agent: GRAB · Cabrinha Moto 10m · €320 · 4 km
|
|
34
|
-
# https://es.wallapop.com/item/...
|
|
35
|
-
|
|
36
|
-
walla search "…" --export md,csv,html,pdf --out ./shortlist --json
|
|
37
|
-
# or later: walla export --format md,csv,html,pdf --out ./shortlist --json
|
|
38
|
-
|
|
39
33
|
walla login # paste session cookie once
|
|
40
|
-
walla
|
|
41
|
-
walla
|
|
42
|
-
walla offer <id> --eur 280 --yes # only after you approve; never pays
|
|
34
|
+
walla pursue "tabla kite Cabrinha" --json
|
|
35
|
+
walla desk --json # one inbox read; replies; stack.best
|
|
43
36
|
```
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
Sell is the same idea: answer the photo questions, publish once, then desk
|
|
39
|
+
talks to buyers.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
walla sell ./shot.jpg --json
|
|
43
|
+
walla sell ./shot.jpg --title "…" --desc "…" --eur 40 \
|
|
44
|
+
--category 10105 --root 12579 --floor 35 --yes --json
|
|
45
|
+
```
|
|
47
46
|
|
|
48
47
|
**Quality bar (enforced in CI):**
|
|
49
48
|
|
|
@@ -95,26 +94,25 @@ Optional MCP:
|
|
|
95
94
|
| Ask | Command |
|
|
96
95
|
|-----|---------|
|
|
97
96
|
| Mandate | `walla setup --budget 400 --aggression fair --must "kite"` |
|
|
98
|
-
| Look | `walla search "tabla kite" --max 40 --json`
|
|
99
|
-
|
|
|
100
|
-
|
|
|
101
|
-
|
|
|
97
|
+
| Look | `walla search "tabla kite" --max 40 --json` |
|
|
98
|
+
| Buy | `walla pursue "<query>" --json` then `walla desk --json` |
|
|
99
|
+
| Sell | `walla sell <photos> --json`, then the same command with `--yes` |
|
|
100
|
+
| Close | Human pays or accepts in the Wallapop app |
|
|
102
101
|
|
|
103
|
-
### HITL
|
|
102
|
+
### HITL
|
|
104
103
|
|
|
105
104
|
| Step | Agent alone | Ask human |
|
|
106
105
|
|------|-------------|-----------|
|
|
107
106
|
| Search inside mandate | yes | if query vague or budget unset |
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
| `offer --yes` | never; refuse if EUR > budget | yes to that exact EUR |
|
|
112
|
-
| Pay / buy / reserve | **never** | human finishes in the Wallapop app |
|
|
107
|
+
| `pursue` / `desk` | yes, including the chat messages | never for each text |
|
|
108
|
+
| Publish or delete a listing | no | `--yes` once the questions are answered |
|
|
109
|
+
| Pay / accept / meet | **never** | human finishes in the Wallapop app |
|
|
113
110
|
|
|
114
111
|
Rules:
|
|
115
112
|
|
|
116
|
-
-
|
|
117
|
-
-
|
|
113
|
+
- `pursue` and `desk` send negotiation chat. Do not ask the human to send.
|
|
114
|
+
- Bare `say` / `offer` / `sell` / `unsell` still need `--yes`.
|
|
115
|
+
- `--yes` never completes a purchase or accepts a sale.
|
|
118
116
|
- If they only asked to look, do not send.
|
|
119
117
|
- Ambiguous match or multiple GRABs: ask once with candidates.
|
|
120
118
|
- Always present Wallapop shortlists ranked best-first with a markdown link on every item URL.
|
|
@@ -143,9 +141,11 @@ walla doctor --json
|
|
|
143
141
|
| `walla search` / `item` / `categories` | Look |
|
|
144
142
|
| `walla watch add\|list\|check` | Local saved searches |
|
|
145
143
|
| `walla login` / `logout` / `whoami` | Session (paste cookie; optional `--password`) |
|
|
144
|
+
| `walla pursue` / `desk` | Message a shortlist and follow the inbox |
|
|
146
145
|
| `walla negotiate` | Win-win Spanish draft (never sends) |
|
|
147
|
-
| `walla inbox` / `thread` / `say` | Talk |
|
|
148
|
-
| `walla offer` |
|
|
146
|
+
| `walla inbox` / `thread` / `say` | Talk (`say` needs `--yes`) |
|
|
147
|
+
| `walla offer` | Formal offer (needs `--yes`) |
|
|
148
|
+
| `walla sell` / `unsell` | Publish or delete a listing (`--yes`) |
|
|
149
149
|
| `walla fav list\|add\|rm` | Favorites |
|
|
150
150
|
| `walla instruct` / `doctor` | Agent recipe + health |
|
|
151
151
|
|
|
@@ -44,15 +44,43 @@ login ever succeeds.
|
|
|
44
44
|
|
|
45
45
|
## Messaging / offers (auth)
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- Inbox: `GET /api/v3/conversations
|
|
50
|
-
-
|
|
47
|
+
- Open chat (listing **Chat** button): `POST /api/v3/conversations`
|
|
48
|
+
body `{ "item_id": "<hash>" }` → `{ conversation_id, item_id, other_user_id, channel }`.
|
|
49
|
+
- Inbox: `GET /bff/messaging/inbox?page_size=&max_messages=` (not `/api/v3/conversations`, 405).
|
|
50
|
+
- Send text: PubNub publish after `GET /api/v3/instant-messaging/token`
|
|
51
|
+
(REST `…/messages` is 404). See `walla.account.chat.publish_text`.
|
|
51
52
|
- Offers: `POST /api/v3/delivery/buyer/offers` with
|
|
52
53
|
`{offer_id, offer_price_amount, offer_price_currency, item_ids}`.
|
|
53
|
-
|
|
54
|
+
Requires header `X-AppVersion` (without it Wallapop returns a generic HTTP 400).
|
|
55
|
+
Fixture: `fixtures/offer_buyer_request.json`. The old
|
|
56
|
+
`{item_id, amount, currency}` body returns **HTTP 400** — forbidden.
|
|
57
|
+
Some sellers disable offers → **409** `offer creation not allowed`; fall back to chat text.
|
|
54
58
|
- Favorites: `/api/v3/users/me/favorites`, `/api/v3/items/{id}/favorite`
|
|
55
59
|
|
|
60
|
+
## Sell / Vender (auth)
|
|
61
|
+
|
|
62
|
+
Browser: `https://es.wallapop.com/app/catalog/upload/consumer-goods`.
|
|
63
|
+
|
|
64
|
+
1. `POST /api/v3/steps` `{mode:{action:"upload",id:<uuid>}}` → step `title`
|
|
65
|
+
2. `POST /api/v3/steps` `current_step=title` draft `{title}` → `photo`
|
|
66
|
+
3. `POST /api/v3/upload/{upload_id}/pictures` multipart `file` → **204**
|
|
67
|
+
4. `POST /api/v3/steps` `current_step=photo` → `category`
|
|
68
|
+
5. `POST /api/v3/steps` `current_step=category` draft
|
|
69
|
+
`{title, category_leaf_id, root_category_id}` (**ids as strings**) → `loading`
|
|
70
|
+
6. Poll `GET /api/v3/suggested-item-data/{upload_id}` (404 then empty 200)
|
|
71
|
+
7. `POST /api/v3/steps` `current_step=loading` → `listing`
|
|
72
|
+
8. Create: `POST /api/v3/items` multipart `image` + `item` (JSON string).
|
|
73
|
+
Header **`Accept: application/vnd.upload-v2+json`** (without it → HTTP 405).
|
|
74
|
+
Extra photos: `POST /api/v3/items/{id}/picture2` multipart `image` + `order`.
|
|
75
|
+
9. Delete: `DELETE /api/v3/items/{id}` → 204.
|
|
76
|
+
|
|
77
|
+
Fixture: `fixtures/sell_item_request.json` / `walla.account.sell_wire.build_item_body`.
|
|
78
|
+
|
|
79
|
+
Every CLI call (method, path, auth, body) is listed in `fixtures/wire_contracts.json`.
|
|
80
|
+
`scripts/check_code_quality.py` fails if `walla/account`, `walla/search`, or `walla/http`
|
|
81
|
+
grows a `/api/` or `/bff/` literal that the catalog does not list.
|
|
82
|
+
CLI: `walla sell photo.jpg … --yes` (draft without `--yes`).
|
|
83
|
+
|
|
56
84
|
Writes that fail closed as `error_type: unsupported` until fixtures exist.
|
|
57
85
|
|
|
58
86
|
## Rate limits
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
1. Find the listing (search + verdict GRAB / LOOK / PASS).
|
|
4
4
|
2. Do not miss a reply (inbox / thread).
|
|
5
|
-
3.
|
|
5
|
+
3. `walla say` / `walla offer` still need `--yes`. `walla pursue` and `walla desk` send negotiation chat on purpose. Never pay.
|
|
6
6
|
4. Never leak session cookies or bearer tokens.
|
|
7
7
|
5. Stay polite on rate limits.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"path": "/api/v3/delivery/buyer/offers",
|
|
4
|
+
"method": "POST",
|
|
5
|
+
"required_keys": [
|
|
6
|
+
"offer_id",
|
|
7
|
+
"offer_price_amount",
|
|
8
|
+
"offer_price_currency",
|
|
9
|
+
"item_ids"
|
|
10
|
+
],
|
|
11
|
+
"forbidden_keys": ["item_id", "amount", "currency"],
|
|
12
|
+
"example": {
|
|
13
|
+
"offer_id": "00000000-0000-4000-8000-000000000001",
|
|
14
|
+
"offer_price_amount": 650,
|
|
15
|
+
"offer_price_currency": "EUR",
|
|
16
|
+
"item_ids": ["wzv48vgrmdzl"]
|
|
17
|
+
},
|
|
18
|
+
"notes": "Old {item_id, amount, currency} returns HTTP 400. Never invent without this fixture."
|
|
19
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"endpoint": "POST /api/v3/items",
|
|
3
|
+
"content_type": "multipart/form-data",
|
|
4
|
+
"accept": "application/vnd.upload-v2+json",
|
|
5
|
+
"fields": {
|
|
6
|
+
"image": "first JPEG/PNG/WebP file",
|
|
7
|
+
"item": "JSON string (see example)"
|
|
8
|
+
},
|
|
9
|
+
"steps": [
|
|
10
|
+
"POST /api/v3/steps mode.upload",
|
|
11
|
+
"POST /api/v3/steps current_step=title draft.title",
|
|
12
|
+
"POST /api/v3/upload/{upload_id}/pictures multipart file",
|
|
13
|
+
"POST /api/v3/steps current_step=photo",
|
|
14
|
+
"POST /api/v3/steps current_step=category draft.category_leaf_id+root_category_id (strings)",
|
|
15
|
+
"GET /api/v3/suggested-item-data/{upload_id} until 200",
|
|
16
|
+
"POST /api/v3/steps current_step=loading",
|
|
17
|
+
"POST /api/v3/items Accept upload-v2"
|
|
18
|
+
],
|
|
19
|
+
"delete": "DELETE /api/v3/items/{id}",
|
|
20
|
+
"example": {
|
|
21
|
+
"attributes": {
|
|
22
|
+
"title": "TEST walla CLI - borrar",
|
|
23
|
+
"description": "Anuncio de prueba. NO COMPRAR.",
|
|
24
|
+
"price_amount": 9,
|
|
25
|
+
"condition": "good"
|
|
26
|
+
},
|
|
27
|
+
"category_leaf_id": "10105",
|
|
28
|
+
"apply_discount": false,
|
|
29
|
+
"location": {
|
|
30
|
+
"latitude": 41.3874,
|
|
31
|
+
"longitude": 2.1686,
|
|
32
|
+
"approximated": true
|
|
33
|
+
},
|
|
34
|
+
"delivery": {
|
|
35
|
+
"allowed_by_user": false,
|
|
36
|
+
"max_weight_kg": null,
|
|
37
|
+
"cost_configuration_id": null
|
|
38
|
+
},
|
|
39
|
+
"upload_id": "00000000-0000-0000-0000-000000000000"
|
|
40
|
+
},
|
|
41
|
+
"required_keys": [
|
|
42
|
+
"attributes",
|
|
43
|
+
"category_leaf_id",
|
|
44
|
+
"apply_discount",
|
|
45
|
+
"location",
|
|
46
|
+
"delivery",
|
|
47
|
+
"upload_id"
|
|
48
|
+
],
|
|
49
|
+
"attribute_required_keys": ["title", "description", "price_amount"],
|
|
50
|
+
"conditions": ["as_good_as_new", "good", "fair", "has_given_it_all"]
|
|
51
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
{
|
|
2
|
+
"shared_headers": ["X-DeviceOS", "X-AppVersion", "Accept-Language"],
|
|
3
|
+
"requests": [
|
|
4
|
+
{
|
|
5
|
+
"id": "search",
|
|
6
|
+
"cli": "search",
|
|
7
|
+
"method": "GET",
|
|
8
|
+
"path": "/api/v3/search",
|
|
9
|
+
"auth": false,
|
|
10
|
+
"ok_status": [200]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "item",
|
|
14
|
+
"cli": "item",
|
|
15
|
+
"method": "GET",
|
|
16
|
+
"path": "/api/v3/items/{id}",
|
|
17
|
+
"auth": false,
|
|
18
|
+
"ok_status": [200]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "categories",
|
|
22
|
+
"cli": "categories",
|
|
23
|
+
"method": "GET",
|
|
24
|
+
"path": "/api/v3/categories",
|
|
25
|
+
"auth": false,
|
|
26
|
+
"ok_status": [200]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "session_mint",
|
|
30
|
+
"cli": "login",
|
|
31
|
+
"method": "GET",
|
|
32
|
+
"path": "/api/auth/session",
|
|
33
|
+
"auth": false,
|
|
34
|
+
"ok_status": [200]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "password_login",
|
|
38
|
+
"cli": "login",
|
|
39
|
+
"method": "POST",
|
|
40
|
+
"path": "/api/v3/access/login",
|
|
41
|
+
"auth": false,
|
|
42
|
+
"body_keys": ["username", "password"],
|
|
43
|
+
"ok_status": [200]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "refresh",
|
|
47
|
+
"cli": "login",
|
|
48
|
+
"method": "POST",
|
|
49
|
+
"path": "/api/v3/access/refresh",
|
|
50
|
+
"auth": false,
|
|
51
|
+
"body_keys": ["refresh_token"],
|
|
52
|
+
"ok_status": [200]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "whoami",
|
|
56
|
+
"cli": "whoami",
|
|
57
|
+
"method": "GET",
|
|
58
|
+
"path": "/api/v3/users/me",
|
|
59
|
+
"auth": true,
|
|
60
|
+
"ok_status": [200, 404]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "inbox",
|
|
64
|
+
"cli": "inbox",
|
|
65
|
+
"method": "GET",
|
|
66
|
+
"path": "/bff/messaging/inbox",
|
|
67
|
+
"auth": true,
|
|
68
|
+
"ok_status": [200],
|
|
69
|
+
"known_bad": [{"status": 405, "cause": "GET /api/v3/conversations"}]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "chat_open",
|
|
73
|
+
"cli": "chat",
|
|
74
|
+
"method": "POST",
|
|
75
|
+
"path": "/api/v3/conversations",
|
|
76
|
+
"auth": true,
|
|
77
|
+
"body_keys": ["item_id"],
|
|
78
|
+
"ok_status": [200, 201]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "im_token",
|
|
82
|
+
"cli": "say",
|
|
83
|
+
"method": "GET",
|
|
84
|
+
"path": "/api/v3/instant-messaging/token",
|
|
85
|
+
"auth": true,
|
|
86
|
+
"ok_status": [200]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"id": "say_pubnub",
|
|
90
|
+
"cli": "say",
|
|
91
|
+
"method": "GET",
|
|
92
|
+
"path": "https://ps2.pndsn.com/publish/",
|
|
93
|
+
"needle": "ps2.pndsn.com",
|
|
94
|
+
"auth": false,
|
|
95
|
+
"ok_status": [200],
|
|
96
|
+
"known_bad": [{"status": 404, "cause": "REST messages"}]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "offer",
|
|
100
|
+
"cli": "offer",
|
|
101
|
+
"method": "POST",
|
|
102
|
+
"path": "/api/v3/delivery/buyer/offers",
|
|
103
|
+
"auth": true,
|
|
104
|
+
"body_keys": [
|
|
105
|
+
"offer_id",
|
|
106
|
+
"offer_price_amount",
|
|
107
|
+
"offer_price_currency",
|
|
108
|
+
"item_ids"
|
|
109
|
+
],
|
|
110
|
+
"forbidden_body_keys": ["item_id", "amount", "currency"],
|
|
111
|
+
"ok_status": [200, 201],
|
|
112
|
+
"known_bad": [
|
|
113
|
+
{"status": 400, "cause": "wrong body or missing X-AppVersion"},
|
|
114
|
+
{"status": 409, "cause": "seller disabled offers"}
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"id": "favorites",
|
|
119
|
+
"cli": "fav list",
|
|
120
|
+
"method": "GET",
|
|
121
|
+
"path": "/api/v3/users/me/favorites",
|
|
122
|
+
"auth": true,
|
|
123
|
+
"ok_status": [200]
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"id": "favorites_fallback",
|
|
127
|
+
"cli": "fav list",
|
|
128
|
+
"method": "GET",
|
|
129
|
+
"path": "/api/v3/favorites",
|
|
130
|
+
"auth": true,
|
|
131
|
+
"ok_status": [200]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"id": "favorite_add",
|
|
135
|
+
"cli": "fav add",
|
|
136
|
+
"method": "POST",
|
|
137
|
+
"path": "/api/v3/items/{id}/favorite",
|
|
138
|
+
"needle": "/favorite",
|
|
139
|
+
"auth": true,
|
|
140
|
+
"ok_status": [200, 201, 204]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"id": "favorite_rm",
|
|
144
|
+
"cli": "fav rm",
|
|
145
|
+
"method": "DELETE",
|
|
146
|
+
"path": "/api/v3/items/{id}/favorite",
|
|
147
|
+
"needle": "/favorite",
|
|
148
|
+
"auth": true,
|
|
149
|
+
"ok_status": [200, 204]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "sell_steps",
|
|
153
|
+
"cli": "sell",
|
|
154
|
+
"method": "POST",
|
|
155
|
+
"path": "/api/v3/steps",
|
|
156
|
+
"auth": true,
|
|
157
|
+
"ok_status": [200]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"id": "sell_pictures",
|
|
161
|
+
"cli": "sell",
|
|
162
|
+
"method": "POST",
|
|
163
|
+
"path": "/api/v3/upload/{id}/pictures",
|
|
164
|
+
"needle": "/api/v3/upload/",
|
|
165
|
+
"auth": true,
|
|
166
|
+
"multipart": ["file"],
|
|
167
|
+
"ok_status": [200, 204]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"id": "sell_suggest",
|
|
171
|
+
"cli": "sell",
|
|
172
|
+
"method": "GET",
|
|
173
|
+
"path": "/api/v3/suggested-item-data/{id}",
|
|
174
|
+
"needle": "/suggested-item-data/",
|
|
175
|
+
"auth": true,
|
|
176
|
+
"ok_status": [200, 404]
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"id": "sell_create",
|
|
180
|
+
"cli": "sell",
|
|
181
|
+
"method": "POST",
|
|
182
|
+
"path": "/api/v3/items",
|
|
183
|
+
"needle": "/api/v3/items",
|
|
184
|
+
"auth": true,
|
|
185
|
+
"accept": "application/vnd.upload-v2+json",
|
|
186
|
+
"multipart": ["image", "item"],
|
|
187
|
+
"ok_status": [200, 201],
|
|
188
|
+
"known_bad": [{"status": 405, "cause": "missing upload-v2 Accept"}]
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"id": "sell_picture2",
|
|
192
|
+
"cli": "sell",
|
|
193
|
+
"method": "POST",
|
|
194
|
+
"path": "/api/v3/items/{id}/picture2",
|
|
195
|
+
"needle": "/picture2",
|
|
196
|
+
"auth": true,
|
|
197
|
+
"multipart": ["image", "order"],
|
|
198
|
+
"ok_status": [200, 201, 204]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "unsell",
|
|
202
|
+
"cli": "unsell",
|
|
203
|
+
"method": "DELETE",
|
|
204
|
+
"path": "/api/v3/items/{id}",
|
|
205
|
+
"needle": "/api/v3/items/",
|
|
206
|
+
"auth": true,
|
|
207
|
+
"ok_status": [200, 204]
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"id": "inbox_not_conversations",
|
|
211
|
+
"cli": "inbox",
|
|
212
|
+
"method": "GET",
|
|
213
|
+
"path": "/api/v3/conversations",
|
|
214
|
+
"forbidden": true,
|
|
215
|
+
"auth": true,
|
|
216
|
+
"ok_status": [],
|
|
217
|
+
"known_bad": [{"status": 405, "cause": "inbox is /bff/messaging/inbox"}]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"id": "say_not_rest_messages",
|
|
221
|
+
"cli": "say",
|
|
222
|
+
"method": "POST",
|
|
223
|
+
"path": "/api/v3/instant-messaging/messages",
|
|
224
|
+
"forbidden": true,
|
|
225
|
+
"auth": true,
|
|
226
|
+
"ok_status": [],
|
|
227
|
+
"known_bad": [{"status": 404, "cause": "say uses PubNub publish"}]
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "walla-cli"
|
|
3
|
-
version = "0.1.
|
|
4
|
-
description = "Wallapop.es CLI - look · offer · talk for humans and agents"
|
|
3
|
+
version = "0.1.8"
|
|
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"
|
|
7
7
|
license = { text = "MIT" }
|