sweatstack-cli 0.6.0__tar.gz → 0.7.1__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.
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/.claude/settings.local.json +2 -1
- sweatstack_cli-0.7.1/.claude/skills/sweatstack/SKILL.md +24 -0
- sweatstack_cli-0.7.1/.claude/skills/sweatstack/api.md +170 -0
- sweatstack_cli-0.7.1/.claude/skills/sweatstack/auth.md +136 -0
- sweatstack_cli-0.7.1/.claude/skills/sweatstack/cli.md +70 -0
- sweatstack_cli-0.7.1/.claude/skills/sweatstack/pyscript.md +33 -0
- sweatstack_cli-0.7.1/.claude/skills/sweatstack/webapp.md +138 -0
- sweatstack_cli-0.7.1/PKG-INFO +139 -0
- sweatstack_cli-0.7.1/README.md +109 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/pyproject.toml +1 -1
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/__init__.py +1 -1
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/commands/app.py +9 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/commands/page.py +12 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/uv.lock +1 -1
- sweatstack_cli-0.6.0/PKG-INFO +0 -132
- sweatstack_cli-0.6.0/README.md +0 -102
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/.gitignore +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/.python-version +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/CREATE_PRIVATE_APP.md +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/DEVELOPMENT.md +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/Makefile +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/PLAN.md +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/api/__init__.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/api/client.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/auth/__init__.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/auth/callback_server.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/auth/jwt.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/auth/pkce.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/auth/tokens.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/commands/__init__.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/commands/auth.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/config.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/console.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/exceptions.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/main.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/project.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/py.typed +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/src/sweatstack_cli/version_check.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/sweatstack.toml +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/__init__.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/conftest.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_api/__init__.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_api/test_client.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_auth/__init__.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_auth/test_callback_server.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_auth/test_jwt.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_auth/test_pkce.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_auth/test_tokens.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_commands/__init__.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_commands/test_app.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_commands/test_cli.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_project.py +0 -0
- {sweatstack_cli-0.6.0 → sweatstack_cli-0.7.1}/tests/test_version_check.py +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sweatstack
|
|
3
|
+
description: Builds applications integrating with SweatStack sports data platform. Provides OAuth PKCE authentication, API endpoints, and patterns for single-file HTML web apps. Use when building fitness dashboards, athletic data tools, or apps using SweatStack.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SweatStack
|
|
7
|
+
|
|
8
|
+
Sports data platform providing auth, storage, and wearable integrations for developers.
|
|
9
|
+
|
|
10
|
+
**Base URL:** `https://app.sweatstack.no`
|
|
11
|
+
|
|
12
|
+
**Default scopes:** `openid profile data:read`
|
|
13
|
+
|
|
14
|
+
## Reference
|
|
15
|
+
|
|
16
|
+
**Webapps:** Single-file HTML apps with vanilla JS. See [webapp.md](webapp.md)
|
|
17
|
+
|
|
18
|
+
**PyScript webapps:** Python data science in the browser (pandas, sklearn, scipy). Trade-offs and template. See [pyscript.md](pyscript.md)
|
|
19
|
+
|
|
20
|
+
**CLI:** App management and deployment. See [cli.md](cli.md)
|
|
21
|
+
|
|
22
|
+
**Auth:** OAuth2/PKCE authentication. See [auth.md](auth.md)
|
|
23
|
+
|
|
24
|
+
**API:** Endpoints and data formats. See [api.md](api.md)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# SweatStack API Reference
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Endpoints](#endpoints)
|
|
6
|
+
- [Choosing the Right Endpoint](#choosing-the-right-endpoint)
|
|
7
|
+
- [Data Fetching Patterns](#data-fetching-patterns)
|
|
8
|
+
- [Parquet Responses](#parquet-responses)
|
|
9
|
+
- [Metrics Parameter](#metrics-parameter)
|
|
10
|
+
- [Available Metrics](#available-metrics)
|
|
11
|
+
- [GPS Data](#gps-data)
|
|
12
|
+
- [Sports](#sports)
|
|
13
|
+
- [Authentication](#authentication)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
**Base URL:** `https://app.sweatstack.no`
|
|
18
|
+
|
|
19
|
+
**OpenAPI spec:** https://app.sweatstack.no/openapi.json
|
|
20
|
+
|
|
21
|
+
**IMPORTANT: Before making any API call, fetch the OpenAPI spec to verify:**
|
|
22
|
+
- Valid enum values (e.g. sports use hierarchical format like `walking.hiking`, not `hiking`)
|
|
23
|
+
- Required vs optional parameters
|
|
24
|
+
- Response schemas — look up the specific schema (e.g., `ActivityRead`) before accessing fields
|
|
25
|
+
|
|
26
|
+
Never guess field names or enum values. The spec is the source of truth.
|
|
27
|
+
|
|
28
|
+
## Common Gotchas
|
|
29
|
+
|
|
30
|
+
**Activity timestamps:** Use `start` (UTC) or `start_local` (user's local time). Not `start_time`, `started_at`, or `timestamp`.
|
|
31
|
+
|
|
32
|
+
**Key activity fields:** `id`, `start`, `start_local`, `sport`, `duration`, `name`, `description`
|
|
33
|
+
|
|
34
|
+
**Date parameters:** Use `YYYY-MM-DD` format. Most endpoints reject timestamps.
|
|
35
|
+
|
|
36
|
+
**Parameter naming:** Longitudinal endpoints use `sports` (plural), activity list uses `sport` (singular).
|
|
37
|
+
|
|
38
|
+
## Endpoints
|
|
39
|
+
|
|
40
|
+
| Endpoint | Method | Purpose |
|
|
41
|
+
|----------|--------|---------|
|
|
42
|
+
| `/api/v1/activities/` | GET | List activities (filter: sport, tags, start, end) |
|
|
43
|
+
| `/api/v1/activities/{id}` | GET | Get activity details |
|
|
44
|
+
| `/api/v1/activities/{id}/data` | GET | Timeseries data (returns Parquet) |
|
|
45
|
+
| `/api/v1/activities/latest` | GET | Most recent activity |
|
|
46
|
+
| `/api/v1/activities/upload` | POST | Upload .fit files (max 10) |
|
|
47
|
+
| `/api/v1/profile/tags/` | GET | User-defined tags |
|
|
48
|
+
| `/api/v1/profile/sports/` | GET | Available sports hierarchy |
|
|
49
|
+
| `/api/v1/activities/longitudinal-data` | GET | Timeseries data across activities. Allows extensive filtering on sports and metrics. (returns Parquet) |
|
|
50
|
+
| `/api/v1/activities/longitudinal-mean-max` | GET | Historical mean-max (returns Parquet) |
|
|
51
|
+
| `/api/v1/oauth/userinfo` | GET | Current user info |
|
|
52
|
+
|
|
53
|
+
**Critical**: Never guess request paramaters or body schemas. Use the openapi.json when not sure.
|
|
54
|
+
|
|
55
|
+
## Choosing the Right Endpoint
|
|
56
|
+
|
|
57
|
+
**Multi-activity analysis** (trends, distributions, comparisons): Use `/activities/longitudinal-data` — one request for all matching activities.
|
|
58
|
+
|
|
59
|
+
**Single activity** (detail view, route map): Use `/activities/{id}/data`.
|
|
60
|
+
|
|
61
|
+
## Data Fetching Patterns
|
|
62
|
+
|
|
63
|
+
**Time window comparisons:** Fetch the larger window once, filter client-side. Avoid multiple requests for overlapping or adjacent ranges.
|
|
64
|
+
|
|
65
|
+
**Bulk activity data:** Need data from many activities? Use `/activities/longitudinal-data` with filters instead of looping through `/activities/{id}/data` — one request vs N.
|
|
66
|
+
|
|
67
|
+
**Per-activity stats:** Longitudinal endpoints return raw samples with `activity_id`. Use DuckDB to aggregate: `GROUP BY activity_id`.
|
|
68
|
+
|
|
69
|
+
## Parquet Responses
|
|
70
|
+
|
|
71
|
+
Data endpoints return **Parquet files (snappy-compressed)**. Your Parquet library must support snappy codec.
|
|
72
|
+
|
|
73
|
+
**Browser apps:** Use DuckDB-WASM (recommended - full codec support, reliable CDN).
|
|
74
|
+
|
|
75
|
+
**Other environments:** Any Parquet library with snappy support.
|
|
76
|
+
|
|
77
|
+
### DuckDB-WASM Setup (browser)
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
import * as duckdb from 'https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.28.0/+esm';
|
|
81
|
+
|
|
82
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
83
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
84
|
+
const worker_url = URL.createObjectURL(
|
|
85
|
+
new Blob([`importScripts("${bundle.mainWorker}");`], { type: 'text/javascript' })
|
|
86
|
+
);
|
|
87
|
+
const worker = new Worker(worker_url);
|
|
88
|
+
const logger = new duckdb.ConsoleLogger();
|
|
89
|
+
const db = new duckdb.AsyncDuckDB(logger, worker);
|
|
90
|
+
await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
91
|
+
URL.revokeObjectURL(worker_url);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Parsing Parquet
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
async function parseParquet(arrayBuffer) {
|
|
98
|
+
const uint8 = new Uint8Array(arrayBuffer);
|
|
99
|
+
await db.registerFileBuffer('data.parquet', uint8);
|
|
100
|
+
const conn = await db.connect();
|
|
101
|
+
const result = await conn.query('SELECT * FROM "data.parquet"');
|
|
102
|
+
|
|
103
|
+
const data = {};
|
|
104
|
+
for (const field of result.schema.fields) {
|
|
105
|
+
data[field.name] = result.getChild(field.name).toArray();
|
|
106
|
+
}
|
|
107
|
+
await conn.close();
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Usage
|
|
112
|
+
const response = await fetch(`${API_BASE}/activities/${id}/data?metrics=altitude&metrics=distance`, {
|
|
113
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
114
|
+
});
|
|
115
|
+
const data = await parseParquet(await response.arrayBuffer());
|
|
116
|
+
// data = { altitude: [...], distance: [...] }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Note:** DuckDB-WASM is ~10MB but reliable. Other browser libraries (parquet-wasm, hyparquet) have codec or CDN issues.
|
|
120
|
+
|
|
121
|
+
## Metrics Parameter
|
|
122
|
+
|
|
123
|
+
Pass multiple times for multiple metrics:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
?metrics=altitude&metrics=distance ✓
|
|
127
|
+
?metrics=altitude,distance ✗
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Available Metrics
|
|
131
|
+
|
|
132
|
+
duration, power, speed, heart_rate, cadence, altitude, latitude, longitude, temperature, core_temperature, smo2, distance, lactate, RPE
|
|
133
|
+
|
|
134
|
+
## GPS Data
|
|
135
|
+
|
|
136
|
+
Filter invalid coordinates:
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
function isValidGPS(lat, lon) {
|
|
140
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return false;
|
|
141
|
+
if (lat < -90 || lat > 90) return false;
|
|
142
|
+
if (lon < -180 || lon > 180) return false;
|
|
143
|
+
|
|
144
|
+
const EPSILON = 1e-6;
|
|
145
|
+
if (Math.abs(lat) < EPSILON && Math.abs(lon) < EPSILON) return false;
|
|
146
|
+
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Catches NaN (from null values), Null Island (0, 0), and garbage floats.
|
|
152
|
+
|
|
153
|
+
## Sports
|
|
154
|
+
|
|
155
|
+
**Formatting sport values for display:**
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
function formatSport(sport) {
|
|
159
|
+
const parts = sport.split(".");
|
|
160
|
+
const base = parts[0].replace(/_/g, " ");
|
|
161
|
+
if (parts.length === 1) return base;
|
|
162
|
+
const rest = parts.slice(1).join(" ").replace(/_/g, " ");
|
|
163
|
+
return `${base} (${rest})`;
|
|
164
|
+
}
|
|
165
|
+
// "cycling.mountain" → "cycling (mountain)"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Authentication
|
|
169
|
+
|
|
170
|
+
All endpoints require `Authorization: Bearer TOKEN` header.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# OAuth2/OpenID Authentication
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Endpoints](#endpoints)
|
|
6
|
+
- [Scopes](#scopes)
|
|
7
|
+
- [PKCE Flow](#pkce-flow-public-clients)
|
|
8
|
+
- [Authorization Code Flow](#authorization-code-flow-private-clients)
|
|
9
|
+
- [Token Exchange](#token-exchange)
|
|
10
|
+
- [Refresh Tokens](#refresh-tokens)
|
|
11
|
+
- [API Authentication](#api-authentication)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Endpoints
|
|
16
|
+
|
|
17
|
+
| Endpoint | URL |
|
|
18
|
+
|----------|-----|
|
|
19
|
+
| Authorization | `https://app.sweatstack.no/oauth/authorize` |
|
|
20
|
+
| Token | `https://app.sweatstack.no/api/v1/oauth/token` |
|
|
21
|
+
|
|
22
|
+
## Scopes
|
|
23
|
+
|
|
24
|
+
| Scope | Purpose |
|
|
25
|
+
|-------|---------|
|
|
26
|
+
| `openid` | OpenID Connect (currently unused by SweatStack) |
|
|
27
|
+
| `profile` | Access to profile information |
|
|
28
|
+
| `data:read` | Read access to user data |
|
|
29
|
+
| `data:write` | Write and delete access (no read) |
|
|
30
|
+
| `offline_access` | Refresh token for public clients |
|
|
31
|
+
|
|
32
|
+
**Default:** `openid profile data:read`
|
|
33
|
+
|
|
34
|
+
## PKCE Flow (Public Clients)
|
|
35
|
+
|
|
36
|
+
For browser apps, mobile apps, and other public clients without a client secret.
|
|
37
|
+
|
|
38
|
+
### 1. Generate PKCE pair
|
|
39
|
+
|
|
40
|
+
- Code verifier: random string (43-128 chars)
|
|
41
|
+
- Code challenge: base64url(SHA-256(verifier))
|
|
42
|
+
|
|
43
|
+
### 2. Redirect to authorize
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
https://app.sweatstack.no/oauth/authorize?
|
|
47
|
+
client_id=CLIENT_ID&
|
|
48
|
+
redirect_uri=REDIRECT_URI&
|
|
49
|
+
response_type=code&
|
|
50
|
+
scope=openid%20profile%20data:read&
|
|
51
|
+
code_challenge=CHALLENGE&
|
|
52
|
+
code_challenge_method=S256&
|
|
53
|
+
prompt=none
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Critical:** Include `prompt=none` to avoid double consent screens.
|
|
57
|
+
|
|
58
|
+
### 3. Exchange code for token
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
POST https://app.sweatstack.no/api/v1/oauth/token
|
|
62
|
+
Content-Type: application/x-www-form-urlencoded
|
|
63
|
+
|
|
64
|
+
grant_type=authorization_code&
|
|
65
|
+
code=AUTH_CODE&
|
|
66
|
+
redirect_uri=REDIRECT_URI&
|
|
67
|
+
client_id=CLIENT_ID&
|
|
68
|
+
code_verifier=VERIFIER
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Authorization Code Flow (Private Clients)
|
|
72
|
+
|
|
73
|
+
For server-side apps with a client secret.
|
|
74
|
+
|
|
75
|
+
### 1. Redirect to authorize
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
https://app.sweatstack.no/oauth/authorize?
|
|
79
|
+
client_id=CLIENT_ID&
|
|
80
|
+
redirect_uri=REDIRECT_URI&
|
|
81
|
+
response_type=code&
|
|
82
|
+
scope=openid%20profile%20data:read&
|
|
83
|
+
prompt=none
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. Exchange code for token
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
POST https://app.sweatstack.no/api/v1/oauth/token
|
|
90
|
+
Content-Type: application/x-www-form-urlencoded
|
|
91
|
+
|
|
92
|
+
grant_type=authorization_code&
|
|
93
|
+
code=AUTH_CODE&
|
|
94
|
+
redirect_uri=REDIRECT_URI&
|
|
95
|
+
client_id=CLIENT_ID&
|
|
96
|
+
client_secret=CLIENT_SECRET
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Token Exchange
|
|
100
|
+
|
|
101
|
+
**Response:**
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"access_token": "...",
|
|
105
|
+
"token_type": "Bearer",
|
|
106
|
+
"expires_in": 3600,
|
|
107
|
+
"refresh_token": "...",
|
|
108
|
+
"scope": "openid profile data:read"
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- `refresh_token` included automatically for private clients
|
|
113
|
+
- Public clients must request `offline_access` scope
|
|
114
|
+
|
|
115
|
+
## Refresh Tokens
|
|
116
|
+
|
|
117
|
+
Exchange a refresh token for a new access token:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
POST https://app.sweatstack.no/api/v1/oauth/token
|
|
121
|
+
Content-Type: application/x-www-form-urlencoded
|
|
122
|
+
|
|
123
|
+
grant_type=refresh_token&
|
|
124
|
+
refresh_token=REFRESH_TOKEN&
|
|
125
|
+
client_id=CLIENT_ID
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
For private clients, include `client_secret`.
|
|
129
|
+
|
|
130
|
+
## API Authentication
|
|
131
|
+
|
|
132
|
+
All API requests require the Authorization header:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Authorization: Bearer ACCESS_TOKEN
|
|
136
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# SweatStack CLI
|
|
2
|
+
|
|
3
|
+
Command-line tool for authentication, app management, and static deployments.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
**macOS/Linux:**
|
|
8
|
+
```bash
|
|
9
|
+
curl -LsSf sweatstack.no/install-cli | sh
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Windows:**
|
|
13
|
+
```powershell
|
|
14
|
+
powershell -ExecutionPolicy ByPass -c "irm sweatstack.no/install-cli-windows | iex"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Verify with `sweatstack --version`. Alternative: `uv pip install sweatstack-cli`
|
|
18
|
+
|
|
19
|
+
## Authentication
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
sweatstack login # Opens browser for OAuth2, stores credentials locally
|
|
23
|
+
sweatstack logout # Remove stored credentials
|
|
24
|
+
sweatstack status # Show auth status and token details
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Always `sweatstack status` to check if the user is authenticated.
|
|
28
|
+
|
|
29
|
+
## Create Applications
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
sweatstack app create "My App"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Options:**
|
|
36
|
+
|
|
37
|
+
| Flag | Purpose |
|
|
38
|
+
|------|---------|
|
|
39
|
+
| `--page myapp` | Associate with a SweatStack Page (auto-configures redirect URI) |
|
|
40
|
+
| `--secret` | Generate client secret for confidential clients |
|
|
41
|
+
| `--env` | Write credentials to `.env` file |
|
|
42
|
+
| `--env-file PATH` | Write credentials to specific file |
|
|
43
|
+
| `--json` | Output as JSON for scripting |
|
|
44
|
+
|
|
45
|
+
**Full example:**
|
|
46
|
+
```bash
|
|
47
|
+
sweatstack app create "My Streamlit App" --page my-streamlit-app --secret --env-file .env.local
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Creates a private application. Public apps require the web dashboard. Page slugs can be rejected if unavailable.
|
|
51
|
+
|
|
52
|
+
## Deploy Static Sites
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
sweatstack page deploy myapp ./dist
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Uploads files to `https://myapp.sweatstack.pages.dev`.
|
|
59
|
+
|
|
60
|
+
**Constraints:**
|
|
61
|
+
- Pages must be pre-created via dashboard: https://app.sweatstack.no/settings/api
|
|
62
|
+
- 10MB limit per app
|
|
63
|
+
|
|
64
|
+
## Help
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
sweatstack --help
|
|
68
|
+
sweatstack app --help
|
|
69
|
+
sweatstack app create --help
|
|
70
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# PyScript Web Apps
|
|
2
|
+
|
|
3
|
+
Use when the user needs Python data science libraries (pandas, numpy, scikit-learn, scipy, matplotlib) in the browser. No backend — static deployment.
|
|
4
|
+
|
|
5
|
+
## Trade-offs vs Vanilla JS Webapp
|
|
6
|
+
|
|
7
|
+
| | PyScript | Vanilla JS ([webapp.md](webapp.md)) |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| **Best for** | Data analysis, ML, statistical computing | Dashboards, visualizations, simple data display |
|
|
10
|
+
| **Libraries** | Full Python ecosystem via Pyodide | Browser JS only |
|
|
11
|
+
| **Cold start** | 10-20s (loading Pyodide + packages) | Instant |
|
|
12
|
+
| **Computation** | Client-side Python (slower than native) | Client-side JS (fast) |
|
|
13
|
+
|
|
14
|
+
Confirm with user before choosing PyScript — the cold start is significant.
|
|
15
|
+
|
|
16
|
+
## Template
|
|
17
|
+
|
|
18
|
+
**Repo:** https://github.com/sweatstack/sweatstack-pyscript-template
|
|
19
|
+
|
|
20
|
+
Clone the template to start. The README covers setup, the ctx API, parameter patterns, examples, and deployment.
|
|
21
|
+
|
|
22
|
+
## Workflow
|
|
23
|
+
|
|
24
|
+
1. Clone the template repo
|
|
25
|
+
2. Register an OAuth app with `sweatstack auth register`
|
|
26
|
+
3. Set Client ID in `index.html`
|
|
27
|
+
4. Write analysis logic in `main.py`
|
|
28
|
+
5. Deploy with `sweatstack apps deploy`
|
|
29
|
+
|
|
30
|
+
## Key Constraints
|
|
31
|
+
|
|
32
|
+
- **Only edit `main.py` and `index.html`.** Do not generate or modify `app.js`, `worker.py`, `runtime.py`, CSS, or `mini-coi.js` — these are framework files maintained by the template.
|
|
33
|
+
- The template includes 4 working examples (`examples/`) — use these as reference for different patterns (single activity, multi-parameter, ML pipelines).
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Building Single-File Web Apps
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Tech Stack](#tech-stack)
|
|
6
|
+
- [Output Location](#output-location)
|
|
7
|
+
- [Quality Bar](#quality-bar)
|
|
8
|
+
- [Prerequisites](#prerequisites)
|
|
9
|
+
- [Authentication](#authentication)
|
|
10
|
+
- [HTML Structure](#html-structure)
|
|
11
|
+
- [Default Styling](#default-styling)
|
|
12
|
+
- [SweatStack Branding](#sweatstack-branding)
|
|
13
|
+
- [After Generation](#after-generation)
|
|
14
|
+
- [Reference Apps](#reference-apps)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Tech Stack
|
|
19
|
+
|
|
20
|
+
- Single HTML file with inline `<style>` and `<script>`
|
|
21
|
+
- Vanilla JS only (no React, no build step)
|
|
22
|
+
- CDN dependencies when needed (e.g., html2canvas for sharing)
|
|
23
|
+
- localStorage for access token and user preferences
|
|
24
|
+
- DuckDB-WASM for Parquet parsing (supports full SQL for analytics)
|
|
25
|
+
|
|
26
|
+
## Output Location
|
|
27
|
+
|
|
28
|
+
Place generated files in `public/index.html`. Creates a deployment-ready structure.
|
|
29
|
+
|
|
30
|
+
## Quality Bar
|
|
31
|
+
|
|
32
|
+
UI can be minimal - focus on functionality. Auth and API error handling must be solid: handle 401s, show clear error states, never fail silently.
|
|
33
|
+
|
|
34
|
+
## Prerequisites
|
|
35
|
+
|
|
36
|
+
Requires a **Client ID** from a SweatStack application.
|
|
37
|
+
|
|
38
|
+
**If user provides a client_id:** Use it directly, skip app creation.
|
|
39
|
+
|
|
40
|
+
**Otherwise, create an app with the SweatStack CLI:**
|
|
41
|
+
1. Derive app name from the task (e.g., "heart rate analyzer" → "Heart Rate Analyzer")
|
|
42
|
+
2. Generate page slug from app name: lowercase, spaces to hyphens (e.g., "heart-rate-analyzer")
|
|
43
|
+
3. Create the app with the sweatstack cli
|
|
44
|
+
4. If slug is rejected, prompt user for alternative
|
|
45
|
+
|
|
46
|
+
Prompt user for app name only if it cannot be reasonably inferred from the task.
|
|
47
|
+
|
|
48
|
+
## Authentication
|
|
49
|
+
|
|
50
|
+
Browser apps use PKCE flow (public client, no client secret).
|
|
51
|
+
|
|
52
|
+
**Critical:** Always include `prompt=none` in auth redirects to avoid double consent screens.
|
|
53
|
+
|
|
54
|
+
**Token storage:** Use localStorage. Store `access_token` and expiry timestamp.
|
|
55
|
+
|
|
56
|
+
**On page load:** No token → show login. Expired token → silent re-auth with `prompt=none`.
|
|
57
|
+
|
|
58
|
+
**On 401:** Silent re-auth with `prompt=none`.
|
|
59
|
+
|
|
60
|
+
**Constants to define:**
|
|
61
|
+
```javascript
|
|
62
|
+
const CLIENT_ID = '...';
|
|
63
|
+
const REDIRECT_URI = window.location.origin + window.location.pathname; // Works for localhost and production
|
|
64
|
+
const AUTH_URL = 'https://app.sweatstack.no/oauth/authorize';
|
|
65
|
+
const TOKEN_URL = 'https://app.sweatstack.no/api/v1/oauth/token';
|
|
66
|
+
const API_BASE = 'https://app.sweatstack.no/api/v1';
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## HTML Structure
|
|
70
|
+
|
|
71
|
+
- Auth container (login button): shown when no token
|
|
72
|
+
- App container (main UI): shown after authentication
|
|
73
|
+
|
|
74
|
+
## Default Styling
|
|
75
|
+
|
|
76
|
+
Use only when user doesn't specify styling preferences.
|
|
77
|
+
|
|
78
|
+
**Aesthetic direction:** Minimal and clean. Think scientific paper, not dashboard.
|
|
79
|
+
|
|
80
|
+
**Layout:**
|
|
81
|
+
- Centered container, `max-width: 48rem`
|
|
82
|
+
- Visual elements fill container width
|
|
83
|
+
- Full-width only for apps that are primarily visual (e.g., mapping app)
|
|
84
|
+
|
|
85
|
+
**Styling:**
|
|
86
|
+
- Clear heading hierarchy with generous whitespace
|
|
87
|
+
- Light background, dark text
|
|
88
|
+
- Everything flows vertically — no sidebars, no cards, no visual clutter
|
|
89
|
+
|
|
90
|
+
**Typography:** System fonts are acceptable for minimal aesthetic. If choosing a web font, make it intentional — one distinctive font, not generic defaults like Inter or Roboto.
|
|
91
|
+
|
|
92
|
+
**What to avoid:** Generic "AI slop" — purple gradients, card-heavy layouts, excessive shadows, decorative elements that add no meaning. Minimalism requires precision in spacing and typography, not decoration.
|
|
93
|
+
|
|
94
|
+
## SweatStack Branding
|
|
95
|
+
|
|
96
|
+
**Login page content:**
|
|
97
|
+
- Brief explanation of what the app does
|
|
98
|
+
- Text explaining the app uses SweatStack to access sports data (e.g., "This app connects to SweatStack to access your workout data")
|
|
99
|
+
|
|
100
|
+
**Login button** (Norwegian flag red):
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<button onclick="startAuth()" style="
|
|
104
|
+
background: #EF2B2D;
|
|
105
|
+
color: white;
|
|
106
|
+
border: none;
|
|
107
|
+
padding: 0.75rem 1.5rem;
|
|
108
|
+
font-size: 1rem;
|
|
109
|
+
border-radius: 4px;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
">Connect with SweatStack</button>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Logout button:** Top right of main content area (not viewport). Clears sessionStorage and returns to login view.
|
|
115
|
+
|
|
116
|
+
**Footer** (always present):
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<footer style="color: #666; font-size: 0.875rem; text-align: center; margin-top: 3rem;">
|
|
120
|
+
Powered by <a href="https://sweatstack.no" style="color: #666;">SweatStack</a> ·
|
|
121
|
+
Built with <a href="https://github.com/sweatStack/sweatstack-skills" style="color: #666;">Skills</a>
|
|
122
|
+
</footer>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## After Generation
|
|
126
|
+
|
|
127
|
+
**Preview locally:** Suggest user start a local file server in the `public/` directory.
|
|
128
|
+
|
|
129
|
+
**Next steps to mention:**
|
|
130
|
+
- **Deploy to the web:** `sweatstack page deploy <slug> ./public`
|
|
131
|
+
- **Make available to other users:** Add privacy policy and app description at `https://app.sweatstack.no/applications/{APP_ID}`
|
|
132
|
+
|
|
133
|
+
## Reference Apps
|
|
134
|
+
|
|
135
|
+
Study these for patterns:
|
|
136
|
+
|
|
137
|
+
- **graph.sweatstack.no** - Activity contribution graph
|
|
138
|
+
- **zones.sweatstack.no** - Training zone calculator
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sweatstack-cli
|
|
3
|
+
Version: 0.7.1
|
|
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
|
+
Requires Python 3.11+. We recommend [uv](https://docs.astral.sh/uv/) for installation.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Run directly without installing
|
|
41
|
+
uvx sweatstack-cli
|
|
42
|
+
|
|
43
|
+
# Or install as a tool
|
|
44
|
+
uv tool install sweatstack-cli
|
|
45
|
+
|
|
46
|
+
# Or with pip
|
|
47
|
+
pip install sweatstack-cli
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
To upgrade:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv tool upgrade sweatstack-cli
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
To reinstall (e.g. after a broken install or Python version change):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uvx --reinstall sweatstack-cli
|
|
60
|
+
# or if installed as a tool
|
|
61
|
+
uv tool install --reinstall sweatstack-cli
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Authenticate with SweatStack
|
|
68
|
+
sweatstack login
|
|
69
|
+
|
|
70
|
+
# Create an app and deploy
|
|
71
|
+
sweatstack app create "My App" --page my-page --env
|
|
72
|
+
sweatstack page deploy
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Commands
|
|
76
|
+
|
|
77
|
+
### Authentication
|
|
78
|
+
|
|
79
|
+
| Command | Description |
|
|
80
|
+
|---------|-------------|
|
|
81
|
+
| `sweatstack login` | Authenticate via browser (OAuth2 PKCE) |
|
|
82
|
+
| `sweatstack login --force` | Force re-authentication |
|
|
83
|
+
| `sweatstack logout` | Remove stored credentials |
|
|
84
|
+
| `sweatstack status` | Show auth status, user info, and version |
|
|
85
|
+
| `sweatstack --version` | Show version and check for updates |
|
|
86
|
+
|
|
87
|
+
### Application Management
|
|
88
|
+
|
|
89
|
+
| Command | Description |
|
|
90
|
+
|---------|-------------|
|
|
91
|
+
| `sweatstack app create NAME` | Create a new private OAuth2 application |
|
|
92
|
+
| `sweatstack app link` | Link current directory to an existing application |
|
|
93
|
+
|
|
94
|
+
#### `app create` options
|
|
95
|
+
|
|
96
|
+
| Option | Description |
|
|
97
|
+
|--------|-------------|
|
|
98
|
+
| `--description, -d TEXT` | Application description (max 500 chars) |
|
|
99
|
+
| `--page, -p SLUG` | Associate with a SweatStack Page |
|
|
100
|
+
| `--secret, -s` | Generate a client secret |
|
|
101
|
+
| `--env` | Write credentials to `.env` |
|
|
102
|
+
| `--env-file PATH` | Write credentials to a specific file |
|
|
103
|
+
| `--json` | Output as JSON |
|
|
104
|
+
|
|
105
|
+
#### `app link` options
|
|
106
|
+
|
|
107
|
+
| Option | Description |
|
|
108
|
+
|--------|-------------|
|
|
109
|
+
| `--force, -f` | Overwrite existing `sweatstack.toml` |
|
|
110
|
+
|
|
111
|
+
### Project Configuration
|
|
112
|
+
|
|
113
|
+
Both `app create` and `app link` generate a `sweatstack.toml` in the current directory. Other commands read defaults from this file.
|
|
114
|
+
|
|
115
|
+
```toml
|
|
116
|
+
[app]
|
|
117
|
+
name = "My App"
|
|
118
|
+
client_id = "abc123"
|
|
119
|
+
|
|
120
|
+
[page]
|
|
121
|
+
slug = "my-page"
|
|
122
|
+
directory = "dist"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Page Deployment
|
|
126
|
+
|
|
127
|
+
Deploy static sites to SweatStack Pages.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Deploy using sweatstack.toml defaults (recommended)
|
|
131
|
+
sweatstack page deploy
|
|
132
|
+
|
|
133
|
+
# Or specify slug and directory directly
|
|
134
|
+
sweatstack page deploy my-page ./dist
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# SweatStack CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for [SweatStack](https://sweatstack.no) — the sports data platform for developers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Requires Python 3.11+. We recommend [uv](https://docs.astral.sh/uv/) for installation.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Run directly without installing
|
|
11
|
+
uvx sweatstack-cli
|
|
12
|
+
|
|
13
|
+
# Or install as a tool
|
|
14
|
+
uv tool install sweatstack-cli
|
|
15
|
+
|
|
16
|
+
# Or with pip
|
|
17
|
+
pip install sweatstack-cli
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
To upgrade:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv tool upgrade sweatstack-cli
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
To reinstall (e.g. after a broken install or Python version change):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
uvx --reinstall sweatstack-cli
|
|
30
|
+
# or if installed as a tool
|
|
31
|
+
uv tool install --reinstall sweatstack-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Authenticate with SweatStack
|
|
38
|
+
sweatstack login
|
|
39
|
+
|
|
40
|
+
# Create an app and deploy
|
|
41
|
+
sweatstack app create "My App" --page my-page --env
|
|
42
|
+
sweatstack page deploy
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Commands
|
|
46
|
+
|
|
47
|
+
### Authentication
|
|
48
|
+
|
|
49
|
+
| Command | Description |
|
|
50
|
+
|---------|-------------|
|
|
51
|
+
| `sweatstack login` | Authenticate via browser (OAuth2 PKCE) |
|
|
52
|
+
| `sweatstack login --force` | Force re-authentication |
|
|
53
|
+
| `sweatstack logout` | Remove stored credentials |
|
|
54
|
+
| `sweatstack status` | Show auth status, user info, and version |
|
|
55
|
+
| `sweatstack --version` | Show version and check for updates |
|
|
56
|
+
|
|
57
|
+
### Application Management
|
|
58
|
+
|
|
59
|
+
| Command | Description |
|
|
60
|
+
|---------|-------------|
|
|
61
|
+
| `sweatstack app create NAME` | Create a new private OAuth2 application |
|
|
62
|
+
| `sweatstack app link` | Link current directory to an existing application |
|
|
63
|
+
|
|
64
|
+
#### `app create` options
|
|
65
|
+
|
|
66
|
+
| Option | Description |
|
|
67
|
+
|--------|-------------|
|
|
68
|
+
| `--description, -d TEXT` | Application description (max 500 chars) |
|
|
69
|
+
| `--page, -p SLUG` | Associate with a SweatStack Page |
|
|
70
|
+
| `--secret, -s` | Generate a client secret |
|
|
71
|
+
| `--env` | Write credentials to `.env` |
|
|
72
|
+
| `--env-file PATH` | Write credentials to a specific file |
|
|
73
|
+
| `--json` | Output as JSON |
|
|
74
|
+
|
|
75
|
+
#### `app link` options
|
|
76
|
+
|
|
77
|
+
| Option | Description |
|
|
78
|
+
|--------|-------------|
|
|
79
|
+
| `--force, -f` | Overwrite existing `sweatstack.toml` |
|
|
80
|
+
|
|
81
|
+
### Project Configuration
|
|
82
|
+
|
|
83
|
+
Both `app create` and `app link` generate a `sweatstack.toml` in the current directory. Other commands read defaults from this file.
|
|
84
|
+
|
|
85
|
+
```toml
|
|
86
|
+
[app]
|
|
87
|
+
name = "My App"
|
|
88
|
+
client_id = "abc123"
|
|
89
|
+
|
|
90
|
+
[page]
|
|
91
|
+
slug = "my-page"
|
|
92
|
+
directory = "dist"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Page Deployment
|
|
96
|
+
|
|
97
|
+
Deploy static sites to SweatStack Pages.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Deploy using sweatstack.toml defaults (recommended)
|
|
101
|
+
sweatstack page deploy
|
|
102
|
+
|
|
103
|
+
# Or specify slug and directory directly
|
|
104
|
+
sweatstack page deploy my-page ./dist
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT
|
|
@@ -19,6 +19,12 @@ app = typer.Typer(name="app", help="Manage OAuth2 applications.")
|
|
|
19
19
|
@app.command()
|
|
20
20
|
def create(
|
|
21
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
|
+
),
|
|
22
28
|
page: str | None = typer.Option(
|
|
23
29
|
None,
|
|
24
30
|
"--page",
|
|
@@ -54,6 +60,9 @@ def create(
|
|
|
54
60
|
|
|
55
61
|
body: dict[str, str | bool] = {"name": name}
|
|
56
62
|
|
|
63
|
+
if description:
|
|
64
|
+
body["description"] = description
|
|
65
|
+
|
|
57
66
|
if page:
|
|
58
67
|
body["page"] = page
|
|
59
68
|
|
|
@@ -9,6 +9,7 @@ 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
|
|
14
15
|
from sweatstack_cli.project import CONFIG_FILENAME, ProjectConfig
|
|
@@ -146,3 +147,14 @@ def deploy(
|
|
|
146
147
|
console.print(f"[green]✓[/green] Deployed [bold]{page_slug}[/bold] successfully!")
|
|
147
148
|
console.print(f" Files: {file_count}")
|
|
148
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}")
|
sweatstack_cli-0.6.0/PKG-INFO
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: sweatstack-cli
|
|
3
|
-
Version: 0.6.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.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
|
sweatstack_cli-0.6.0/README.md
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# SweatStack CLI
|
|
2
|
-
|
|
3
|
-
Command-line interface for [SweatStack](https://sweatstack.no) — the sports data platform for developers.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pip install sweatstack-cli
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Requires Python 3.13+.
|
|
12
|
-
|
|
13
|
-
## Quick Start
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
# Authenticate with SweatStack
|
|
17
|
-
sweatstack login
|
|
18
|
-
|
|
19
|
-
# Check who you're logged in as
|
|
20
|
-
sweatstack whoami
|
|
21
|
-
|
|
22
|
-
# Deploy a static site
|
|
23
|
-
sweatstack pages deploy ./dist --prod
|
|
24
|
-
|
|
25
|
-
# View authentication status
|
|
26
|
-
sweatstack status
|
|
27
|
-
|
|
28
|
-
# Logout
|
|
29
|
-
sweatstack logout
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Commands
|
|
33
|
-
|
|
34
|
-
### Authentication
|
|
35
|
-
|
|
36
|
-
| Command | Description |
|
|
37
|
-
|---------|-------------|
|
|
38
|
-
| `sweatstack login` | Authenticate via browser |
|
|
39
|
-
| `sweatstack login --force` | Force re-authentication |
|
|
40
|
-
| `sweatstack logout` | Remove stored credentials |
|
|
41
|
-
| `sweatstack whoami` | Show current user |
|
|
42
|
-
| `sweatstack status` | Show token status and expiry |
|
|
43
|
-
|
|
44
|
-
### Pages
|
|
45
|
-
|
|
46
|
-
| Command | Description |
|
|
47
|
-
|---------|-------------|
|
|
48
|
-
| `sweatstack pages deploy <dir>` | Deploy static site |
|
|
49
|
-
| `sweatstack pages deploy --prod` | Deploy to production |
|
|
50
|
-
| `sweatstack pages list` | List all sites |
|
|
51
|
-
| `sweatstack pages delete <name>` | Delete a site |
|
|
52
|
-
|
|
53
|
-
## CI/CD Usage
|
|
54
|
-
|
|
55
|
-
For automated environments, use environment variables instead of interactive login:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
export SWEATSTACK_API_KEY="your-access-token"
|
|
59
|
-
export SWEATSTACK_REFRESH_TOKEN="your-refresh-token"
|
|
60
|
-
|
|
61
|
-
sweatstack pages deploy ./dist --prod
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Configuration
|
|
65
|
-
|
|
66
|
-
| Environment Variable | Description | Default |
|
|
67
|
-
|---------------------|-------------|---------|
|
|
68
|
-
| `SWEATSTACK_URL` | API base URL | `https://app.sweatstack.no` |
|
|
69
|
-
| `SWEATSTACK_API_KEY` | Access token | — |
|
|
70
|
-
| `SWEATSTACK_REFRESH_TOKEN` | Refresh token | — |
|
|
71
|
-
|
|
72
|
-
## Token Storage
|
|
73
|
-
|
|
74
|
-
Credentials are stored securely in your OS user data directory:
|
|
75
|
-
|
|
76
|
-
- **macOS**: `~/Library/Application Support/SweatStack/SweatStack/tokens.json`
|
|
77
|
-
- **Linux**: `~/.local/share/SweatStack/SweatStack/tokens.json`
|
|
78
|
-
- **Windows**: `%APPDATA%\SweatStack\SweatStack\tokens.json`
|
|
79
|
-
|
|
80
|
-
This location is shared with the [sweatstack Python library](https://github.com/sweatstack/sweatstack-python), so authenticating with either tool works for both.
|
|
81
|
-
|
|
82
|
-
## Development
|
|
83
|
-
|
|
84
|
-
See [DEVELOPMENT.md](DEVELOPMENT.md) for development setup and contribution guidelines.
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
# Install with dev dependencies
|
|
88
|
-
uv pip install -e ".[dev]"
|
|
89
|
-
|
|
90
|
-
# Run tests
|
|
91
|
-
pytest
|
|
92
|
-
|
|
93
|
-
# Lint and format
|
|
94
|
-
ruff check . && ruff format .
|
|
95
|
-
|
|
96
|
-
# Type check
|
|
97
|
-
mypy src/sweatstack_cli
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## License
|
|
101
|
-
|
|
102
|
-
MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|