sweatstack-cli 0.5.0__py3-none-any.whl → 0.7.0__py3-none-any.whl

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.
@@ -1,3 +1,3 @@
1
1
  """SweatStack CLI — Command-line interface for the SweatStack platform."""
2
2
 
3
- __version__ = "0.5.0"
3
+ __version__ = "0.7.0"
@@ -4,12 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  import json as json_lib
6
6
  from pathlib import Path
7
+ from typing import Any, cast
7
8
 
8
9
  import typer
9
10
 
10
11
  from sweatstack_cli.api import APIClient
11
12
  from sweatstack_cli.console import console, stderr_console
12
13
  from sweatstack_cli.exceptions import APIError, AuthenticationError
14
+ from sweatstack_cli.project import CONFIG_FILENAME, write_config
13
15
 
14
16
  app = typer.Typer(name="app", help="Manage OAuth2 applications.")
15
17
 
@@ -17,6 +19,12 @@ app = typer.Typer(name="app", help="Manage OAuth2 applications.")
17
19
  @app.command()
18
20
  def create(
19
21
  name: str = typer.Argument(..., help="Application name."),
22
+ description: str | None = typer.Option(
23
+ None,
24
+ "--description",
25
+ "-d",
26
+ help="Application description (max 500 chars).",
27
+ ),
20
28
  page: str | None = typer.Option(
21
29
  None,
22
30
  "--page",
@@ -52,6 +60,9 @@ def create(
52
60
 
53
61
  body: dict[str, str | bool] = {"name": name}
54
62
 
63
+ if description:
64
+ body["description"] = description
65
+
55
66
  if page:
56
67
  body["page"] = page
57
68
 
@@ -75,13 +86,20 @@ def create(
75
86
  if env_path:
76
87
  _write_env_file(env_path, result)
77
88
 
89
+ # Write sweatstack.toml
90
+ config_path = write_config(
91
+ app_name=result["name"],
92
+ client_id=result["client_id"],
93
+ page_slug=page,
94
+ )
95
+
78
96
  if json_output:
79
97
  console.print(json_lib.dumps(result, indent=2))
80
98
  else:
81
- _print_app_created(result, env_path)
99
+ _print_app_created(result, env_path, config_path)
82
100
 
83
101
 
84
- def _write_env_file(path: Path, result: dict) -> None:
102
+ def _write_env_file(path: Path, result: dict[str, str]) -> None:
85
103
  """Append credentials to an env file."""
86
104
  lines_to_add: list[str] = []
87
105
  existing_content = ""
@@ -116,7 +134,7 @@ def _write_env_file(path: Path, result: dict) -> None:
116
134
  f.write("\n".join(lines_to_add) + "\n")
117
135
 
118
136
 
119
- def _print_app_created(result: dict, env_path: Path | None) -> None:
137
+ def _print_app_created(result: dict[str, str], env_path: Path | None, config_path: Path) -> None:
120
138
  """Pretty-print the created application details."""
121
139
  console.print(f"[green]✓[/green] Created application [bold]{result['name']}[/bold]")
122
140
  console.print()
@@ -131,9 +149,77 @@ def _print_app_created(result: dict, env_path: Path | None) -> None:
131
149
  for uri in result["redirect_uris"]:
132
150
  console.print(f" - {uri}")
133
151
 
152
+ console.print()
153
+ console.print(f"[green]✓[/green] Config written to [bold]{config_path}[/bold]")
154
+
134
155
  if env_path:
135
- console.print()
136
156
  console.print(f"[green]✓[/green] Credentials written to [bold]{env_path}[/bold]")
137
- elif "client_secret" in result:
157
+
158
+ if "client_secret" in result and not env_path:
138
159
  console.print()
139
160
  console.print("[yellow]Save the secret now — it won't be shown again.[/yellow]")
161
+
162
+
163
+ @app.command()
164
+ def link(
165
+ force: bool = typer.Option(
166
+ False,
167
+ "--force",
168
+ "-f",
169
+ help="Overwrite existing sweatstack.toml.",
170
+ ),
171
+ ) -> None:
172
+ """Link this directory to an existing application."""
173
+ config_path = Path(CONFIG_FILENAME)
174
+
175
+ # Check if config already exists
176
+ if config_path.exists() and not force:
177
+ console.print(f"[yellow]Already linked.[/yellow] {CONFIG_FILENAME} exists.")
178
+ console.print("Use [cyan]--force[/cyan] to overwrite.")
179
+ raise typer.Exit(1)
180
+
181
+ # Fetch user's apps
182
+ try:
183
+ client = APIClient()
184
+ except AuthenticationError as e:
185
+ console.print(f"[red]Error:[/red] {e}")
186
+ console.print("[dim]Run 'sweatstack login' to authenticate.[/dim]")
187
+ raise typer.Exit(2)
188
+
189
+ try:
190
+ # API returns a list, but client.get is typed as returning dict
191
+ apps = cast(list[dict[str, Any]], client.get("/api/v1/applications"))
192
+ except APIError as e:
193
+ console.print(f"[red]Error:[/red] {e}")
194
+ raise typer.Exit(3)
195
+
196
+ if not apps:
197
+ console.print("[yellow]No applications found.[/yellow]")
198
+ console.print("Create one with [cyan]sweatstack app create <name>[/cyan]")
199
+ raise typer.Exit(1)
200
+
201
+ # Show apps and let user select
202
+ console.print("Link this directory to an existing application.\n")
203
+ console.print("Your applications:")
204
+ for i, app_info in enumerate(apps, 1):
205
+ page_info = f" [dim](page: {app_info['page_slug']})[/dim]" if app_info.get("page_slug") else ""
206
+ console.print(f" [cyan][{i}][/cyan] {app_info['name']}{page_info}")
207
+
208
+ console.print()
209
+ selection = typer.prompt("Select application", type=int)
210
+
211
+ if selection < 1 or selection > len(apps):
212
+ console.print("[red]Invalid selection.[/red]")
213
+ raise typer.Exit(1)
214
+
215
+ selected = apps[selection - 1]
216
+
217
+ # Write config
218
+ write_config(
219
+ app_name=selected["name"],
220
+ client_id=selected["id"],
221
+ page_slug=selected.get("page_slug"),
222
+ )
223
+
224
+ console.print()
225
+ console.print(f"[green]✓[/green] Linked to [bold]{selected['name']}[/bold]")
@@ -9,8 +9,10 @@ import typer
9
9
  from rich.progress import Progress, SpinnerColumn, TextColumn
10
10
 
11
11
  from sweatstack_cli.api import APIClient
12
+ from sweatstack_cli.config import get_settings
12
13
  from sweatstack_cli.console import console
13
14
  from sweatstack_cli.exceptions import APIError, AuthenticationError
15
+ from sweatstack_cli.project import CONFIG_FILENAME, ProjectConfig
14
16
 
15
17
  # URL where users can create pages for their applications
16
18
  SETTINGS_URL = "https://app.sweatstack.no/settings/api"
@@ -53,21 +55,40 @@ def _collect_files(directory: Path) -> list[tuple[str, tuple[str, bytes, str]]]:
53
55
 
54
56
  @app.command()
55
57
  def deploy(
56
- slug: str = typer.Argument(
57
- ...,
58
- help="Page slug (must be created first at app.sweatstack.no).",
58
+ slug: str | None = typer.Argument(
59
+ None,
60
+ help="Page slug. Reads from sweatstack.toml if not provided.",
59
61
  ),
60
- directory: Path = typer.Argument(
61
- ".",
62
- help="Directory containing static files to deploy.",
63
- exists=True,
64
- file_okay=False,
65
- dir_okay=True,
66
- resolve_path=True,
62
+ directory: Path | None = typer.Argument(
63
+ None,
64
+ help="Directory to deploy. Reads from sweatstack.toml or defaults to current dir.",
67
65
  ),
68
66
  ) -> None:
69
67
  """Deploy a static site to SweatStack Pages."""
70
- page_slug = slug
68
+ # Load config for defaults
69
+ config = ProjectConfig.load()
70
+
71
+ # Resolve slug
72
+ page_slug = slug or (config.page.slug if config else None)
73
+ if not page_slug:
74
+ console.print("[red]Error:[/red] No page slug provided.")
75
+ console.print(f"Specify a slug or add it to [bold]{CONFIG_FILENAME}[/bold]")
76
+ raise typer.Exit(1)
77
+
78
+ # Resolve directory
79
+ if directory is None:
80
+ dir_from_config = config.page.directory if config else None
81
+ directory = Path(dir_from_config) if dir_from_config else Path(".")
82
+
83
+ if not directory.exists():
84
+ console.print(f"[red]Error:[/red] Directory '{directory}' does not exist.")
85
+ raise typer.Exit(1)
86
+
87
+ if not directory.is_dir():
88
+ console.print(f"[red]Error:[/red] '{directory}' is not a directory.")
89
+ raise typer.Exit(1)
90
+
91
+ directory = directory.resolve()
71
92
 
72
93
  # Collect files
73
94
  files = _collect_files(directory)
@@ -126,3 +147,14 @@ def deploy(
126
147
  console.print(f"[green]✓[/green] Deployed [bold]{page_slug}[/bold] successfully!")
127
148
  console.print(f" Files: {file_count}")
128
149
  console.print(f" URL: {result.get('url', 'N/A')}")
150
+
151
+ # Nudge to publish if app is private
152
+ application = result.get("application", {})
153
+ if application.get("is_private"):
154
+ app_id = application.get("id")
155
+ if app_id:
156
+ base_url = get_settings().api_url
157
+ console.print()
158
+ console.print("Your app is private. Ready to go public?")
159
+ publish_url = f"{base_url}/applications/{app_id}/publish"
160
+ console.print(f" {publish_url}")
@@ -0,0 +1,111 @@
1
+ """Project configuration via sweatstack.toml."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import tomllib
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+
9
+ CONFIG_FILENAME = "sweatstack.toml"
10
+
11
+
12
+ @dataclass
13
+ class PageConfig:
14
+ """Page configuration."""
15
+
16
+ slug: str | None = None
17
+ directory: str | None = None
18
+
19
+
20
+ @dataclass
21
+ class AppConfig:
22
+ """Application configuration."""
23
+
24
+ name: str | None = None
25
+ client_id: str | None = None
26
+
27
+
28
+ @dataclass
29
+ class ProjectConfig:
30
+ """Project configuration from sweatstack.toml."""
31
+
32
+ app: AppConfig
33
+ page: PageConfig
34
+
35
+ @classmethod
36
+ def load(cls, path: Path | None = None) -> ProjectConfig | None:
37
+ """
38
+ Load config from sweatstack.toml in current directory.
39
+
40
+ Args:
41
+ path: Optional path to config file. Defaults to ./sweatstack.toml.
42
+
43
+ Returns:
44
+ ProjectConfig if file exists and is valid, None otherwise.
45
+ """
46
+ config_path = path or Path(CONFIG_FILENAME)
47
+
48
+ if not config_path.exists():
49
+ return None
50
+
51
+ try:
52
+ data = tomllib.loads(config_path.read_text(encoding="utf-8"))
53
+ except tomllib.TOMLDecodeError:
54
+ return None
55
+
56
+ app_data = data.get("app", {})
57
+ page_data = data.get("page", {})
58
+
59
+ return cls(
60
+ app=AppConfig(
61
+ name=app_data.get("name"),
62
+ client_id=app_data.get("client_id"),
63
+ ),
64
+ page=PageConfig(
65
+ slug=page_data.get("slug"),
66
+ directory=page_data.get("directory"),
67
+ ),
68
+ )
69
+
70
+
71
+ def write_config(
72
+ path: Path | None = None,
73
+ *,
74
+ app_name: str | None = None,
75
+ client_id: str | None = None,
76
+ page_slug: str | None = None,
77
+ page_directory: str | None = None,
78
+ ) -> Path:
79
+ """
80
+ Write sweatstack.toml file.
81
+
82
+ Args:
83
+ path: Optional path to config file. Defaults to ./sweatstack.toml.
84
+ app_name: Application name.
85
+ client_id: OAuth2 client ID.
86
+ page_slug: Page slug.
87
+ page_directory: Page directory.
88
+
89
+ Returns:
90
+ Path to the written config file.
91
+ """
92
+ config_path = path or Path(CONFIG_FILENAME)
93
+
94
+ lines = ["[app]"]
95
+ if app_name:
96
+ lines.append(f'name = "{app_name}"')
97
+ if client_id:
98
+ lines.append(f'client_id = "{client_id}"')
99
+
100
+ if page_slug or page_directory:
101
+ lines.append("")
102
+ lines.append("[page]")
103
+ if page_slug:
104
+ lines.append(f'slug = "{page_slug}"')
105
+ if page_directory:
106
+ lines.append(f'directory = "{page_directory}"')
107
+
108
+ lines.append("") # Trailing newline
109
+ config_path.write_text("\n".join(lines), encoding="utf-8")
110
+
111
+ return config_path
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: sweatstack-cli
3
+ Version: 0.7.0
4
+ Summary: Command-line interface for SweatStack — the sports data platform for developers
5
+ Project-URL: Homepage, https://sweatstack.no
6
+ Project-URL: Documentation, https://docs.sweatstack.no
7
+ Project-URL: Repository, https://github.com/sweatstack/sweatstack-cli
8
+ Author-email: Aart Goossens <aart@sweatstack.no>
9
+ License: MIT
10
+ Keywords: api,cli,fitness,sports,sweatstack
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: httpx>=0.28.0
21
+ Requires-Dist: platformdirs>=4.0
22
+ Requires-Dist: typer>=0.15.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: coverage>=7.0; extra == 'dev'
25
+ Requires-Dist: mypy>=1.14; extra == 'dev'
26
+ Requires-Dist: pytest-httpx>=0.35; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.9; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # SweatStack CLI
32
+
33
+ Command-line interface for [SweatStack](https://sweatstack.no) — the sports data platform for developers.
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install sweatstack-cli
39
+ ```
40
+
41
+ Requires Python 3.11+.
42
+
43
+ ## Quick Start
44
+
45
+ ```bash
46
+ # Authenticate with SweatStack
47
+ sweatstack login
48
+
49
+ # Check authentication and version status
50
+ sweatstack status
51
+
52
+ # Deploy a static site
53
+ sweatstack page deploy my-page ./dist
54
+
55
+ # Create an OAuth2 application
56
+ sweatstack app create "My App" --page my-page --env
57
+
58
+ # Logout
59
+ sweatstack logout
60
+ ```
61
+
62
+ ## Commands
63
+
64
+ ### Authentication
65
+
66
+ | Command | Description |
67
+ |---------|-------------|
68
+ | `sweatstack login` | Authenticate via browser (OAuth2 PKCE) |
69
+ | `sweatstack login --force` | Force re-authentication |
70
+ | `sweatstack logout` | Remove stored credentials |
71
+ | `sweatstack status` | Show auth status, user info, and version |
72
+ | `sweatstack --version` | Show version and check for updates |
73
+
74
+ ### Page Deployment
75
+
76
+ Deploy static sites to SweatStack Pages.
77
+
78
+ | Command | Description |
79
+ |---------|-------------|
80
+ | `sweatstack page deploy [SLUG] [DIRECTORY]` | Deploy a static site |
81
+
82
+ - **SLUG** — Page identifier. Falls back to `sweatstack.toml` if omitted.
83
+ - **DIRECTORY** — Directory to deploy. Falls back to `sweatstack.toml`, then current directory.
84
+
85
+ ```bash
86
+ # Deploy using CLI arguments
87
+ sweatstack page deploy my-page ./dist
88
+
89
+ # Deploy using sweatstack.toml defaults
90
+ sweatstack page deploy
91
+ ```
92
+
93
+ ### Application Management
94
+
95
+ Manage OAuth2 applications.
96
+
97
+ | Command | Description |
98
+ |---------|-------------|
99
+ | `sweatstack app create NAME` | Create a new private OAuth2 application |
100
+ | `sweatstack app link` | Link current directory to an existing application |
101
+
102
+ #### `app create` options
103
+
104
+ | Option | Description |
105
+ |--------|-------------|
106
+ | `--description, -d TEXT` | Application description (max 500 chars) |
107
+ | `--page, -p SLUG` | Associate with a SweatStack Page |
108
+ | `--secret, -s` | Generate a client secret |
109
+ | `--env` | Write credentials to `.env` |
110
+ | `--env-file PATH` | Write credentials to a specific file |
111
+ | `--json` | Output as JSON |
112
+
113
+ #### `app link` options
114
+
115
+ | Option | Description |
116
+ |--------|-------------|
117
+ | `--force, -f` | Overwrite existing `sweatstack.toml` |
118
+
119
+ ## Project Configuration
120
+
121
+ Commands read defaults from a `sweatstack.toml` in the current directory. This file is created automatically by `app create` and `app link`.
122
+
123
+ ```toml
124
+ [app]
125
+ name = "My App"
126
+ client_id = "abc123"
127
+
128
+ [page]
129
+ slug = "my-page"
130
+ directory = "dist"
131
+ ```
132
+
133
+ ## CI/CD Usage
134
+
135
+ For automated environments, set environment variables instead of interactive login:
136
+
137
+ ```bash
138
+ export SWEATSTACK_API_KEY="your-access-token"
139
+ export SWEATSTACK_REFRESH_TOKEN="your-refresh-token"
140
+
141
+ sweatstack page deploy my-page ./dist
142
+ ```
143
+
144
+ ## Environment Variables
145
+
146
+ | Variable | Description | Default |
147
+ |----------|-------------|---------|
148
+ | `SWEATSTACK_URL` | API base URL | `https://app.sweatstack.no` |
149
+ | `SWEATSTACK_API_KEY` | Access token (for CI/CD) | — |
150
+ | `SWEATSTACK_REFRESH_TOKEN` | Refresh token (for CI/CD) | — |
151
+
152
+ ## Token Storage
153
+
154
+ Credentials are stored in your OS user data directory:
155
+
156
+ - **macOS**: `~/Library/Application Support/SweatStack/SweatStack/tokens.json`
157
+ - **Linux**: `~/.local/share/SweatStack/SweatStack/tokens.json`
158
+ - **Windows**: `%APPDATA%\SweatStack\SweatStack\tokens.json`
159
+
160
+ Token files are created with `0600` permissions (owner read/write only). This location is shared with the [sweatstack Python library](https://github.com/sweatstack/sweatstack-python), so authenticating with either tool works for both.
161
+
162
+ ## Development
163
+
164
+ See [DEVELOPMENT.md](DEVELOPMENT.md) for development setup and contribution guidelines.
165
+
166
+ ```bash
167
+ # Install with dev dependencies
168
+ uv pip install -e ".[dev]"
169
+
170
+ # Run tests
171
+ pytest
172
+
173
+ # Lint and format
174
+ ruff check . && ruff format .
175
+
176
+ # Type check
177
+ mypy src/sweatstack_cli
178
+ ```
179
+
180
+ ## License
181
+
182
+ MIT
@@ -1,8 +1,9 @@
1
- sweatstack_cli/__init__.py,sha256=gSiBrerQOXS5NObG0xMbpd_mT_efPbam-ZYLGfspc6k,100
1
+ sweatstack_cli/__init__.py,sha256=n2zPulBh92qWjkryatKmyuF192Pfhslh0yCiCZdD-Co,100
2
2
  sweatstack_cli/config.py,sha256=cD4OXnVAKVagYazyk9Cw-WWx62tJPqcjQt1cknYyymM,706
3
3
  sweatstack_cli/console.py,sha256=69KWAM7ksnYRTONMYTXGXN4LWRAQhRxcLXO0LcWGLqo,300
4
4
  sweatstack_cli/exceptions.py,sha256=qEUu8GydXGRKR4ndAdZ2wnkHekmFpBXDJiT5bXdl7Yk,613
5
5
  sweatstack_cli/main.py,sha256=obcF1VGhwcE_pI1BwTJkY4J4BgIGac-y3yYGvY6ZOp8,2330
6
+ sweatstack_cli/project.py,sha256=mOwqzuc6D6J6IHf-5L9Vv_sfjpReS4vwzSjUSFi7qys,2716
6
7
  sweatstack_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
8
  sweatstack_cli/version_check.py,sha256=yZ_qVe7tUBowUDAmMki-wyf5pSxlNDsoWKgTcSfesBA,2015
8
9
  sweatstack_cli/api/__init__.py,sha256=l9s6BzF6Wx17K_ElRUX-8aAwwIIzqmGF1PQ3Mbc07mw,107
@@ -13,10 +14,10 @@ sweatstack_cli/auth/jwt.py,sha256=Ja6ccFyRKtX5MPjFdHNavVYf8KTz0MYT86StbnF-cwg,15
13
14
  sweatstack_cli/auth/pkce.py,sha256=sO_xUeN1UIvXKX0yEIGgMTIIzvfCOCUwlyNAveEd16k,1542
14
15
  sweatstack_cli/auth/tokens.py,sha256=kc8ytoO9PAyjLD6wUTIagVuP7_ia5ZjW5m_WjXIRIkA,4942
15
16
  sweatstack_cli/commands/__init__.py,sha256=YCO8wYVvdGeoEI0p_oUm8ANLneuET4SHsKDjitMjAgY,35
16
- sweatstack_cli/commands/app.py,sha256=fOdnaX1yYogo_Gire6VwBXizOXOiFLZHBd9v65s-wgA,4189
17
+ sweatstack_cli/commands/app.py,sha256=vTMR1Hs76LOJe8q1_FY1AcNPG7LOJdz8SCOfY3ZwayY,6945
17
18
  sweatstack_cli/commands/auth.py,sha256=lmlN2RcZIHdbZG-EEUVFwOSn6vPN-Ijgh_5E-cFQVtU,3478
18
- sweatstack_cli/commands/page.py,sha256=PX3KPMgf6uSBtWioHOl0YeRqaWEwSOGbr7xRJwsdtU0,3978
19
- sweatstack_cli-0.5.0.dist-info/METADATA,sha256=lLgfGN-b6EmjC3tEFYvlXYfcV_6eaYaitdYaUpFFycU,3574
20
- sweatstack_cli-0.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
21
- sweatstack_cli-0.5.0.dist-info/entry_points.txt,sha256=rnH0EV1U9CQlmKQcJ7RmxGpNRTa9J-YdiIJZDYrAXb4,56
22
- sweatstack_cli-0.5.0.dist-info/RECORD,,
19
+ sweatstack_cli/commands/page.py,sha256=tEWjzOmht_IKm47lbprJz-XDKibrx5nFlStuvBRST6A,5329
20
+ sweatstack_cli-0.7.0.dist-info/METADATA,sha256=e4RwtVRDIbDAH6uAPnLL4QcEXu_cvgs3yjMt-LxVqm0,5017
21
+ sweatstack_cli-0.7.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
22
+ sweatstack_cli-0.7.0.dist-info/entry_points.txt,sha256=rnH0EV1U9CQlmKQcJ7RmxGpNRTa9J-YdiIJZDYrAXb4,56
23
+ sweatstack_cli-0.7.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.28.0
2
+ Generator: hatchling 1.29.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,132 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: sweatstack-cli
3
- Version: 0.5.0
4
- Summary: Command-line interface for SweatStack — the sports data platform for developers
5
- Project-URL: Homepage, https://sweatstack.no
6
- Project-URL: Documentation, https://docs.sweatstack.no
7
- Project-URL: Repository, https://github.com/sweatstack/sweatstack-cli
8
- Author-email: Aart Goossens <aart@goossens.me>
9
- License: MIT
10
- Keywords: api,cli,fitness,sports,sweatstack
11
- Classifier: Development Status :: 3 - Alpha
12
- Classifier: Environment :: Console
13
- Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Operating System :: OS Independent
16
- Classifier: Programming Language :: Python :: 3.13
17
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
- Classifier: Typing :: Typed
19
- Requires-Python: >=3.10
20
- Requires-Dist: httpx>=0.28.0
21
- Requires-Dist: platformdirs>=4.0
22
- Requires-Dist: typer>=0.15.0
23
- Provides-Extra: dev
24
- Requires-Dist: coverage>=7.0; extra == 'dev'
25
- Requires-Dist: mypy>=1.14; extra == 'dev'
26
- Requires-Dist: pytest-httpx>=0.35; extra == 'dev'
27
- Requires-Dist: pytest>=8.0; extra == 'dev'
28
- Requires-Dist: ruff>=0.9; extra == 'dev'
29
- Description-Content-Type: text/markdown
30
-
31
- # SweatStack CLI
32
-
33
- Command-line interface for [SweatStack](https://sweatstack.no) — the sports data platform for developers.
34
-
35
- ## Installation
36
-
37
- ```bash
38
- pip install sweatstack-cli
39
- ```
40
-
41
- Requires Python 3.13+.
42
-
43
- ## Quick Start
44
-
45
- ```bash
46
- # Authenticate with SweatStack
47
- sweatstack login
48
-
49
- # Check who you're logged in as
50
- sweatstack whoami
51
-
52
- # Deploy a static site
53
- sweatstack pages deploy ./dist --prod
54
-
55
- # View authentication status
56
- sweatstack status
57
-
58
- # Logout
59
- sweatstack logout
60
- ```
61
-
62
- ## Commands
63
-
64
- ### Authentication
65
-
66
- | Command | Description |
67
- |---------|-------------|
68
- | `sweatstack login` | Authenticate via browser |
69
- | `sweatstack login --force` | Force re-authentication |
70
- | `sweatstack logout` | Remove stored credentials |
71
- | `sweatstack whoami` | Show current user |
72
- | `sweatstack status` | Show token status and expiry |
73
-
74
- ### Pages
75
-
76
- | Command | Description |
77
- |---------|-------------|
78
- | `sweatstack pages deploy <dir>` | Deploy static site |
79
- | `sweatstack pages deploy --prod` | Deploy to production |
80
- | `sweatstack pages list` | List all sites |
81
- | `sweatstack pages delete <name>` | Delete a site |
82
-
83
- ## CI/CD Usage
84
-
85
- For automated environments, use environment variables instead of interactive login:
86
-
87
- ```bash
88
- export SWEATSTACK_API_KEY="your-access-token"
89
- export SWEATSTACK_REFRESH_TOKEN="your-refresh-token"
90
-
91
- sweatstack pages deploy ./dist --prod
92
- ```
93
-
94
- ## Configuration
95
-
96
- | Environment Variable | Description | Default |
97
- |---------------------|-------------|---------|
98
- | `SWEATSTACK_URL` | API base URL | `https://app.sweatstack.no` |
99
- | `SWEATSTACK_API_KEY` | Access token | — |
100
- | `SWEATSTACK_REFRESH_TOKEN` | Refresh token | — |
101
-
102
- ## Token Storage
103
-
104
- Credentials are stored securely in your OS user data directory:
105
-
106
- - **macOS**: `~/Library/Application Support/SweatStack/SweatStack/tokens.json`
107
- - **Linux**: `~/.local/share/SweatStack/SweatStack/tokens.json`
108
- - **Windows**: `%APPDATA%\SweatStack\SweatStack\tokens.json`
109
-
110
- This location is shared with the [sweatstack Python library](https://github.com/sweatstack/sweatstack-python), so authenticating with either tool works for both.
111
-
112
- ## Development
113
-
114
- See [DEVELOPMENT.md](DEVELOPMENT.md) for development setup and contribution guidelines.
115
-
116
- ```bash
117
- # Install with dev dependencies
118
- uv pip install -e ".[dev]"
119
-
120
- # Run tests
121
- pytest
122
-
123
- # Lint and format
124
- ruff check . && ruff format .
125
-
126
- # Type check
127
- mypy src/sweatstack_cli
128
- ```
129
-
130
- ## License
131
-
132
- MIT