travelmaxx 0.1.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.
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: travelmaxx
3
+ Version: 0.1.0
4
+ Summary: Search award flights across 14 mileage programs from your terminal
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://travelmaxx.app
7
+ Project-URL: Documentation, https://travelmaxx.app/cli/docs
8
+ Keywords: awards,flights,miles,points,travel
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: End Users/Desktop
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Utilities
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: httpx>=0.27
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=8.0; extra == "dev"
19
+ Requires-Dist: respx>=0.22; extra == "dev"
20
+
21
+ # travelmaxx
22
+
23
+ Search award flights across 14 mileage programs from your terminal, powered by
24
+ [TravelMaxx](https://travelmaxx.app).
25
+
26
+ ```sh
27
+ curl -fsSL https://travelmaxx.app/cli | sh
28
+ travelmaxx login
29
+ travelmaxx search SFO NRT --date 2026-10-05 --programs ALASKA
30
+ ```
31
+
32
+ ## Install
33
+
34
+ Any of:
35
+
36
+ ```sh
37
+ curl -fsSL https://travelmaxx.app/cli | sh # auto-detects uv/pipx/pip
38
+ uv tool install travelmaxx
39
+ pipx install travelmaxx
40
+ brew install travelmaxx/travelmaxx/travelmaxx # once the tap is live
41
+ ```
42
+
43
+ Requires Python 3.10+ (macOS or Linux), an active TravelMaxx subscription, and
44
+ Roame credentials linked at <https://travelmaxx.app/dashboard/settings>.
45
+
46
+ ## Sign in
47
+
48
+ ```sh
49
+ travelmaxx login # opens your browser; click Approve
50
+ travelmaxx login --no-browser # headless/SSH: 6-digit code at travelmaxx.app/activate
51
+ travelmaxx whoami
52
+ travelmaxx logout
53
+ ```
54
+
55
+ Tokens are stored in `~/.config/travelmaxx/credentials.json` (chmod 600) and
56
+ refresh automatically for 30 days. Your Roame credentials never touch this
57
+ machine — searches run through the TravelMaxx server.
58
+
59
+ ## Search
60
+
61
+ ```sh
62
+ # Single date (always pass --programs when you know them: 40-50x faster)
63
+ travelmaxx search SFO NRT --date 2026-10-05 --programs ALASKA
64
+
65
+ # Business class, 2 passengers, flexible ±2 days
66
+ travelmaxx search SFO CDG --date 2026-09-12 --class BUSINESS \
67
+ --programs ALASKA,UNITED --pax 2 --days-around 2
68
+
69
+ # A whole month in parallel, with a live progress bar
70
+ travelmaxx search SFO NRT --date-range november --programs ALASKA
71
+
72
+ # Explicit range / year / "next year"
73
+ travelmaxx search SFO HEL --date-range 2026-09-12:2026-10-18 --programs ALASKA
74
+ ```
75
+
76
+ Options: `--sort points|duration`, `--limit N`, `--max-wait SECONDS` (5–120;
77
+ results are marked `partial` if the search returns early), `--min-results N`,
78
+ `--max-workers N` (range searches), `--json`, `--output-file FILE`.
79
+
80
+ Programs: `ALASKA, UNITED, DELTA, JETBLUE, LIFEMILES` (fast) ·
81
+ `AMERICAN, QANTAS, EMIRATES, VIRGIN_ATLANTIC, VIRGIN_AUSTRALIA, CLUB_PREMIER,
82
+ ETIHAD, FLYING_BLUE, GOL` (slower).
83
+
84
+ ## Alerts & discovery
85
+
86
+ ```sh
87
+ travelmaxx alerts create SFO NRT --class BUSINESS --max-points 75000
88
+ travelmaxx alerts list
89
+ travelmaxx alerts delete <uuid>
90
+ travelmaxx discover --limit 10
91
+ ```
92
+
93
+ ## JSON output
94
+
95
+ `--json` prints the full API response. Single-date searches return
96
+ `{"fares": [...], "percent_completed": 100, "partial": false}`; job-based
97
+ searches return `{"status", "percent", "fares_count", "fares", ...}`.
98
+ Each fare:
99
+
100
+ ```json
101
+ {
102
+ "departure_date": "2026-10-05",
103
+ "origin": "SFO", "destination": "NRT",
104
+ "mileage_program": "ALASKA",
105
+ "award_points": 75000, "surcharge_usd": 18.1,
106
+ "num_stops": 0, "duration_minutes": 630,
107
+ "cabin_classes": ["BUSINESS"], "operating_airlines": ["JL"],
108
+ "equipment_types": ["77W"], "available_seats": 2, "roame_score": 1684
109
+ }
110
+ ```
111
+
112
+ ## Configuration
113
+
114
+ | Env var | Purpose |
115
+ |---|---|
116
+ | `TRAVELMAXX_API_URL` | Point at a different server (also `--server URL`) |
117
+ | `XDG_CONFIG_HOME` | Relocate the config dir (default `~/.config/travelmaxx`) |
118
+
119
+ ## Development
120
+
121
+ ```sh
122
+ cd cli
123
+ python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
124
+ .venv/bin/pytest
125
+ .venv/bin/travelmaxx --server http://localhost:8000 login
126
+ ```
127
+
128
+ Design doc: `../CLI.md`. Releases: tag `cli-vX.Y.Z` (see
129
+ `.github/workflows/release-cli.yml`).
@@ -0,0 +1,109 @@
1
+ # travelmaxx
2
+
3
+ Search award flights across 14 mileage programs from your terminal, powered by
4
+ [TravelMaxx](https://travelmaxx.app).
5
+
6
+ ```sh
7
+ curl -fsSL https://travelmaxx.app/cli | sh
8
+ travelmaxx login
9
+ travelmaxx search SFO NRT --date 2026-10-05 --programs ALASKA
10
+ ```
11
+
12
+ ## Install
13
+
14
+ Any of:
15
+
16
+ ```sh
17
+ curl -fsSL https://travelmaxx.app/cli | sh # auto-detects uv/pipx/pip
18
+ uv tool install travelmaxx
19
+ pipx install travelmaxx
20
+ brew install travelmaxx/travelmaxx/travelmaxx # once the tap is live
21
+ ```
22
+
23
+ Requires Python 3.10+ (macOS or Linux), an active TravelMaxx subscription, and
24
+ Roame credentials linked at <https://travelmaxx.app/dashboard/settings>.
25
+
26
+ ## Sign in
27
+
28
+ ```sh
29
+ travelmaxx login # opens your browser; click Approve
30
+ travelmaxx login --no-browser # headless/SSH: 6-digit code at travelmaxx.app/activate
31
+ travelmaxx whoami
32
+ travelmaxx logout
33
+ ```
34
+
35
+ Tokens are stored in `~/.config/travelmaxx/credentials.json` (chmod 600) and
36
+ refresh automatically for 30 days. Your Roame credentials never touch this
37
+ machine — searches run through the TravelMaxx server.
38
+
39
+ ## Search
40
+
41
+ ```sh
42
+ # Single date (always pass --programs when you know them: 40-50x faster)
43
+ travelmaxx search SFO NRT --date 2026-10-05 --programs ALASKA
44
+
45
+ # Business class, 2 passengers, flexible ±2 days
46
+ travelmaxx search SFO CDG --date 2026-09-12 --class BUSINESS \
47
+ --programs ALASKA,UNITED --pax 2 --days-around 2
48
+
49
+ # A whole month in parallel, with a live progress bar
50
+ travelmaxx search SFO NRT --date-range november --programs ALASKA
51
+
52
+ # Explicit range / year / "next year"
53
+ travelmaxx search SFO HEL --date-range 2026-09-12:2026-10-18 --programs ALASKA
54
+ ```
55
+
56
+ Options: `--sort points|duration`, `--limit N`, `--max-wait SECONDS` (5–120;
57
+ results are marked `partial` if the search returns early), `--min-results N`,
58
+ `--max-workers N` (range searches), `--json`, `--output-file FILE`.
59
+
60
+ Programs: `ALASKA, UNITED, DELTA, JETBLUE, LIFEMILES` (fast) ·
61
+ `AMERICAN, QANTAS, EMIRATES, VIRGIN_ATLANTIC, VIRGIN_AUSTRALIA, CLUB_PREMIER,
62
+ ETIHAD, FLYING_BLUE, GOL` (slower).
63
+
64
+ ## Alerts & discovery
65
+
66
+ ```sh
67
+ travelmaxx alerts create SFO NRT --class BUSINESS --max-points 75000
68
+ travelmaxx alerts list
69
+ travelmaxx alerts delete <uuid>
70
+ travelmaxx discover --limit 10
71
+ ```
72
+
73
+ ## JSON output
74
+
75
+ `--json` prints the full API response. Single-date searches return
76
+ `{"fares": [...], "percent_completed": 100, "partial": false}`; job-based
77
+ searches return `{"status", "percent", "fares_count", "fares", ...}`.
78
+ Each fare:
79
+
80
+ ```json
81
+ {
82
+ "departure_date": "2026-10-05",
83
+ "origin": "SFO", "destination": "NRT",
84
+ "mileage_program": "ALASKA",
85
+ "award_points": 75000, "surcharge_usd": 18.1,
86
+ "num_stops": 0, "duration_minutes": 630,
87
+ "cabin_classes": ["BUSINESS"], "operating_airlines": ["JL"],
88
+ "equipment_types": ["77W"], "available_seats": 2, "roame_score": 1684
89
+ }
90
+ ```
91
+
92
+ ## Configuration
93
+
94
+ | Env var | Purpose |
95
+ |---|---|
96
+ | `TRAVELMAXX_API_URL` | Point at a different server (also `--server URL`) |
97
+ | `XDG_CONFIG_HOME` | Relocate the config dir (default `~/.config/travelmaxx`) |
98
+
99
+ ## Development
100
+
101
+ ```sh
102
+ cd cli
103
+ python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
104
+ .venv/bin/pytest
105
+ .venv/bin/travelmaxx --server http://localhost:8000 login
106
+ ```
107
+
108
+ Design doc: `../CLI.md`. Releases: tag `cli-vX.Y.Z` (see
109
+ `.github/workflows/release-cli.yml`).
@@ -0,0 +1,41 @@
1
+ [project]
2
+ name = "travelmaxx"
3
+ description = "Search award flights across 14 mileage programs from your terminal"
4
+ readme = "README.md"
5
+ requires-python = ">=3.10"
6
+ license = "MIT"
7
+ dynamic = ["version"]
8
+ dependencies = [
9
+ "httpx>=0.27",
10
+ ]
11
+ keywords = ["awards", "flights", "miles", "points", "travel"]
12
+ classifiers = [
13
+ "Environment :: Console",
14
+ "Intended Audience :: End Users/Desktop",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3",
17
+ "Topic :: Utilities",
18
+ ]
19
+
20
+ [project.urls]
21
+ Homepage = "https://travelmaxx.app"
22
+ Documentation = "https://travelmaxx.app/cli/docs"
23
+
24
+ [project.scripts]
25
+ travelmaxx = "travelmaxx_cli.main:main"
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "pytest>=8.0",
30
+ "respx>=0.22",
31
+ ]
32
+
33
+ [build-system]
34
+ requires = ["setuptools>=75"]
35
+ build-backend = "setuptools.build_meta"
36
+
37
+ [tool.setuptools.dynamic]
38
+ version = { attr = "travelmaxx_cli.__version__" }
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: travelmaxx
3
+ Version: 0.1.0
4
+ Summary: Search award flights across 14 mileage programs from your terminal
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://travelmaxx.app
7
+ Project-URL: Documentation, https://travelmaxx.app/cli/docs
8
+ Keywords: awards,flights,miles,points,travel
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: End Users/Desktop
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Utilities
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: httpx>=0.27
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=8.0; extra == "dev"
19
+ Requires-Dist: respx>=0.22; extra == "dev"
20
+
21
+ # travelmaxx
22
+
23
+ Search award flights across 14 mileage programs from your terminal, powered by
24
+ [TravelMaxx](https://travelmaxx.app).
25
+
26
+ ```sh
27
+ curl -fsSL https://travelmaxx.app/cli | sh
28
+ travelmaxx login
29
+ travelmaxx search SFO NRT --date 2026-10-05 --programs ALASKA
30
+ ```
31
+
32
+ ## Install
33
+
34
+ Any of:
35
+
36
+ ```sh
37
+ curl -fsSL https://travelmaxx.app/cli | sh # auto-detects uv/pipx/pip
38
+ uv tool install travelmaxx
39
+ pipx install travelmaxx
40
+ brew install travelmaxx/travelmaxx/travelmaxx # once the tap is live
41
+ ```
42
+
43
+ Requires Python 3.10+ (macOS or Linux), an active TravelMaxx subscription, and
44
+ Roame credentials linked at <https://travelmaxx.app/dashboard/settings>.
45
+
46
+ ## Sign in
47
+
48
+ ```sh
49
+ travelmaxx login # opens your browser; click Approve
50
+ travelmaxx login --no-browser # headless/SSH: 6-digit code at travelmaxx.app/activate
51
+ travelmaxx whoami
52
+ travelmaxx logout
53
+ ```
54
+
55
+ Tokens are stored in `~/.config/travelmaxx/credentials.json` (chmod 600) and
56
+ refresh automatically for 30 days. Your Roame credentials never touch this
57
+ machine — searches run through the TravelMaxx server.
58
+
59
+ ## Search
60
+
61
+ ```sh
62
+ # Single date (always pass --programs when you know them: 40-50x faster)
63
+ travelmaxx search SFO NRT --date 2026-10-05 --programs ALASKA
64
+
65
+ # Business class, 2 passengers, flexible ±2 days
66
+ travelmaxx search SFO CDG --date 2026-09-12 --class BUSINESS \
67
+ --programs ALASKA,UNITED --pax 2 --days-around 2
68
+
69
+ # A whole month in parallel, with a live progress bar
70
+ travelmaxx search SFO NRT --date-range november --programs ALASKA
71
+
72
+ # Explicit range / year / "next year"
73
+ travelmaxx search SFO HEL --date-range 2026-09-12:2026-10-18 --programs ALASKA
74
+ ```
75
+
76
+ Options: `--sort points|duration`, `--limit N`, `--max-wait SECONDS` (5–120;
77
+ results are marked `partial` if the search returns early), `--min-results N`,
78
+ `--max-workers N` (range searches), `--json`, `--output-file FILE`.
79
+
80
+ Programs: `ALASKA, UNITED, DELTA, JETBLUE, LIFEMILES` (fast) ·
81
+ `AMERICAN, QANTAS, EMIRATES, VIRGIN_ATLANTIC, VIRGIN_AUSTRALIA, CLUB_PREMIER,
82
+ ETIHAD, FLYING_BLUE, GOL` (slower).
83
+
84
+ ## Alerts & discovery
85
+
86
+ ```sh
87
+ travelmaxx alerts create SFO NRT --class BUSINESS --max-points 75000
88
+ travelmaxx alerts list
89
+ travelmaxx alerts delete <uuid>
90
+ travelmaxx discover --limit 10
91
+ ```
92
+
93
+ ## JSON output
94
+
95
+ `--json` prints the full API response. Single-date searches return
96
+ `{"fares": [...], "percent_completed": 100, "partial": false}`; job-based
97
+ searches return `{"status", "percent", "fares_count", "fares", ...}`.
98
+ Each fare:
99
+
100
+ ```json
101
+ {
102
+ "departure_date": "2026-10-05",
103
+ "origin": "SFO", "destination": "NRT",
104
+ "mileage_program": "ALASKA",
105
+ "award_points": 75000, "surcharge_usd": 18.1,
106
+ "num_stops": 0, "duration_minutes": 630,
107
+ "cabin_classes": ["BUSINESS"], "operating_airlines": ["JL"],
108
+ "equipment_types": ["77W"], "available_seats": 2, "roame_score": 1684
109
+ }
110
+ ```
111
+
112
+ ## Configuration
113
+
114
+ | Env var | Purpose |
115
+ |---|---|
116
+ | `TRAVELMAXX_API_URL` | Point at a different server (also `--server URL`) |
117
+ | `XDG_CONFIG_HOME` | Relocate the config dir (default `~/.config/travelmaxx`) |
118
+
119
+ ## Development
120
+
121
+ ```sh
122
+ cd cli
123
+ python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
124
+ .venv/bin/pytest
125
+ .venv/bin/travelmaxx --server http://localhost:8000 login
126
+ ```
127
+
128
+ Design doc: `../CLI.md`. Releases: tag `cli-vX.Y.Z` (see
129
+ `.github/workflows/release-cli.yml`).
@@ -0,0 +1,20 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/travelmaxx.egg-info/PKG-INFO
4
+ src/travelmaxx.egg-info/SOURCES.txt
5
+ src/travelmaxx.egg-info/dependency_links.txt
6
+ src/travelmaxx.egg-info/entry_points.txt
7
+ src/travelmaxx.egg-info/requires.txt
8
+ src/travelmaxx.egg-info/top_level.txt
9
+ src/travelmaxx_cli/__init__.py
10
+ src/travelmaxx_cli/api.py
11
+ src/travelmaxx_cli/auth.py
12
+ src/travelmaxx_cli/config.py
13
+ src/travelmaxx_cli/main.py
14
+ src/travelmaxx_cli/render.py
15
+ src/travelmaxx_cli/commands/__init__.py
16
+ src/travelmaxx_cli/commands/alerts.py
17
+ src/travelmaxx_cli/commands/auth_cmds.py
18
+ src/travelmaxx_cli/commands/discover.py
19
+ src/travelmaxx_cli/commands/search.py
20
+ tests/test_cli.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ travelmaxx = travelmaxx_cli.main:main
@@ -0,0 +1,5 @@
1
+ httpx>=0.27
2
+
3
+ [dev]
4
+ pytest>=8.0
5
+ respx>=0.22
@@ -0,0 +1 @@
1
+ travelmaxx_cli
@@ -0,0 +1,3 @@
1
+ """TravelMaxx CLI — award flight search from your terminal."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,90 @@
1
+ """HTTP client for the TravelMaxx /api/v1 REST API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import httpx
6
+
7
+ from . import auth, config
8
+
9
+
10
+ class APIError(Exception):
11
+ def __init__(self, status: int, code: str, message: str):
12
+ super().__init__(message)
13
+ self.status = status
14
+ self.code = code
15
+
16
+
17
+ def _friendly(status: int, code: str, message: str) -> APIError:
18
+ if status == 402:
19
+ message = "Active subscription required — start a trial at https://travelmaxx.app/subscribe"
20
+ elif status == 409:
21
+ message = ("No Roame account linked — connect it at "
22
+ "https://travelmaxx.app/dashboard/settings")
23
+ return APIError(status, code, message)
24
+
25
+
26
+ class APIClient:
27
+ def __init__(self, server: str | None = None):
28
+ self._creds = auth.get_valid_credentials(server)
29
+ self._server = self._creds["server"]
30
+ self._http = httpx.Client(base_url=self._server, timeout=180.0)
31
+
32
+ def close(self):
33
+ self._http.close()
34
+
35
+ def __enter__(self):
36
+ return self
37
+
38
+ def __exit__(self, *exc):
39
+ self.close()
40
+
41
+ def _request(self, method: str, path: str, *, retry_auth: bool = True, **kwargs):
42
+ headers = {"Authorization": f"Bearer {self._creds['access_token']}"}
43
+ resp = self._http.request(method, path, headers=headers, **kwargs)
44
+ if resp.status_code == 401 and retry_auth:
45
+ self._creds = auth.refresh_credentials(self._creds)
46
+ return self._request(method, path, retry_auth=False, **kwargs)
47
+ if resp.status_code >= 400:
48
+ try:
49
+ body = resp.json()
50
+ code, message = body.get("error", ""), body.get("message", "")
51
+ except Exception:
52
+ code, message = f"http_{resp.status_code}", resp.text[:200]
53
+ raise _friendly(resp.status_code, code, message or code)
54
+ return resp.json()
55
+
56
+ # ── Endpoints ───────────────────────────────────────────────────────
57
+
58
+ def me(self) -> dict:
59
+ return self._request("GET", "/api/v1/me")
60
+
61
+ def search(self, **body) -> dict:
62
+ return self._request("POST", "/api/v1/search",
63
+ json={k: v for k, v in body.items() if v is not None})
64
+
65
+ def create_search_job(self, **body) -> dict:
66
+ return self._request("POST", "/api/v1/search/jobs",
67
+ json={k: v for k, v in body.items() if v is not None})
68
+
69
+ def get_search_job(self, job_id: str, include_fares: bool = True,
70
+ sort_by: str = "points", limit: int = 500) -> dict:
71
+ return self._request(
72
+ "GET", f"/api/v1/search/jobs/{job_id}",
73
+ params={"include_fares": str(include_fares).lower(),
74
+ "sort_by": sort_by, "limit": limit})
75
+
76
+ def cancel_search_job(self, job_id: str) -> dict:
77
+ return self._request("DELETE", f"/api/v1/search/jobs/{job_id}")
78
+
79
+ def list_alerts(self) -> list[dict]:
80
+ return self._request("GET", "/api/v1/alerts")
81
+
82
+ def create_alert(self, **body) -> dict:
83
+ return self._request("POST", "/api/v1/alerts",
84
+ json={k: v for k, v in body.items() if v is not None})
85
+
86
+ def delete_alert(self, uuid: str) -> dict:
87
+ return self._request("DELETE", f"/api/v1/alerts/{uuid}")
88
+
89
+ def discover(self, limit: int = 20) -> list[dict]:
90
+ return self._request("GET", "/api/v1/discover", params={"limit": limit})