slab-cli 0.1.0__tar.gz → 0.3.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.
- {slab_cli-0.1.0 → slab_cli-0.3.0}/PKG-INFO +1 -1
- {slab_cli-0.1.0 → slab_cli-0.3.0}/pyproject.toml +1 -1
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/client.py +0 -5
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/catalog.py +8 -4
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/registry.py +0 -1
- slab_cli-0.3.0/src/slab_cli/commands/setup.py +23 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/context.py +11 -5
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/help.py +16 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/main.py +3 -3
- slab_cli-0.1.0/src/slab_cli/commands/setup.py +0 -24
- {slab_cli-0.1.0 → slab_cli-0.3.0}/README.md +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/__init__.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/banner.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/__init__.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/breaks.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/collection.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/custom_sets.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/export.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/lots.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/commands/pricing.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/config.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/display.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/flags.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/paging.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/picker.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/prompts.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/sources.py +0 -0
- {slab_cli-0.1.0 → slab_cli-0.3.0}/src/slab_cli/theme.py +0 -0
|
@@ -36,7 +36,6 @@ from slab_schemas.collection import (
|
|
|
36
36
|
CardCopyUpdate,
|
|
37
37
|
CollectionResult,
|
|
38
38
|
CollectionSearchQuery,
|
|
39
|
-
CollectorCreate,
|
|
40
39
|
CollectorOut,
|
|
41
40
|
CollectorUpdate,
|
|
42
41
|
)
|
|
@@ -207,10 +206,6 @@ class SlabClient:
|
|
|
207
206
|
|
|
208
207
|
# --- collectors ---
|
|
209
208
|
|
|
210
|
-
def create_collector(self, name: str) -> CollectorOut:
|
|
211
|
-
resp = self._request("POST", "/collectors", json=CollectorCreate(name=name).model_dump(mode="json"))
|
|
212
|
-
return CollectorOut.model_validate(resp.json())
|
|
213
|
-
|
|
214
209
|
def rename_collector(self, collector_uuid: str, name: str) -> CollectorOut:
|
|
215
210
|
resp = self._request(
|
|
216
211
|
"PATCH", f"/collectors/{collector_uuid}", json=CollectorUpdate(name=name).model_dump(mode="json")
|
|
@@ -21,10 +21,14 @@ def cmd_search(args: list[str]) -> None:
|
|
|
21
21
|
The same flags work on `slab collection search` — learn them once.
|
|
22
22
|
|
|
23
23
|
Examples:
|
|
24
|
-
slab card search McDavid --rookie
|
|
25
|
-
slab card search Bedard --year 2025 --numbered
|
|
26
|
-
slab card search --
|
|
27
|
-
slab card search --
|
|
24
|
+
slab card search McDavid --rookie # free text + one filter
|
|
25
|
+
slab card search Bedard --year 2025 --numbered # season + serial-numbered only
|
|
26
|
+
slab card search --team Bruins --team Leafs # any-of: either team
|
|
27
|
+
slab card search --brand "Upper Deck" --run-min 1 --run-max 99 # print-run range
|
|
28
|
+
slab card search McDavid --auto --parallel --sort -year # stack modifiers, newest first
|
|
29
|
+
slab card search --subset "Young Guns" --rookie --limit 50
|
|
30
|
+
|
|
31
|
+
Every flag combines with AND; repeat an any-of flag (--team, --brand, --finish…) to widen it.
|
|
28
32
|
|
|
29
33
|
Sort keys: year, card_number, numbered, subject, brand, set (prefix - for descending).
|
|
30
34
|
"""
|
|
@@ -104,7 +104,6 @@ GROUPS: list[Group] = [
|
|
|
104
104
|
Command("delete", "Delete a set you created", custom_sets.cmd_chase_delete),
|
|
105
105
|
]),
|
|
106
106
|
Group("collector", "your identity", [
|
|
107
|
-
Command("create", "Create a collector", setup.cmd_register, "<name>"),
|
|
108
107
|
Command("current", "Show the active collector", setup.cmd_whoami),
|
|
109
108
|
]),
|
|
110
109
|
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Setup commands — check identity.
|
|
2
|
+
|
|
3
|
+
Collectors are NOT created here. Your account and its collector are provisioned when you sign up
|
|
4
|
+
in the portal; the CLI just consumes the API key and collector UUID you get there. When
|
|
5
|
+
SLAB_COLLECTOR is unset the CLI falls back to your account's default collector (see context.py),
|
|
6
|
+
so a solo user only needs their API key.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from ..config import get_collector_uuid
|
|
12
|
+
from ..display import console
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def cmd_whoami(args: list[str]) -> None:
|
|
16
|
+
uuid = get_collector_uuid()
|
|
17
|
+
if uuid:
|
|
18
|
+
console.print(f"Active collector: [bold]{uuid}[/bold]")
|
|
19
|
+
else:
|
|
20
|
+
console.print(
|
|
21
|
+
"[dim]SLAB_COLLECTOR not set — the CLI will use your account's default collector.\n"
|
|
22
|
+
"Find your collector UUID and API key in the portal.[/dim]"
|
|
23
|
+
)
|
|
@@ -31,15 +31,21 @@ class CliContext:
|
|
|
31
31
|
|
|
32
32
|
@property
|
|
33
33
|
def collector(self) -> str:
|
|
34
|
-
"""The active collector UUID. Resolution order: SLAB_COLLECTOR env var, then
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
"""The active collector UUID. Resolution order: SLAB_COLLECTOR env var, then — for a
|
|
35
|
+
single-collector (`collector`) account only — the account's default collector, so a solo
|
|
36
|
+
user needs just their API key. A `developer` account owns many collectors (one per
|
|
37
|
+
end-user), so there is NO safe default: it must set SLAB_COLLECTOR explicitly, or this
|
|
38
|
+
raises MissingCollector. Cached per invocation."""
|
|
37
39
|
if self._collector:
|
|
38
40
|
return self._collector
|
|
39
41
|
uuid = get_collector_uuid()
|
|
40
42
|
if not uuid:
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
+
# No explicit collector. Only a single-collector account may zero-config fall back to
|
|
44
|
+
# its default; a developer account has no unambiguous default and must set one.
|
|
45
|
+
me = self.client.get_account_context()
|
|
46
|
+
if me.account.account_type == "developer":
|
|
47
|
+
raise MissingCollector
|
|
48
|
+
uuid = me.default_collector_uuid
|
|
43
49
|
if not uuid:
|
|
44
50
|
raise MissingCollector
|
|
45
51
|
self._collector = uuid
|
|
@@ -66,9 +66,25 @@ def render_command_help(group: Group, command: Command) -> None:
|
|
|
66
66
|
for f in flags:
|
|
67
67
|
grid.add_row(f" {f.usage()}", f.help)
|
|
68
68
|
console.print(grid)
|
|
69
|
+
_render_query_grammar()
|
|
69
70
|
console.print()
|
|
70
71
|
|
|
71
72
|
|
|
73
|
+
def _render_query_grammar() -> None:
|
|
74
|
+
"""A generic primer on how the search grammar composes — printed under every command's flag
|
|
75
|
+
table. It describes the mechanics (free text, any-of, ranges, AND), never specific flags, so it
|
|
76
|
+
stays true no matter how the flag list evolves."""
|
|
77
|
+
console.print(Text.from_markup(
|
|
78
|
+
"\n [slab.label]Writing a query[/]"
|
|
79
|
+
"\n [slab.dim]Bare words are a free-text search (player, set…).[/]"
|
|
80
|
+
"\n [slab.dim]Flags narrow it, and every flag you add combines with AND.[/]"
|
|
81
|
+
"\n [slab.dim]Repeat an 'any-of' flag to match several values:[/] "
|
|
82
|
+
"[slab.value]--team Bruins --team Leafs[/]"
|
|
83
|
+
"\n [slab.dim]Pair a[/] [slab.value]--…-min[/] [slab.dim]with its[/] [slab.value]--…-max[/] "
|
|
84
|
+
"[slab.dim]for a range:[/] [slab.value]--run-min 1 --run-max 99[/]"
|
|
85
|
+
))
|
|
86
|
+
|
|
87
|
+
|
|
72
88
|
def render_root_help() -> None:
|
|
73
89
|
"""`slab` — the whole command surface, grouped by noun. The discovery home."""
|
|
74
90
|
console.print(BANNER)
|
|
@@ -73,9 +73,9 @@ def main() -> None:
|
|
|
73
73
|
console.print(f"[red]{e}[/red]")
|
|
74
74
|
sys.exit(2)
|
|
75
75
|
except MissingCollector:
|
|
76
|
-
console.print("[red]No collector
|
|
77
|
-
console.print("
|
|
78
|
-
console.print("
|
|
76
|
+
console.print("[red]No collector resolved for this API key.[/red]")
|
|
77
|
+
console.print(" Your collector is created when you sign up in the portal.")
|
|
78
|
+
console.print(" Grab its UUID there, then: export SLAB_COLLECTOR=<uuid>")
|
|
79
79
|
sys.exit(1)
|
|
80
80
|
except ApiConnectionError as e:
|
|
81
81
|
console.print(f"\n[red]{e}[/red]")
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"""Setup commands — register a collector, check identity."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from ..config import get_collector_uuid
|
|
6
|
-
from ..context import ctx
|
|
7
|
-
from ..display import console
|
|
8
|
-
from ..prompts import ask_text
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def cmd_register(args: list[str]) -> None:
|
|
12
|
-
name = " ".join(args) if args else ask_text("Collector name:")
|
|
13
|
-
collector = ctx.client.create_collector(name)
|
|
14
|
-
console.print(f"\n[green]Collector created:[/green] {collector.name}")
|
|
15
|
-
console.print(f" UUID: {collector.uuid}")
|
|
16
|
-
console.print(f"\n Now run: [bold]export SLAB_COLLECTOR={collector.uuid}[/bold]")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def cmd_whoami(args: list[str]) -> None:
|
|
20
|
-
uuid = get_collector_uuid()
|
|
21
|
-
if uuid:
|
|
22
|
-
console.print(f"Active collector: [bold]{uuid}[/bold]")
|
|
23
|
-
else:
|
|
24
|
-
console.print("[dim]No collector set. Run: slab collector create <name>[/dim]")
|
|
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
|