cubctl 1.0.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.
cubctl-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pavel Pikta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
cubctl-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,396 @@
1
+ Metadata-Version: 2.4
2
+ Name: cubctl
3
+ Version: 1.0.0
4
+ Summary: CLI and Python client for the CUB REST API (cub.rip)
5
+ Author: Pavel Pikta
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/pavelpikta/lampa-cub-cli
8
+ Project-URL: Repository, https://github.com/pavelpikta/lampa-cub-cli
9
+ Project-URL: Documentation, https://github.com/pavelpikta/lampa-cub-cli#readme
10
+ Project-URL: Issues, https://github.com/pavelpikta/lampa-cub-cli/issues
11
+ Keywords: cub,cub.rip,lampa,cli,bookmarks,migration
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Internet :: WWW/HTTP
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: requests>=2.28.0
27
+ Provides-Extra: test
28
+ Requires-Dist: pytest>=7.0; extra == "test"
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0; extra == "dev"
31
+ Requires-Dist: build>=1.0; extra == "dev"
32
+ Requires-Dist: twine>=5.0; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # cubctl
36
+
37
+ CLI and Python library for the [CUB REST API](https://cub.rip/developer/) (cub.rip).
38
+
39
+ ## Quick start
40
+
41
+ ```bash
42
+ pip install cubctl
43
+ cubctl --help
44
+ ```
45
+
46
+ Development install from this repo:
47
+
48
+ ```bash
49
+ pip install -e ".[dev]"
50
+ cubctl --help
51
+ # or: python -m cubctl --help
52
+ ```
53
+
54
+ The package lives under `src/cubctl/`. From the repo root, `cubctl` is available after `pip install -e .` (or `PYTHONPATH=src python -m cubctl`).
55
+
56
+ ## Concepts (AWS-style)
57
+
58
+ | Term | CLI flag | Config | Description |
59
+ |------|----------|--------|-------------|
60
+ | **Account slot** | `--account src\|target` | `account_src` / `account_target` | Saved API token + default profile ID (like AWS named profiles, fixed as `src` and `target` for migrations) |
61
+ | **CUB profile** | `--profile ID` | per-slot `profile` field | User profile within an account (Pavel, DEVOPS, …). Many commands need an explicit profile ID |
62
+
63
+ Credential chain (highest priority first):
64
+
65
+ 1. `--account src|target` (+ optional `--profile` override)
66
+ 2. `--token` (+ optional `--profile`)
67
+ 3. `CUB_TOKEN` / `CUB_PROFILE` environment variables
68
+ 4. Default slot: `CUB_ACCOUNT` env or `account_src`
69
+
70
+ Global flags (before or after the subcommand):
71
+
72
+ ```bash
73
+ cubctl --account src --profile 311483 bookmarks all --type book
74
+ cubctl bookmarks all --account src --profile 311483 --type book
75
+ ```
76
+
77
+ ## Install
78
+
79
+ ```bash
80
+ pip install cubctl
81
+ pip install "cubctl[test]" # optional: pytest for running tests
82
+ ```
83
+
84
+ From source:
85
+
86
+ ```bash
87
+ pip install -e ".[dev]"
88
+ cubctl --help
89
+ ```
90
+
91
+ ## Authentication (device/add)
92
+
93
+ CUB uses **device-based login** — the same flow as Lampa:
94
+
95
+ 1. Open [cub.rip/add](https://cub.rip/add) while logged in to your CUB account
96
+ 2. Copy the **6-digit code** shown on the page
97
+ 3. Exchange it for a token via `POST device/add`
98
+
99
+ ```bash
100
+ cubctl device add 123456 --save # saves to src account slot in config
101
+ cubctl auth 123456 --save # alias; prefer accounts setup for src + target
102
+ ```
103
+
104
+ For **src + target** account slots:
105
+
106
+ ```bash
107
+ cubctl accounts setup # reads accounts.env in repo root
108
+ cubctl accounts show
109
+ ```
110
+
111
+ Credentials live in **`~/.cub/config.json`** — account tokens, default slot, and saved profile names. Manage with `cubctl config`:
112
+
113
+ ```bash
114
+ cubctl config show # accounts + profiles (no tokens)
115
+ cubctl config path
116
+ cubctl config set default-account src
117
+ cubctl config profile sync --account src # fetch profile names from API → config
118
+ cubctl config profile list --account src
119
+ cubctl config profile set --account src --name Pavel --id 311483
120
+ cubctl config profile default --account src --name Pavel
121
+ cubctl bookmarks all --account src --profile Pavel # name resolved from config
122
+ ```
123
+
124
+ Example `~/.cub/config.json`:
125
+
126
+ ```json
127
+ {
128
+ "default_account": "src",
129
+ "account_src": {
130
+ "token": "...",
131
+ "profile": "311483",
132
+ "email": "user@example.com",
133
+ "profiles": {
134
+ "Pavel": "311483",
135
+ "Kids": "311484"
136
+ }
137
+ },
138
+ "account_target": {
139
+ "token": "...",
140
+ "profile": "796529",
141
+ "profiles": { "Main": "796529" }
142
+ }
143
+ }
144
+ ```
145
+
146
+ The client sends these headers on every authenticated request:
147
+
148
+ | Header | Purpose |
149
+ |-----------|----------------------------------|
150
+ | `token` | API access token (required) |
151
+ | `profile` | Profile ID (required for some) |
152
+
153
+ ## Environment variables
154
+
155
+ | Variable | Description | Default |
156
+ |--------------------|------------------------------------------|------------------------|
157
+ | `CUB_TOKEN` | API token | — |
158
+ | `CUB_PROFILE` | CUB profile ID | — |
159
+ | `CUB_ACCOUNT` | Default account slot: `src` or `target` | `src` when configured |
160
+ | `CUB_CODE_SRC` | Device code for src slot (setup) | — |
161
+ | `CUB_CODE_TARGET` | Device code for target slot (setup) | — |
162
+ | `CUB_FROM_TOKEN` | Source token for migration | — |
163
+ | `CUB_TO_TOKEN` | Target token for migration | — |
164
+ | `CUB_BASE_URL` | API base URL | `https://cub.rip/api/` |
165
+ | `CUB_CONFIG_DIR` | Config directory | `~/.cub` |
166
+
167
+ `accounts.env` is auto-loaded at startup when present (see `accounts.env.example`).
168
+
169
+ ## Public vs authenticated commands
170
+
171
+ These work **without** a token:
172
+
173
+ ```bash
174
+ cubctl collections top-collectors
175
+ cubctl collections list --category new
176
+ cubctl collections roll
177
+ cubctl collections view 123
178
+ cubctl reactions get movie_539972
179
+ cubctl reactions add movie_539972 think
180
+ cubctl users find --email user@example.com
181
+ ```
182
+
183
+ All other subcommands require `--account src|target`, `--token`, or `accounts setup`.
184
+
185
+ ### Credential resolution
186
+
187
+ Every authenticated subcommand accepts:
188
+
189
+ | Flag | Env | Config |
190
+ |------|-----|--------|
191
+ | `--account src\|target` | `CUB_ACCOUNT` | `account_src` / `account_target` |
192
+ | `--token` | `CUB_TOKEN` | — (explicit override only) |
193
+ | `--profile` | `CUB_PROFILE` | slot `profile` or `profiles` map |
194
+
195
+ Order: `--account` → explicit `--token` → `account_src` → error with setup hint.
196
+
197
+ ## Profiles
198
+
199
+ List and resolve CUB profile IDs by name:
200
+
201
+ ```bash
202
+ cubctl profiles all --account src
203
+ cubctl profiles pick --account src --name Pavel
204
+ cubctl profiles pick --account target --name DEVOPS
205
+ ```
206
+
207
+ Use the returned `id` with `--profile` or `--from-profile` / `--to-profile`.
208
+
209
+ ## Bookmarks
210
+
211
+ The API has **documented filter types** (`book`, `history`, `like`, `wath`) and **internal types** returned with `--full 1` (`viewed`, `scheduled`, `look`, `thrown`, …).
212
+
213
+ ```bash
214
+ # Category counts (matches Lampa UI totals)
215
+ cubctl bookmarks counts --account src --profile 311483
216
+
217
+ # List by type
218
+ cubctl bookmarks all --account src --profile 311483 --type history
219
+
220
+ # All internal types (~400 items)
221
+ cubctl bookmarks all --account src --profile 311483 --full 1
222
+ ```
223
+
224
+ ## Premium
225
+
226
+ Timeline endpoints require [CUB Premium](https://cub.rip/premium): `timeline/all`, `changelog`, `dump`, `update`.
227
+
228
+ ## Backup (JSON v2)
229
+
230
+ Full profile snapshot including all bookmark internal types, timeline, and optional notifications.
231
+
232
+ ```bash
233
+ cubctl backup create --account src --profile 311483
234
+ cubctl backup restore --account target --profile 796529 -i backup.json --dry-run
235
+ cubctl backup restore --account target --profile 796529 -i backup.json
236
+ ```
237
+
238
+ Equivalent: `migrate export` / `migrate import` (same JSON format).
239
+
240
+ ## Account slots (src / target)
241
+
242
+ ```bash
243
+ cp accounts.env.example accounts.env
244
+ # edit CUB_CODE_SRC, CUB_CODE_TARGET, optional CUB_ACCOUNT=src
245
+ cubctl accounts setup
246
+ ```
247
+
248
+ ```bash
249
+ cubctl bookmarks all --account src --type book
250
+ cubctl migrate run \
251
+ --from src --from-profile 311483 \
252
+ --to target --to-profile 796529 \
253
+ --dry-run
254
+ ```
255
+
256
+ ## Migration
257
+
258
+ Copy **all bookmark types** + timeline (+ optional notifications) between profiles or account slots.
259
+
260
+ **Always specify profile IDs** — saved slots store the main profile from login, not named profiles like Pavel.
261
+
262
+ ```bash
263
+ # Preview
264
+ cubctl migrate run \
265
+ --from src --from-profile 311483 \
266
+ --to target --to-profile 796529 \
267
+ --dry-run
268
+
269
+ # Run with progress
270
+ cubctl migrate run \
271
+ --from src --from-profile 311483 \
272
+ --to target --to-profile 796529 \
273
+ --include bookmarks,timeline,notifications \
274
+ --progress
275
+
276
+ # Export / import (offline)
277
+ cubctl migrate export --from src --from-profile 311483 -o pavel.json
278
+ cubctl migrate import --to target --to-profile 796529 -i pavel.json --progress
279
+ ```
280
+
281
+ | Flag | Description |
282
+ |------|-------------|
283
+ | `--from` / `--to` | Account slot: `src` or `target` |
284
+ | `--from-profile` / `--to-profile` | CUB profile ID within each slot |
285
+ | `--include bookmarks,timeline,notifications` | What to migrate |
286
+ | `--merge skip` | Skip existing items (default) |
287
+ | `--merge overwrite` | Overwrite timeline entries |
288
+ | `--dry-run` | Counts only, no writes |
289
+ | `--progress` | Print each import step to stderr |
290
+ | `--quiet` | Compact JSON (export: summary only) |
291
+ | `--delay SECS` | Pause between API writes (rate limiting) |
292
+
293
+ Same account slot, two CUB profiles (one token):
294
+
295
+ ```bash
296
+ cubctl migrate run --from-profile 311484 --to-profile 311483 --dry-run
297
+ ```
298
+
299
+ ## Security notes
300
+
301
+ - Tokens are stored in plain text in `~/.cub/config.json` (local CLI tool).
302
+ - `users give --password` passes the password on the command line (visible in shell history). Prefer env vars in scripts.
303
+
304
+ ## Tests
305
+
306
+ ```bash
307
+ pip install -e ".[test]"
308
+ pytest tests/test_unit.py tests/test_migration.py tests/test_cli.py -m "not integration"
309
+ pytest tests -m integration # live API; needs CUB_TEST_DEVICE_CODE
310
+ ```
311
+
312
+ ## Project layout
313
+
314
+ ```
315
+ lampa-cub-cli/
316
+ ├── README.md
317
+ ├── pyproject.toml
318
+ ├── requirements.txt
319
+ ├── accounts.env.example
320
+ ├── tests/
321
+ └── src/cubctl/
322
+ ├── cli.py # command dispatch
323
+ ├── cli_helpers.py # auth resolution, progress, public commands
324
+ ├── client.py
325
+ ├── migration.py
326
+ └── api_spec.json
327
+ ```
328
+
329
+ ```bash
330
+ pip install -e ".[dev]" && cubctl --help
331
+ ```
332
+
333
+ ## Publish to PyPI
334
+
335
+ ### Recommended: Trusted Publisher (no long-lived token)
336
+
337
+ PyPI exchanges a short-lived token with GitHub via OIDC. You do **not** need `PYPI_API_TOKEN` in GitHub Secrets when this is configured.
338
+
339
+ **1. PyPI** — [Account → Publishing](https://pypi.org/manage/account/publishing/) (or project → Publishing after the first release)
340
+
341
+ Add a **GitHub** trusted publisher:
342
+
343
+ | Field | Value |
344
+ |-------|--------|
345
+ | Owner | `pavelpikta` |
346
+ | Repository | `lampa-cub-cli` |
347
+ | Workflow name | `publish.yml` |
348
+ | Environment | `pypi` *(optional but matches this repo’s workflow)* |
349
+
350
+ For the **first upload**, you can add a [pending publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/) before the `cubctl` project exists on PyPI.
351
+
352
+ **2. GitHub** — repo [Settings → Environments](https://github.com/pavelpikta/lampa-cub-cli/settings/environments)
353
+
354
+ Create environment **`pypi`** (optional protections: required reviewers, branch rules).
355
+
356
+ No repository secret is required for trusted publishing.
357
+
358
+ **3. Release**
359
+
360
+ ```bash
361
+ git tag v1.0.0
362
+ git push origin v1.0.0
363
+ # GitHub → Releases → Draft new release from tag v1.0.0 → Publish
364
+ ```
365
+
366
+ Or run **Actions → publish → Run workflow** manually.
367
+
368
+ The workflow uses [`pypa/gh-action-pypi-publish`](https://github.com/marketplace/actions/pypi-publish) with `id-token: write` (see `.github/workflows/publish.yml`).
369
+
370
+ ### Alternative: API token + secret
371
+
372
+ If you prefer a classic token instead of trusted publishing:
373
+
374
+ 1. PyPI → Account settings → API tokens → create token (scope: entire account or project `cubctl`)
375
+ 2. GitHub → Settings → Secrets and variables → Actions → **`PYPI_API_TOKEN`**
376
+ 3. Replace the publish step in `publish.yml` with:
377
+
378
+ ```yaml
379
+ - name: Publish to PyPI
380
+ env:
381
+ TWINE_USERNAME: __token__
382
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
383
+ run: twine upload dist/*
384
+ ```
385
+
386
+ Remove `id-token: write` if you use only the token method.
387
+
388
+ ### Manual upload (local)
389
+
390
+ ```bash
391
+ pip install build twine
392
+ python -m build
393
+ twine check dist/*
394
+ twine upload dist/*
395
+ # Username: __token__ Password: pypi-Ag... (API token)
396
+ ```