kctl-api 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kctl_api/__init__.py +3 -0
- kctl_api/__main__.py +5 -0
- kctl_api/cli.py +238 -0
- kctl_api/commands/__init__.py +1 -0
- kctl_api/commands/ai.py +250 -0
- kctl_api/commands/aliases.py +84 -0
- kctl_api/commands/apps.py +172 -0
- kctl_api/commands/auth.py +313 -0
- kctl_api/commands/automation.py +242 -0
- kctl_api/commands/build_cmd.py +87 -0
- kctl_api/commands/clean.py +182 -0
- kctl_api/commands/config_cmd.py +443 -0
- kctl_api/commands/dashboard.py +139 -0
- kctl_api/commands/db.py +599 -0
- kctl_api/commands/deploy.py +84 -0
- kctl_api/commands/deps.py +289 -0
- kctl_api/commands/dev.py +136 -0
- kctl_api/commands/docker_cmd.py +252 -0
- kctl_api/commands/doctor_cmd.py +286 -0
- kctl_api/commands/env.py +289 -0
- kctl_api/commands/files.py +250 -0
- kctl_api/commands/fmt_cmd.py +58 -0
- kctl_api/commands/health.py +479 -0
- kctl_api/commands/jobs.py +169 -0
- kctl_api/commands/lint_cmd.py +81 -0
- kctl_api/commands/logs.py +258 -0
- kctl_api/commands/marketplace.py +316 -0
- kctl_api/commands/monitor_cmd.py +243 -0
- kctl_api/commands/notifications.py +132 -0
- kctl_api/commands/odoo_proxy.py +182 -0
- kctl_api/commands/openapi.py +299 -0
- kctl_api/commands/perf.py +307 -0
- kctl_api/commands/rate_limit.py +223 -0
- kctl_api/commands/realtime.py +100 -0
- kctl_api/commands/redis_cmd.py +609 -0
- kctl_api/commands/routes_cmd.py +277 -0
- kctl_api/commands/saas.py +145 -0
- kctl_api/commands/scaffold.py +362 -0
- kctl_api/commands/security_cmd.py +350 -0
- kctl_api/commands/services.py +191 -0
- kctl_api/commands/shell.py +197 -0
- kctl_api/commands/skill_cmd.py +58 -0
- kctl_api/commands/streams.py +309 -0
- kctl_api/commands/stripe_cmd.py +105 -0
- kctl_api/commands/tenant_ai.py +169 -0
- kctl_api/commands/test_cmd.py +95 -0
- kctl_api/commands/users.py +302 -0
- kctl_api/commands/webhooks.py +56 -0
- kctl_api/commands/workflows.py +127 -0
- kctl_api/commands/ws.py +323 -0
- kctl_api/core/__init__.py +1 -0
- kctl_api/core/async_client.py +120 -0
- kctl_api/core/callbacks.py +88 -0
- kctl_api/core/client.py +190 -0
- kctl_api/core/config.py +260 -0
- kctl_api/core/db.py +65 -0
- kctl_api/core/exceptions.py +43 -0
- kctl_api/core/output.py +5 -0
- kctl_api/core/plugins.py +26 -0
- kctl_api/core/redis.py +35 -0
- kctl_api/core/resolve.py +47 -0
- kctl_api/core/utils.py +109 -0
- kctl_api-0.2.0.dist-info/METADATA +34 -0
- kctl_api-0.2.0.dist-info/RECORD +66 -0
- kctl_api-0.2.0.dist-info/WHEEL +4 -0
- kctl_api-0.2.0.dist-info/entry_points.txt +2 -0
kctl_api/core/utils.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Shared utility functions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
# Known apps in the kodemeio-fastapi monorepo
|
|
8
|
+
KNOWN_APPS: dict[str, dict[str, str]] = {
|
|
9
|
+
"api-main": {"type": "API", "port": "8000", "module": "kodemeio_api_main"},
|
|
10
|
+
"ai-main": {"type": "AI API", "port": "8010", "module": "kodemeio_ai_main"},
|
|
11
|
+
"odoo-mm": {"type": "Integration", "port": "8003", "module": "kodemeio_odoo_mm"},
|
|
12
|
+
"plane-mm": {"type": "Integration", "port": "8004", "module": "kodemeio_plane_mm"},
|
|
13
|
+
"webhook-github": {"type": "Webhook", "port": "8005", "module": "kodemeio_webhook_github"},
|
|
14
|
+
"webhook-chatwoot": {"type": "Webhook", "port": "8006", "module": "kodemeio_webhook_chatwoot"},
|
|
15
|
+
"events-sync": {"type": "Events", "port": "", "module": "kodemeio_events_sync"},
|
|
16
|
+
"agent-main": {"type": "Agent", "port": "", "module": "kodemeio_agent_main"},
|
|
17
|
+
"etl-main": {"type": "ETL", "port": "", "module": "kodemeio_etl_main"},
|
|
18
|
+
"mcp-main": {"type": "MCP", "port": "", "module": "kodemeio_mcp_main"},
|
|
19
|
+
"scheduler-main": {"type": "Scheduler", "port": "", "module": "kodemeio_scheduler_main"},
|
|
20
|
+
"stream-main": {"type": "Stream", "port": "8002", "module": "kodemeio_stream_main"},
|
|
21
|
+
"ctl-main": {"type": "CLI", "port": "", "module": "kodemeio_cli_main"},
|
|
22
|
+
"tenant-ai-main": {"type": "Tenant AI", "port": "", "module": "kodemeio_tenant_ai_main"},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def service_status_color(status: str) -> str:
|
|
27
|
+
"""Return Rich color markup for service status."""
|
|
28
|
+
colors = {
|
|
29
|
+
"running": "[green]running[/green]",
|
|
30
|
+
"healthy": "[green]healthy[/green]",
|
|
31
|
+
"unhealthy": "[red]unhealthy[/red]",
|
|
32
|
+
"stopped": "[dim]stopped[/dim]",
|
|
33
|
+
"starting": "[yellow]starting[/yellow]",
|
|
34
|
+
"error": "[red]error[/red]",
|
|
35
|
+
"ok": "[green]ok[/green]",
|
|
36
|
+
}
|
|
37
|
+
return colors.get(status.lower(), status)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def role_color(role: str) -> str:
|
|
41
|
+
"""Return Rich color markup for user role."""
|
|
42
|
+
colors = {
|
|
43
|
+
"admin": "[bold red]admin[/bold red]",
|
|
44
|
+
"user": "[cyan]user[/cyan]",
|
|
45
|
+
}
|
|
46
|
+
return colors.get(role.lower(), role)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def tier_color(tier: str) -> str:
|
|
50
|
+
"""Return Rich color markup for user tier."""
|
|
51
|
+
colors = {
|
|
52
|
+
"admin": "[bold red]admin[/bold red]",
|
|
53
|
+
"premium": "[bold yellow]premium[/bold yellow]",
|
|
54
|
+
"user": "[cyan]user[/cyan]",
|
|
55
|
+
"free": "[dim]free[/dim]",
|
|
56
|
+
}
|
|
57
|
+
return colors.get(tier.lower(), tier)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def find_project_root() -> Path:
|
|
61
|
+
"""Find the kodemeio-fastapi project root.
|
|
62
|
+
|
|
63
|
+
Resolution order:
|
|
64
|
+
1. Walk up from CWD looking for markers (pyproject.toml with kodemeio-fastapi, docker-compose.yml + apps/)
|
|
65
|
+
2. Read ``project_root`` from config profile (~/.config/kodemeio/config.yaml)
|
|
66
|
+
3. Fall back to CWD
|
|
67
|
+
"""
|
|
68
|
+
# 1. CWD walk (fast, no config I/O)
|
|
69
|
+
cwd = Path.cwd()
|
|
70
|
+
for parent in [cwd, *cwd.parents]:
|
|
71
|
+
pyproject = parent / "pyproject.toml"
|
|
72
|
+
if pyproject.exists():
|
|
73
|
+
try:
|
|
74
|
+
content = pyproject.read_text()
|
|
75
|
+
if 'name = "kodemeio-fastapi"' in content:
|
|
76
|
+
return parent
|
|
77
|
+
except Exception:
|
|
78
|
+
pass
|
|
79
|
+
if (parent / "docker-compose.yml").exists() and (parent / "apps").is_dir():
|
|
80
|
+
return parent
|
|
81
|
+
|
|
82
|
+
# 2. Config profile fallback
|
|
83
|
+
try:
|
|
84
|
+
from kctl_api.core.config import get_project_root_from_config
|
|
85
|
+
|
|
86
|
+
config_root = get_project_root_from_config()
|
|
87
|
+
if config_root is not None:
|
|
88
|
+
return config_root
|
|
89
|
+
except Exception:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
# 3. CWD fallback
|
|
93
|
+
return cwd
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def human_size(size_bytes: int | float) -> str:
|
|
97
|
+
"""Format byte count as human-readable string."""
|
|
98
|
+
for unit in ("B", "KB", "MB", "GB", "TB"):
|
|
99
|
+
if abs(size_bytes) < 1024:
|
|
100
|
+
return f"{size_bytes:.1f} {unit}"
|
|
101
|
+
size_bytes /= 1024
|
|
102
|
+
return f"{size_bytes:.1f} PB"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def mask_secret(value: str, show_chars: int = 4) -> str:
|
|
106
|
+
"""Mask a secret string, showing only the last N characters."""
|
|
107
|
+
if not value or len(value) <= show_chars:
|
|
108
|
+
return "***"
|
|
109
|
+
return f"***{value[-show_chars:]}"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kctl-api
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Kodemeio API CLI - manage your FastAPI platform
|
|
5
|
+
Author-email: Kodemeio <dev@kodeme.io>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: api,cli,fastapi,kodemeio,rest
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: System Administrators
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: System :: Systems Administration
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Requires-Dist: httpx>=0.28.0
|
|
17
|
+
Requires-Dist: kctl-lib>=0.4.0
|
|
18
|
+
Provides-Extra: all
|
|
19
|
+
Requires-Dist: asyncpg>=0.30.0; extra == 'all'
|
|
20
|
+
Requires-Dist: redis>=5.0.0; extra == 'all'
|
|
21
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.0; extra == 'all'
|
|
22
|
+
Provides-Extra: db
|
|
23
|
+
Requires-Dist: asyncpg>=0.30.0; extra == 'db'
|
|
24
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.0; extra == 'db'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: mypy>=1.14.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-httpx>=0.35.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: types-pyyaml>=6.0.0; extra == 'dev'
|
|
33
|
+
Provides-Extra: redis
|
|
34
|
+
Requires-Dist: redis>=5.0.0; extra == 'redis'
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
kctl_api/__init__.py,sha256=Vlkbhn3ncthEoN2V5MLQn_eeMajEIW9Hh0LbHX8JJIE,57
|
|
2
|
+
kctl_api/__main__.py,sha256=zxgO82u5ch50ue_nnR-X7JfCFzpM0hr88sq5FFAp4bo,82
|
|
3
|
+
kctl_api/cli.py,sha256=InWwpo1KXciTHQW3Z6YNdknEHUGTsPCcWawsJit14FE,9113
|
|
4
|
+
kctl_api/commands/__init__.py,sha256=fNB66xHJTlbQl3f7UQralX19ZSDz6P1mWY9gcnGnw-o,35
|
|
5
|
+
kctl_api/commands/ai.py,sha256=dqkT-QmfJEHEnmzyA0CuiCPruowBjtQ3nAuNaK3OTT4,8100
|
|
6
|
+
kctl_api/commands/aliases.py,sha256=wjK_Pk03BDPJNj6_U3-9fRCF3I1RI0hDvZx2plPyuMc,3026
|
|
7
|
+
kctl_api/commands/apps.py,sha256=YLgD6j_pN5n78q-yjuPBqn8iQNhBYYfq9NGJvjI_3w8,5154
|
|
8
|
+
kctl_api/commands/auth.py,sha256=nmWvqQarf1lQcyhpzG2rl3R44hc1gKyKR6Cy1FVZ24c,10278
|
|
9
|
+
kctl_api/commands/automation.py,sha256=U6t21LmAPl8pwvGE4H-I_xCz47lIoNmRv-hn2ZyHt7c,7845
|
|
10
|
+
kctl_api/commands/build_cmd.py,sha256=Om2wllEfl-TDdI3voleuMmeHyMwvcjB_Flrgh4qUcZg,2805
|
|
11
|
+
kctl_api/commands/clean.py,sha256=lObvQMwRa7Urr2cg1496lqekR4UodWjVtT3ABmnDQt4,6568
|
|
12
|
+
kctl_api/commands/config_cmd.py,sha256=5-duFy9mOKCFJCgWzYno73YiAGG0AkiUtrHg8glWXkI,14510
|
|
13
|
+
kctl_api/commands/dashboard.py,sha256=7E1_whoQcYHRC5pGKt_eN5tLZ4e5W8YfPK8LmvXnyEU,4808
|
|
14
|
+
kctl_api/commands/db.py,sha256=QI-V9lo9S0qOGqFifX7jE45RDvlCMNT7jFfIOx3VBj4,20069
|
|
15
|
+
kctl_api/commands/deploy.py,sha256=VYeu20I4PJdqBHK75JVWO3QyAn1hQdqSp-id7oYQeRI,3070
|
|
16
|
+
kctl_api/commands/deps.py,sha256=iqQVTfmqxwcbfSPiJGf8y-PkiAASAQEiShAfJocW6bk,9991
|
|
17
|
+
kctl_api/commands/dev.py,sha256=G6RYlaFDMJBHeGrNJmmjAsAeu-mrHT32AzJvPy-ljlI,4803
|
|
18
|
+
kctl_api/commands/docker_cmd.py,sha256=G1JQpy3zTLuhUGLunDNy6Z96njqUlLa6oQuSnpMV3SI,7524
|
|
19
|
+
kctl_api/commands/doctor_cmd.py,sha256=wrtqVDrbB0NekGQth7Cj1Ip72HluU3wkM6-Jp8e3OsM,9170
|
|
20
|
+
kctl_api/commands/env.py,sha256=ZhjLlfEZQQ058B7SbmXXK8lrCfd2W03B89BqkLm1qs4,10092
|
|
21
|
+
kctl_api/commands/files.py,sha256=wEjvUuwmdhmx_jdj94aXRC5Buf3GqNTwj9-aqaIF6tU,8348
|
|
22
|
+
kctl_api/commands/fmt_cmd.py,sha256=3R1qHRSe9ubwsfupFkWko1PHBTX5XpD02TVE1qpE9RU,1732
|
|
23
|
+
kctl_api/commands/health.py,sha256=KQpoKVqjKEfLAtGXS00K6bpofCyZAOlBsx0foqjcOUg,16159
|
|
24
|
+
kctl_api/commands/jobs.py,sha256=liM0FfkQdaMxPjSPMZbgOfSQNAheQwNxzPkgfvFM6gE,5286
|
|
25
|
+
kctl_api/commands/lint_cmd.py,sha256=5I1P0JdKSjgtIhH-K8pj3o_vO2nkY7EbnQ8sYcA7sBI,2375
|
|
26
|
+
kctl_api/commands/logs.py,sha256=aYuThlOHIaFJuWsJRPEfnSIZKLi_pISVl9lhaDvlyr8,9146
|
|
27
|
+
kctl_api/commands/marketplace.py,sha256=4xIGZ9QjMHVklpc4FHxcY41trvkRCInRiwGAb_RaOrM,10153
|
|
28
|
+
kctl_api/commands/monitor_cmd.py,sha256=27xCojDxGjFIuhIcXgD3nBWE8XZbeUHQPQMQny9lIjc,8797
|
|
29
|
+
kctl_api/commands/notifications.py,sha256=LFLd8d9rY_MytfQI_a7UoDIBX3yPqEcz88QxmFPRor4,4466
|
|
30
|
+
kctl_api/commands/odoo_proxy.py,sha256=swMvBUMHubOfvdVWuB9Z5gpJ1ThM0IEoIQt5IX4oBVE,6104
|
|
31
|
+
kctl_api/commands/openapi.py,sha256=lYiedbo3oC8PoNjNeygCCuYLitwphWnG_u6e_7jH_Z0,10552
|
|
32
|
+
kctl_api/commands/perf.py,sha256=m8cdZfWFTyKaeSEIYD6odN0H6tLrencePkGYBTF5Gg4,10919
|
|
33
|
+
kctl_api/commands/rate_limit.py,sha256=r5YE1TTUwypTSWZacjPT0LtU3cS7yMpVNCud3-sm74g,7570
|
|
34
|
+
kctl_api/commands/realtime.py,sha256=D6c46aSnsLp1S0r_Y8ATIMJ2qQbesTvmzuzNEDOZMqo,3248
|
|
35
|
+
kctl_api/commands/redis_cmd.py,sha256=i4ISRaXYKs0Z-nMVJo4C2MqtT9SuTyG4Xf2JXOQAQ4s,19194
|
|
36
|
+
kctl_api/commands/routes_cmd.py,sha256=5TaDVF5OV--JoookRjPynaERPNuUpHMCoiCHzsswXfU,9247
|
|
37
|
+
kctl_api/commands/saas.py,sha256=Mz3S_b1gydiDq-aKSXNmOCezfeU-be19Vm4bcKcnCGI,4710
|
|
38
|
+
kctl_api/commands/scaffold.py,sha256=zg1l-niKLOZfM_tBDcTgQepaN3BCv2kmMzsm-gCMuI0,12720
|
|
39
|
+
kctl_api/commands/security_cmd.py,sha256=4j29fMbDMO8LZ89qphYqWTGnV0GwQOkSc9gNcjQhw4U,12022
|
|
40
|
+
kctl_api/commands/services.py,sha256=66fq1imKJa5acRF96sWoOohfNCpIdCcsLSck61Ff9gE,6216
|
|
41
|
+
kctl_api/commands/shell.py,sha256=95jqn1u4WSp36zMgaNu5mXptkNnKDAmel8Lx9_ahA38,5336
|
|
42
|
+
kctl_api/commands/skill_cmd.py,sha256=8SxwdX3e8flNOqYheu4tY6HZcJ592VBXxuPFk8STIgw,1772
|
|
43
|
+
kctl_api/commands/streams.py,sha256=2i-JWV--JVWK8KCCH1au-wjuD6ztLkD12aOqBNgZIQ4,10071
|
|
44
|
+
kctl_api/commands/stripe_cmd.py,sha256=segH1LsyPZp2oE7vcKHBmIOnQrCtCTH8uHZFkrTOtks,3497
|
|
45
|
+
kctl_api/commands/tenant_ai.py,sha256=lJ1Ucn0AfBqcobSvIMNCOWTThE6FTJNhP_ZgcL0szSM,5473
|
|
46
|
+
kctl_api/commands/test_cmd.py,sha256=C0CKOwUuYG-xZNEo9sDYq0lNY7t7dLo35YjfSmgnfBU,3256
|
|
47
|
+
kctl_api/commands/users.py,sha256=R0X7prMNgngzljYEQ55I88mhF9kzZ1TSz1U7hMDTvdQ,10039
|
|
48
|
+
kctl_api/commands/webhooks.py,sha256=1ae0TzT50es6cvXqf6dQp26g2jogcsOL5Rd9aotjoQg,2245
|
|
49
|
+
kctl_api/commands/workflows.py,sha256=NgJwF5RzqyL4hmGCUoy7a_cUWyvURlLD_sSAMhy3UOM,4059
|
|
50
|
+
kctl_api/commands/ws.py,sha256=Nf4wq_TuT7jEsF3u5mh7JQykh3rXnGhyh_dSXRpHwtk,11858
|
|
51
|
+
kctl_api/core/__init__.py,sha256=1E1CQ_u3wHjRaO3ZJ531KP1r_ZaCjz1nvvcg_0t5Vbw,40
|
|
52
|
+
kctl_api/core/async_client.py,sha256=cQXfu7CnJFeu1b7of0cc0afBYnH9aC7YcPvc0wGuYgs,4097
|
|
53
|
+
kctl_api/core/callbacks.py,sha256=te9QQ3qObkKZbdToH5r3OSK5RPkN-E7pt7xaoTFtRN0,2978
|
|
54
|
+
kctl_api/core/client.py,sha256=Q2yYCoKwmLwUSck0DBG1yxeYF8VPuzyh2jxvhQeh3V8,6845
|
|
55
|
+
kctl_api/core/config.py,sha256=Izk2nH85K311QYN01X1FA6x7hutnMUIMSNGKY0zbKGw,7745
|
|
56
|
+
kctl_api/core/db.py,sha256=-dm1oVGA4Evg7nWTb9U-g90Pq-gMQ4CjdczQD3Rv_RA,1843
|
|
57
|
+
kctl_api/core/exceptions.py,sha256=FRU2C2u2BuWVeqXUNfajsQ9bZu_7x-psDTOdQ_e5NEg,1155
|
|
58
|
+
kctl_api/core/output.py,sha256=irTgk_u3pGmOEViFXq1Tr10SFB0qFqiAc9hwpig7Dw8,110
|
|
59
|
+
kctl_api/core/plugins.py,sha256=8yOowxgD7ykeraDSyWw3zMqCgNSKYRuzV5bW9366tdg,847
|
|
60
|
+
kctl_api/core/redis.py,sha256=GC5ZP8SuSZ1GD76V2W-DHHStfPKKHdMxkZOdb1kL-qs,932
|
|
61
|
+
kctl_api/core/resolve.py,sha256=_7FdRQbFOJyFWnWwGCw-7Ox3638G0eNzr3PnPWlZ3jM,1297
|
|
62
|
+
kctl_api/core/utils.py,sha256=o7bZLOA_33pUtsdp3kFu_lXqEiySNAH3qS3xAH2y1ZY,4072
|
|
63
|
+
kctl_api-0.2.0.dist-info/METADATA,sha256=WwDI6Wood7eexcgg6lLZNdqIVl7dNnxqod4rd0YAupw,1354
|
|
64
|
+
kctl_api-0.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
65
|
+
kctl_api-0.2.0.dist-info/entry_points.txt,sha256=rkHu58uDHn-s_Fjj4_JyGzjlkAqdhOe-ptvjXCZwDRw,47
|
|
66
|
+
kctl_api-0.2.0.dist-info/RECORD,,
|