kctl-vendure 0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ *.egg
6
+ dist/
7
+ build/
8
+ .eggs/
9
+
10
+ # Virtual environments
11
+ .venv/
12
+ venv/
13
+
14
+ # IDE
15
+ .idea/
16
+ .vscode/
17
+ *.swp
18
+ *.swo
19
+
20
+ # Testing
21
+ .pytest_cache/
22
+ .coverage
23
+ htmlcov/
24
+ .mypy_cache/
25
+ .ruff_cache/
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
30
+
31
+ # Environment
32
+ .env
33
+ .env.local
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: kctl-vendure
3
+ Version: 0.6.2
4
+ Summary: Kodemeio Vendure CLI — manage Vendure e-commerce via GraphQL Admin API
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: httpx>=0.28.0
7
+ Requires-Dist: kctl-lib>=0.7.0
8
+ Requires-Dist: pydantic>=2.10.0
9
+ Requires-Dist: pyyaml>=6.0.2
10
+ Requires-Dist: rich>=13.9.0
11
+ Requires-Dist: typer>=0.15.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: mypy>=1.14.0; extra == 'dev'
14
+ Requires-Dist: pytest-httpx>=0.35.0; extra == 'dev'
15
+ Requires-Dist: pytest>=8.3.0; extra == 'dev'
16
+ Requires-Dist: ruff>=0.9.0; extra == 'dev'
17
+ Requires-Dist: types-pyyaml>=6.0.0; extra == 'dev'
@@ -0,0 +1,120 @@
1
+ # kctl-vendure
2
+
3
+ Kodemeio Vendure CLI — manage e-commerce via GraphQL Admin API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # From workspace (development)
9
+ uv tool install --editable packages/kctl-vendure
10
+
11
+ # From PyPI
12
+ uv tool install kctl-vendure
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```bash
18
+ # Initialize configuration
19
+ kctl-vendure config init
20
+
21
+ # Test API connectivity
22
+ kctl-vendure config test
23
+
24
+ # List products
25
+ kctl-vendure products list
26
+
27
+ # List recent orders
28
+ kctl-vendure orders list --take 20
29
+
30
+ # View customer details
31
+ kctl-vendure customers show cust-123
32
+
33
+ # Check payment status
34
+ kctl-vendure payments list --order 456
35
+ ```
36
+
37
+ ## Command Groups
38
+
39
+ | Group | Commands | Description |
40
+ |-------|----------|-------------|
41
+ | `config` | init, add, use, show, set, remove, profiles, current, test, migrate | Manage profiles and credentials |
42
+ | `products` | list, show, create, update, delete, variants | Product catalog management |
43
+ | `orders` | list, show, update, cancel, refund, fulfill | Order lifecycle management |
44
+ | `customers` | list, show, create, update, delete | Customer account management |
45
+ | `assets` | list, show, upload, delete | Digital asset management |
46
+ | `payments` | list, show, settle, cancel | Payment processing operations |
47
+ | `doctor` | check | Diagnostic checks (GraphQL connectivity, auth, config) |
48
+
49
+ ## Global Options
50
+
51
+ All commands accept these options:
52
+
53
+ | Option | Short | Description |
54
+ |--------|-------|-------------|
55
+ | `--json` | | Output as JSON |
56
+ | `--format` | `-f` | Output format: `pretty`, `json`, `csv`, `yaml` |
57
+ | `--quiet` | `-q` | Suppress info messages |
58
+ | `--no-header` | | Omit header row in table/CSV output |
59
+ | `--profile` | `-p` | Config profile name |
60
+ | `--version` | `-V` | Show version and exit |
61
+
62
+ ## Configuration
63
+
64
+ Config is stored in `~/.config/kodemeio/config.yaml` under the `vendure` key.
65
+
66
+ ```bash
67
+ # Create a new profile
68
+ kctl-vendure config init
69
+
70
+ # Add a named profile
71
+ kctl-vendure config add --profile prod
72
+
73
+ # Switch active profile
74
+ kctl-vendure config use prod
75
+
76
+ # Show current config (secrets masked)
77
+ kctl-vendure config show
78
+
79
+ # Test connectivity
80
+ kctl-vendure config test
81
+ ```
82
+
83
+ ### Config Keys
84
+
85
+ | Key | Description |
86
+ |-----|-------------|
87
+ | `url` | Vendure Admin API URL (e.g. `https://shop.example.com/admin-api`) |
88
+ | `admin_username` | Admin user login (email) |
89
+ | `admin_password` | Admin user password |
90
+
91
+ ### Profile Example
92
+
93
+ ```yaml
94
+ profiles:
95
+ kodemeio:
96
+ vendure:
97
+ url: https://shop.kodeme.io/admin-api
98
+ admin_username: admin@kodeme.io
99
+ admin_password: ${VENDURE_ADMIN_PASSWORD}
100
+ ```
101
+
102
+ ## Common Workflows
103
+
104
+ ```bash
105
+ # Fulfill an order
106
+ kctl-vendure orders fulfill 789 --tracking "TRACK123"
107
+
108
+ # Export orders as CSV
109
+ kctl-vendure orders list --format csv > orders.csv
110
+ ```
111
+
112
+ ## Development
113
+
114
+ ```bash
115
+ cd packages/kctl-vendure
116
+ uv sync --all-extras
117
+ uv run pytest tests/ -v
118
+ uv run mypy src/
119
+ uv run ruff check src/
120
+ ```
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "kctl-vendure"
7
+ version = "0.6.2"
8
+ description = "Kodemeio Vendure CLI — manage Vendure e-commerce via GraphQL Admin API"
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "kctl-lib>=0.7.0",
12
+ "typer>=0.15.0",
13
+ "rich>=13.9.0",
14
+ "pydantic>=2.10.0",
15
+ "pyyaml>=6.0.2",
16
+ "httpx>=0.28.0",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ dev = [
21
+ "pytest>=8.3.0",
22
+ "pytest-httpx>=0.35.0",
23
+ "ruff>=0.9.0",
24
+ "mypy>=1.14.0",
25
+ "types-PyYAML>=6.0.0",
26
+ ]
27
+
28
+ [project.scripts]
29
+ kctl-vendure = "kctl_vendure.cli:_run"
30
+
31
+ [tool.uv.sources]
32
+ kctl-lib = { workspace = true }
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["src/kctl_vendure"]
36
+
37
+ [tool.ruff]
38
+ target-version = "py312"
39
+ line-length = 120
40
+
41
+ [tool.mypy]
42
+ python_version = "3.12"
43
+ strict = true
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: vendure-admin
3
+ description: >
4
+ Vendure e-commerce management via kctl-vendure CLI.
5
+ MUST use for ANY kctl-vendure operation.
6
+ Triggers on: "vendure, e-commerce, ecommerce, products, orders, customers, shop, payments".
7
+ ---
8
+
9
+ # kctl-vendure
10
+
11
+ Kodemeio Vendure CLI - manage your Vendure e-commerce instance (products, orders, customers, assets, payments).
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ uv tool install kctl-vendure
17
+ ```
18
+
19
+ ## Global Options
20
+
21
+ | Option | Short | Description |
22
+ |--------|-------|-------------|
23
+ | `--json` | | JSON output (shortcut for --format json) |
24
+ | `--quiet` | `-q` | Suppress info messages |
25
+ | `--format` | `-f` | Output format: pretty/json/csv/yaml |
26
+ | `--no-header` | | Omit headers in CSV output |
27
+ | `--debug` | | Enable debug logging |
28
+ | `--profile` | `-p` | Config profile name |
29
+ | `--url` | | API URL override |
30
+ | `--username` | `-u` | Admin username override |
31
+ | `--password` | | Admin password override |
32
+ | `--version` | `-V` | Show version |
33
+
34
+ ## Commands
35
+
36
+ ### config
37
+ - `kctl-vendure config init` — Initialize CLI configuration
38
+ - `kctl-vendure config show` — Show configuration (passwords masked)
39
+ - `kctl-vendure config test` — Test API connection
40
+
41
+ ### products
42
+ - `kctl-vendure products list` — List products
43
+ - `kctl-vendure products get` — Get a single product with variants
44
+ - `kctl-vendure products stock` — Update stock level for a product variant
45
+
46
+ ### orders
47
+ - `kctl-vendure orders list` — List orders
48
+ - `kctl-vendure orders get` — Get a single order with line items
49
+ - `kctl-vendure orders fulfill` — Add fulfillment to an order
50
+ - `kctl-vendure orders cancel` — Cancel an order
51
+
52
+ ### customers
53
+ - `kctl-vendure customers list` — List customers
54
+ - `kctl-vendure customers get` — Get a single customer with order history
55
+
56
+ ### assets
57
+ - `kctl-vendure assets list` — List assets
58
+ - `kctl-vendure assets delete` — Delete an asset
59
+
60
+ ### payments
61
+ - `kctl-vendure payments list` — List payments for an order
62
+ - `kctl-vendure payments status` — List available payment methods and their status
63
+
64
+ ### doctor
65
+ - `kctl-vendure doctor` — Run diagnostic checks
66
+
67
+ ## Configuration
68
+
69
+ Service key: `vendure` in `~/.config/kodemeio/config.yaml`
70
+
71
+ ```yaml
72
+ profiles:
73
+ terakidz:
74
+ vendure:
75
+ url: https://shop-api.terakidz.com
76
+ username: admin
77
+ password: <password>
78
+ ```
@@ -0,0 +1,3 @@
1
+ """kctl-vendure: Kodemeio Vendure e-commerce CLI."""
2
+
3
+ __version__ = "0.6.2"
@@ -0,0 +1,5 @@
1
+ """Allow running as: python -m kctl_vendure."""
2
+
3
+ from kctl_vendure.cli import _run
4
+
5
+ _run()
@@ -0,0 +1,101 @@
1
+ """Main CLI entry point for kctl-vendure."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+ from kctl_lib import KctlError, handle_cli_error
9
+
10
+ from kctl_vendure import __version__
11
+ from kctl_vendure.commands.assets import app as assets_app
12
+ from kctl_vendure.commands.config_cmd import app as config_app
13
+ from kctl_vendure.commands.customers import app as customers_app
14
+ from kctl_vendure.commands.doctor import app as doctor_app
15
+ from kctl_vendure.commands.orders import app as orders_app
16
+ from kctl_vendure.commands.payments import app as payments_app
17
+ from kctl_vendure.commands.products import app as products_app
18
+ from kctl_vendure.core.callbacks import AppContext
19
+ from kctl_lib.self_update import notify_if_outdated
20
+ from kctl_lib.tui import add_tui_command
21
+
22
+
23
+ def version_callback(value: bool) -> None:
24
+ if value:
25
+ typer.echo(f"kctl-vendure {__version__}")
26
+ raise typer.Exit()
27
+
28
+
29
+ app = typer.Typer(
30
+ name="kctl-vendure",
31
+ help="Kodemeio Vendure CLI - manage your Vendure e-commerce instance.",
32
+ no_args_is_help=True,
33
+ rich_markup_mode="rich",
34
+ pretty_exceptions_enable=False,
35
+ )
36
+
37
+
38
+ @app.callback()
39
+ def main(
40
+ ctx: typer.Context,
41
+ json_output: Annotated[bool, typer.Option("--json", help="Output as JSON (shortcut for --format json)")] = False,
42
+ quiet: Annotated[bool, typer.Option("--quiet", "-q", help="Suppress info messages")] = False,
43
+ output_format: Annotated[
44
+ str, typer.Option("--format", "-f", help="Output format: pretty, json, csv, yaml")
45
+ ] = "pretty",
46
+ no_header: Annotated[bool, typer.Option("--no-header", help="Omit headers in CSV output")] = False,
47
+ debug: Annotated[bool, typer.Option("--debug", help="Enable debug logging")] = False,
48
+ profile: Annotated[str | None, typer.Option("--profile", "-p", help="Config profile name")] = None,
49
+ url: Annotated[str | None, typer.Option("--url", help="API URL override")] = None,
50
+ username: Annotated[str | None, typer.Option("--username", "-u", help="Admin username override")] = None,
51
+ password: Annotated[str | None, typer.Option("--password", help="Admin password override")] = None,
52
+ version: Annotated[
53
+ bool, typer.Option("--version", "-V", callback=version_callback, is_eager=True, help="Show version")
54
+ ] = False,
55
+ ) -> None:
56
+ """Kodemeio Vendure e-commerce CLI."""
57
+ import os
58
+
59
+ if debug:
60
+ os.environ["KCTL_DEBUG"] = "1"
61
+
62
+ effective_format = "json" if json_output else output_format
63
+
64
+ ctx.ensure_object(dict)
65
+ ctx.obj = AppContext(
66
+ json_mode=json_output or effective_format == "json",
67
+ quiet=quiet,
68
+ format=effective_format,
69
+ no_header=no_header,
70
+ debug=debug,
71
+ profile=profile,
72
+ url_override=url,
73
+ username_override=username,
74
+ password_override=password,
75
+ )
76
+ notify_if_outdated(ctx.obj.output, "kctl-vendure", __version__)
77
+
78
+
79
+ # ---------------------------------------------------------------------------
80
+ # Command groups
81
+ # ---------------------------------------------------------------------------
82
+ app.add_typer(config_app, name="config")
83
+ app.add_typer(products_app, name="products")
84
+ app.add_typer(orders_app, name="orders")
85
+ app.add_typer(customers_app, name="customers")
86
+ app.add_typer(assets_app, name="assets")
87
+ app.add_typer(payments_app, name="payments")
88
+ app.add_typer(doctor_app, name="doctor")
89
+ add_tui_command(app, service_key="vendure", version=__version__)
90
+
91
+
92
+ def _run() -> None:
93
+ """Entry point with error handling."""
94
+ try:
95
+ app()
96
+ except KctlError as e:
97
+ handle_cli_error(e)
98
+
99
+
100
+ if __name__ == "__main__":
101
+ _run()
@@ -0,0 +1,90 @@
1
+ """Asset management commands for Vendure e-commerce."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+ from kctl_vendure.core.callbacks import AppContext
10
+
11
+ app = typer.Typer(help="Manage Vendure assets.")
12
+
13
+ # ---------------------------------------------------------------------------
14
+ # GraphQL Operations
15
+ # ---------------------------------------------------------------------------
16
+
17
+ ASSETS_LIST_QUERY = """
18
+ query GetAssets($options: AssetListOptions) {
19
+ assets(options: $options) {
20
+ items {
21
+ id
22
+ name
23
+ type
24
+ fileSize
25
+ mimeType
26
+ source
27
+ preview
28
+ }
29
+ totalItems
30
+ }
31
+ }
32
+ """
33
+
34
+ DELETE_ASSET_MUTATION = """
35
+ mutation DeleteAsset($input: DeleteAssetInput!) {
36
+ deleteAsset(input: $input) {
37
+ result
38
+ message
39
+ }
40
+ }
41
+ """
42
+
43
+
44
+ @app.command("list")
45
+ def list_cmd(
46
+ ctx: typer.Context,
47
+ limit: Annotated[int, typer.Option("--limit", "-l", help="Number of assets to return")] = 10,
48
+ skip: Annotated[int, typer.Option("--skip", help="Number of assets to skip")] = 0,
49
+ ) -> None:
50
+ """List assets."""
51
+ c: AppContext = ctx.obj
52
+ out = c.output
53
+ variables = {"options": {"take": limit, "skip": skip}}
54
+ data = c.client.query(ASSETS_LIST_QUERY, variables)
55
+ assets_data = data.get("assets", {})
56
+ items = assets_data.get("items", [])
57
+ total = assets_data.get("totalItems", 0)
58
+
59
+ if c.json_mode:
60
+ out.raw_json({"items": items, "totalItems": total})
61
+ else:
62
+ out.info(f"Assets ({total} total)")
63
+ out.table(items, columns=["id", "name", "type", "fileSize", "mimeType"], title="Assets")
64
+
65
+
66
+ @app.command()
67
+ def delete(
68
+ ctx: typer.Context,
69
+ asset_id: Annotated[str, typer.Argument(help="Asset ID")],
70
+ force: Annotated[bool, typer.Option("--force", "-f", help="Skip confirmation and force delete")] = False,
71
+ ) -> None:
72
+ """Delete an asset."""
73
+ c: AppContext = ctx.obj
74
+ out = c.output
75
+
76
+ if not force:
77
+ typer.confirm(f"Delete asset {asset_id}?", abort=True)
78
+
79
+ variables = {"input": {"assetId": asset_id, "force": force}}
80
+ data = c.client.mutate(DELETE_ASSET_MUTATION, variables)
81
+ result = data.get("deleteAsset", {})
82
+
83
+ if c.json_mode:
84
+ out.raw_json(result)
85
+ else:
86
+ if result.get("result") == "DELETED":
87
+ out.success(f"Asset {asset_id} deleted")
88
+ else:
89
+ out.error(f"Delete failed: {result.get('message', 'Unknown error')}")
90
+ raise typer.Exit(1)
@@ -0,0 +1,108 @@
1
+ """Configuration management commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+ from kctl_vendure.core.callbacks import AppContext
10
+ from kctl_vendure.core.config import (
11
+ CONFIG_FILE,
12
+ SERVICE_KEY,
13
+ ServiceConfig,
14
+ get_all_services_in_profile,
15
+ get_default_profile,
16
+ get_profile_names,
17
+ resolve_active_profile_name,
18
+ set_default_profile,
19
+ set_service_config,
20
+ )
21
+
22
+ app = typer.Typer(help="Manage CLI configuration and profiles.")
23
+
24
+
25
+ def _mask(val: str) -> str:
26
+ if not val:
27
+ return "[dim]not set[/dim]"
28
+ return f"{val[:4]}{'*' * max(0, len(val) - 8)}{val[-4:]}" if len(val) > 10 else "****"
29
+
30
+
31
+ @app.command()
32
+ def init(
33
+ ctx: typer.Context,
34
+ url: Annotated[str | None, typer.Option("--url")] = None,
35
+ username: Annotated[str | None, typer.Option("--username", "-u")] = None,
36
+ password: Annotated[str | None, typer.Option("--password")] = None,
37
+ name: Annotated[str | None, typer.Option("--name", "-n")] = None,
38
+ ) -> None:
39
+ """Initialize CLI configuration."""
40
+ c: AppContext = ctx.obj
41
+ out = c.output
42
+ profile_name = name or typer.prompt("Profile name", default="kodemeio")
43
+ api_url = url or typer.prompt("Vendure URL (e.g. https://shop.example.com)")
44
+ admin_user = username or typer.prompt("Admin username", default="superadmin")
45
+ admin_pass = password or typer.prompt("Admin password", hide_input=True)
46
+
47
+ svc = ServiceConfig(url=api_url, admin_username=admin_user, admin_password=admin_pass)
48
+ set_service_config(profile_name, svc)
49
+ if len(get_profile_names()) <= 1:
50
+ set_default_profile(profile_name)
51
+ out.success(f"Configuration saved to {CONFIG_FILE}")
52
+ out.kv("Profile", profile_name)
53
+ out.kv("URL", api_url)
54
+ out.kv("Username", admin_user)
55
+ out.kv("Password", _mask(admin_pass))
56
+
57
+
58
+ @app.command()
59
+ def show(ctx: typer.Context) -> None:
60
+ """Show configuration (passwords masked)."""
61
+ c: AppContext = ctx.obj
62
+ out = c.output
63
+ default = get_default_profile()
64
+ sections = [
65
+ (
66
+ "General",
67
+ [
68
+ ("Config file", str(CONFIG_FILE)),
69
+ ("Default profile", default),
70
+ ("Service key", SERVICE_KEY),
71
+ ],
72
+ )
73
+ ]
74
+ for pname in get_profile_names():
75
+ marker = " [green](default)[/green]" if pname == default else ""
76
+ services = get_all_services_in_profile(pname)
77
+ kvs = []
78
+ for svc_name, svc_data in services.items():
79
+ if not isinstance(svc_data, dict):
80
+ continue
81
+ indicator = "[green]●[/green]" if svc_name == SERVICE_KEY else "[dim]○[/dim]"
82
+ kvs.append(
83
+ (
84
+ f"{indicator} {svc_name}",
85
+ f"{svc_data.get('url', '')} user: {svc_data.get('admin_username', '')} pass: {_mask(svc_data.get('admin_password', ''))}",
86
+ )
87
+ )
88
+ sections.append((f"Profile: {pname}{marker}", kvs or [("(empty)", "")]))
89
+ out.detail("Configuration", sections)
90
+
91
+
92
+ @app.command()
93
+ def test(ctx: typer.Context) -> None:
94
+ """Test API connection."""
95
+ c: AppContext = ctx.obj
96
+ out = c.output
97
+ active = resolve_active_profile_name(c.profile)
98
+ out.info(f"Testing profile '{active}' -> {SERVICE_KEY}")
99
+ try:
100
+ status = c.client.check_health()
101
+ if status == 200:
102
+ out.success("Connected to Vendure API")
103
+ else:
104
+ out.error(f"Connection failed: HTTP {status}")
105
+ raise typer.Exit(1)
106
+ except Exception as e:
107
+ out.error(f"Connection failed: {e}")
108
+ raise typer.Exit(1) from e