apigw-cli 0.1.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.
- apigw_cli-0.1.0/PKG-INFO +49 -0
- apigw_cli-0.1.0/README.md +37 -0
- apigw_cli-0.1.0/apigw_cli/__init__.py +3 -0
- apigw_cli-0.1.0/apigw_cli/client.py +65 -0
- apigw_cli-0.1.0/apigw_cli/commands/__init__.py +0 -0
- apigw_cli-0.1.0/apigw_cli/commands/build.py +87 -0
- apigw_cli-0.1.0/apigw_cli/commands/chat.py +145 -0
- apigw_cli-0.1.0/apigw_cli/commands/config_cmd.py +41 -0
- apigw_cli-0.1.0/apigw_cli/commands/connect.py +66 -0
- apigw_cli-0.1.0/apigw_cli/commands/execute.py +65 -0
- apigw_cli-0.1.0/apigw_cli/commands/register.py +66 -0
- apigw_cli-0.1.0/apigw_cli/commands/tools.py +125 -0
- apigw_cli-0.1.0/apigw_cli/config.py +36 -0
- apigw_cli-0.1.0/apigw_cli/main.py +20 -0
- apigw_cli-0.1.0/apigw_cli/output.py +71 -0
- apigw_cli-0.1.0/apigw_cli.egg-info/PKG-INFO +49 -0
- apigw_cli-0.1.0/apigw_cli.egg-info/SOURCES.txt +21 -0
- apigw_cli-0.1.0/apigw_cli.egg-info/dependency_links.txt +1 -0
- apigw_cli-0.1.0/apigw_cli.egg-info/entry_points.txt +2 -0
- apigw_cli-0.1.0/apigw_cli.egg-info/requires.txt +5 -0
- apigw_cli-0.1.0/apigw_cli.egg-info/top_level.txt +1 -0
- apigw_cli-0.1.0/pyproject.toml +20 -0
- apigw_cli-0.1.0/setup.cfg +4 -0
apigw_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apigw-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI для API Gateway
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: typer[all]>=0.12
|
|
8
|
+
Requires-Dist: httpx>=0.27
|
|
9
|
+
Requires-Dist: rich>=13
|
|
10
|
+
Requires-Dist: anthropic>=0.25
|
|
11
|
+
Requires-Dist: questionary>=2
|
|
12
|
+
|
|
13
|
+
# apigw-cli
|
|
14
|
+
|
|
15
|
+
CLI для API Gateway — управляйте интеграциями из терминала.
|
|
16
|
+
|
|
17
|
+
## Установка
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install apigw-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Быстрый старт
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
apigw register
|
|
27
|
+
apigw config set user-id my_user
|
|
28
|
+
apigw tools list
|
|
29
|
+
apigw tools search "отзывы wildberries"
|
|
30
|
+
apigw connect wildberries --api-key MY_TOKEN
|
|
31
|
+
apigw execute wildberries__get_orders \
|
|
32
|
+
--input '{"date_from":"2026-03-01","date_to":"2026-03-23"}'
|
|
33
|
+
apigw chat
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Все команды
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
apigw --help
|
|
40
|
+
apigw register # регистрация
|
|
41
|
+
apigw config set # настройка
|
|
42
|
+
apigw config show # просмотр конфига
|
|
43
|
+
apigw tools list # список инструментов
|
|
44
|
+
apigw tools search # поиск инструментов
|
|
45
|
+
apigw execute # выполнить инструмент
|
|
46
|
+
apigw connect # подключить сервис
|
|
47
|
+
apigw build # создать новый инструмент
|
|
48
|
+
apigw chat # интерактивный режим
|
|
49
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# apigw-cli
|
|
2
|
+
|
|
3
|
+
CLI для API Gateway — управляйте интеграциями из терминала.
|
|
4
|
+
|
|
5
|
+
## Установка
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install apigw-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Быстрый старт
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
apigw register
|
|
15
|
+
apigw config set user-id my_user
|
|
16
|
+
apigw tools list
|
|
17
|
+
apigw tools search "отзывы wildberries"
|
|
18
|
+
apigw connect wildberries --api-key MY_TOKEN
|
|
19
|
+
apigw execute wildberries__get_orders \
|
|
20
|
+
--input '{"date_from":"2026-03-01","date_to":"2026-03-23"}'
|
|
21
|
+
apigw chat
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Все команды
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
apigw --help
|
|
28
|
+
apigw register # регистрация
|
|
29
|
+
apigw config set # настройка
|
|
30
|
+
apigw config show # просмотр конфига
|
|
31
|
+
apigw tools list # список инструментов
|
|
32
|
+
apigw tools search # поиск инструментов
|
|
33
|
+
apigw execute # выполнить инструмент
|
|
34
|
+
apigw connect # подключить сервис
|
|
35
|
+
apigw build # создать новый инструмент
|
|
36
|
+
apigw chat # интерактивный режим
|
|
37
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
|
|
3
|
+
from apigw_cli.config import get_api_key, get_base_url, load_config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class APIGWClient:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.base_url = get_base_url().rstrip("/")
|
|
9
|
+
self.api_key = get_api_key()
|
|
10
|
+
self.user_id = load_config().get("user_id")
|
|
11
|
+
|
|
12
|
+
def _headers(self) -> dict[str, str]:
|
|
13
|
+
return {
|
|
14
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def get(self, path: str, params: dict | None = None,
|
|
19
|
+
extra_headers: dict | None = None) -> dict:
|
|
20
|
+
headers = self._headers()
|
|
21
|
+
if extra_headers:
|
|
22
|
+
headers.update(extra_headers)
|
|
23
|
+
r = httpx.get(
|
|
24
|
+
f"{self.base_url}{path}",
|
|
25
|
+
headers=headers,
|
|
26
|
+
params=params,
|
|
27
|
+
timeout=30,
|
|
28
|
+
)
|
|
29
|
+
return self._handle(r)
|
|
30
|
+
|
|
31
|
+
def post(self, path: str, json: dict | None = None,
|
|
32
|
+
*, allow_errors: bool = False) -> dict:
|
|
33
|
+
r = httpx.post(
|
|
34
|
+
f"{self.base_url}{path}",
|
|
35
|
+
headers=self._headers(),
|
|
36
|
+
json=json,
|
|
37
|
+
timeout=60,
|
|
38
|
+
)
|
|
39
|
+
return self._handle(r, allow_errors=allow_errors)
|
|
40
|
+
|
|
41
|
+
def delete(self, path: str) -> dict:
|
|
42
|
+
r = httpx.delete(
|
|
43
|
+
f"{self.base_url}{path}",
|
|
44
|
+
headers=self._headers(),
|
|
45
|
+
timeout=30,
|
|
46
|
+
)
|
|
47
|
+
return self._handle(r)
|
|
48
|
+
|
|
49
|
+
def _handle(self, r: httpx.Response, *, allow_errors: bool = False) -> dict:
|
|
50
|
+
if r.status_code == 401:
|
|
51
|
+
raise SystemExit("❌ Неверный API ключ. Запустите: apigw register")
|
|
52
|
+
if r.status_code == 429:
|
|
53
|
+
raise SystemExit("❌ Превышен лимит запросов. Подождите минуту.")
|
|
54
|
+
if allow_errors and r.status_code in (403, 503):
|
|
55
|
+
try:
|
|
56
|
+
return r.json()
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
if r.status_code >= 400:
|
|
60
|
+
try:
|
|
61
|
+
err = r.json().get("message", r.text)
|
|
62
|
+
except Exception:
|
|
63
|
+
err = r.text
|
|
64
|
+
raise SystemExit(f"❌ Ошибка: {err}")
|
|
65
|
+
return r.json()
|
|
File without changes
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
6
|
+
|
|
7
|
+
from apigw_cli.client import APIGWClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build(
|
|
11
|
+
service: str = typer.Argument(..., help="slug сервиса"),
|
|
12
|
+
tool: str = typer.Argument(..., help="название инструмента"),
|
|
13
|
+
openapi_url: str = typer.Option(None, "--openapi-url"),
|
|
14
|
+
wait: bool = typer.Option(True, "--wait/--no-wait",
|
|
15
|
+
help="ждать завершения"),
|
|
16
|
+
):
|
|
17
|
+
"""Создать новый инструмент через LLM.
|
|
18
|
+
|
|
19
|
+
Примеры:
|
|
20
|
+
apigw build wildberries reply_to_review
|
|
21
|
+
apigw build my-crm get-leads \\
|
|
22
|
+
--openapi-url https://api.mycrm.com/openapi.json
|
|
23
|
+
"""
|
|
24
|
+
console = Console()
|
|
25
|
+
client = APIGWClient()
|
|
26
|
+
|
|
27
|
+
payload = {
|
|
28
|
+
"service_slug": service.replace("-", "_"),
|
|
29
|
+
"tool_name": tool.replace("-", "_"),
|
|
30
|
+
}
|
|
31
|
+
if openapi_url:
|
|
32
|
+
payload["openapi_url"] = openapi_url
|
|
33
|
+
|
|
34
|
+
data = client.post("/v1/tools/build", json=payload)
|
|
35
|
+
build_id = data.get("build_id")
|
|
36
|
+
|
|
37
|
+
if data.get("status") == "exists":
|
|
38
|
+
console.print(f"[green]✅ Инструмент уже существует: "
|
|
39
|
+
f"{service}__{tool}[/green]")
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
console.print(f"[cyan]🔨 Сборка запущена[/cyan] "
|
|
43
|
+
f"[dim](id: {build_id[:8] if build_id else '-'}...)[/dim]")
|
|
44
|
+
console.print("[dim]Gateway ищет документацию и генерирует код...[/dim]\n")
|
|
45
|
+
|
|
46
|
+
if not wait:
|
|
47
|
+
if build_id:
|
|
48
|
+
console.print(f"Проверить: [cyan]apigw build status {build_id}[/cyan]")
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
STATUS_LABELS = {
|
|
52
|
+
"queued": "В очереди...",
|
|
53
|
+
"building": "Ищу документацию...",
|
|
54
|
+
"testing": "Тестирую в sandbox...",
|
|
55
|
+
"done": "Готово!",
|
|
56
|
+
"failed": "Ошибка",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
with Progress(SpinnerColumn(), TextColumn("{task.description}"),
|
|
60
|
+
console=console) as progress:
|
|
61
|
+
task = progress.add_task("Запуск...", total=None)
|
|
62
|
+
for _ in range(60):
|
|
63
|
+
time.sleep(10)
|
|
64
|
+
s = client.get(f"/v1/tools/build/{build_id}")
|
|
65
|
+
current = s.get("status", "queued")
|
|
66
|
+
progress.update(task,
|
|
67
|
+
description=STATUS_LABELS.get(current, current))
|
|
68
|
+
|
|
69
|
+
if current == "done":
|
|
70
|
+
progress.stop()
|
|
71
|
+
console.print(
|
|
72
|
+
f"\n[green]✅ Готов: {service}__{tool}[/green]\n"
|
|
73
|
+
f"Использовать: [cyan]apigw execute "
|
|
74
|
+
f"{service}__{tool}[/cyan]"
|
|
75
|
+
)
|
|
76
|
+
return
|
|
77
|
+
if current == "failed":
|
|
78
|
+
progress.stop()
|
|
79
|
+
error = s.get("error_msg", "неизвестная ошибка")
|
|
80
|
+
console.print(f"\n[red]❌ Не удалось: {error}[/red]")
|
|
81
|
+
console.print(
|
|
82
|
+
"[dim]Попробуйте передать --openapi-url[/dim]"
|
|
83
|
+
)
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
console.print(f"[yellow]⏱️ Таймаут. Проверьте: "
|
|
87
|
+
f"apigw build status {build_id}[/yellow]")
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.panel import Panel
|
|
6
|
+
|
|
7
|
+
from apigw_cli.client import APIGWClient
|
|
8
|
+
from apigw_cli.config import load_config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def chat(
|
|
12
|
+
user: str = typer.Option(None, "--user", "-u"),
|
|
13
|
+
):
|
|
14
|
+
"""Интерактивный режим — пишите на русском.
|
|
15
|
+
|
|
16
|
+
CLI сам выбирает нужный инструмент и выполняет его.
|
|
17
|
+
Введите 'выход' для завершения.
|
|
18
|
+
"""
|
|
19
|
+
import datetime
|
|
20
|
+
|
|
21
|
+
import anthropic
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
user_id = user or load_config().get("user_id")
|
|
25
|
+
|
|
26
|
+
if not user_id:
|
|
27
|
+
console.print(
|
|
28
|
+
"[red]❌ Укажите user_id: "
|
|
29
|
+
"apigw config set user-id <id>[/red]"
|
|
30
|
+
)
|
|
31
|
+
raise typer.Exit(1)
|
|
32
|
+
|
|
33
|
+
http = APIGWClient()
|
|
34
|
+
|
|
35
|
+
# Получить список инструментов для системного промпта
|
|
36
|
+
tools_data = http.get("/v1/tools", params={"limit": 100})
|
|
37
|
+
available = tools_data.get("tools", [])
|
|
38
|
+
tools_desc = "\n".join(
|
|
39
|
+
f"- {t['name']}: {t.get('description','')}"
|
|
40
|
+
for t in available
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
system = f"""Ты — ассистент для работы с API Gateway.
|
|
44
|
+
Доступные инструменты:
|
|
45
|
+
{tools_desc}
|
|
46
|
+
|
|
47
|
+
Когда пользователь просит что-то сделать — верни ТОЛЬКО JSON:
|
|
48
|
+
{{"tool": "tool_name", "input": {{"param": "value"}}}}
|
|
49
|
+
|
|
50
|
+
Если инструмента нет:
|
|
51
|
+
{{"tool": null, "message": "объяснение"}}
|
|
52
|
+
|
|
53
|
+
user_id: {user_id}
|
|
54
|
+
Сегодня: {datetime.date.today()}
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
console.print(Panel(
|
|
58
|
+
"[bold cyan]API Gateway Chat[/bold cyan]\n"
|
|
59
|
+
"[dim]Пишите на русском что нужно сделать.\n"
|
|
60
|
+
"Введите 'выход' для завершения.[/dim]",
|
|
61
|
+
border_style="cyan",
|
|
62
|
+
))
|
|
63
|
+
|
|
64
|
+
ai = anthropic.Anthropic()
|
|
65
|
+
history = []
|
|
66
|
+
|
|
67
|
+
while True:
|
|
68
|
+
try:
|
|
69
|
+
user_input = input("\n> ").strip()
|
|
70
|
+
except (KeyboardInterrupt, EOFError):
|
|
71
|
+
console.print("\n[dim]До свидания![/dim]")
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
if user_input.lower() in ("выход", "exit", "quit", "q"):
|
|
75
|
+
console.print("[dim]До свидания![/dim]")
|
|
76
|
+
break
|
|
77
|
+
if not user_input:
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
history.append({"role": "user", "content": user_input})
|
|
81
|
+
|
|
82
|
+
with console.status("[dim]Думаю...[/dim]"):
|
|
83
|
+
resp = ai.messages.create(
|
|
84
|
+
model="claude-haiku-4-5-20251001",
|
|
85
|
+
max_tokens=500,
|
|
86
|
+
system=system,
|
|
87
|
+
messages=history,
|
|
88
|
+
)
|
|
89
|
+
ai_text = resp.content[0].text.strip()
|
|
90
|
+
history.append({"role": "assistant", "content": ai_text})
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
clean = (ai_text.replace("```json", "")
|
|
94
|
+
.replace("```", "").strip())
|
|
95
|
+
action = json.loads(clean)
|
|
96
|
+
except json.JSONDecodeError:
|
|
97
|
+
console.print(f"[dim]{ai_text}[/dim]")
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
if not action.get("tool"):
|
|
101
|
+
console.print(
|
|
102
|
+
f"[yellow]{action.get('message', ai_text)}[/yellow]"
|
|
103
|
+
)
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
tool_name = action["tool"]
|
|
107
|
+
tool_input = action.get("input", {})
|
|
108
|
+
console.print(f"[dim]→ {tool_name}[/dim]")
|
|
109
|
+
|
|
110
|
+
result = http.post("/v1/tools/execute", json={
|
|
111
|
+
"tool_name": tool_name,
|
|
112
|
+
"user_id": user_id,
|
|
113
|
+
"input": tool_input,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
if result.get("success"):
|
|
117
|
+
data = result.get("data", {})
|
|
118
|
+
summary_resp = ai.messages.create(
|
|
119
|
+
model="claude-haiku-4-5-20251001",
|
|
120
|
+
max_tokens=200,
|
|
121
|
+
messages=[{"role": "user", "content":
|
|
122
|
+
f"Пользователь просил: {user_input}\n"
|
|
123
|
+
f"Результат: "
|
|
124
|
+
f"{json.dumps(data, ensure_ascii=False)[:1000]}\n"
|
|
125
|
+
f"Кратко опиши на русском (1-2 предложения)."}],
|
|
126
|
+
)
|
|
127
|
+
console.print(
|
|
128
|
+
f"\n[green]{summary_resp.content[0].text}[/green]"
|
|
129
|
+
)
|
|
130
|
+
history.append({"role": "user",
|
|
131
|
+
"content": f"[Результат {tool_name}]: "
|
|
132
|
+
f"{json.dumps(data, ensure_ascii=False)[:300]}"
|
|
133
|
+
})
|
|
134
|
+
else:
|
|
135
|
+
error = result.get("error", "unknown")
|
|
136
|
+
if error == "reauth_required":
|
|
137
|
+
service = tool_name.split("__")[0]
|
|
138
|
+
console.print(
|
|
139
|
+
f"[yellow]⚠️ Подключите {service}: "
|
|
140
|
+
f"apigw connect {service}[/yellow]"
|
|
141
|
+
)
|
|
142
|
+
else:
|
|
143
|
+
console.print(
|
|
144
|
+
f"[red]❌ {result.get('message', error)}[/red]"
|
|
145
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import json
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from apigw_cli.config import load_config, save_config
|
|
5
|
+
|
|
6
|
+
app = typer.Typer(help="Управление конфигурацией")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@app.command("set")
|
|
10
|
+
def config_set(key: str, value: str):
|
|
11
|
+
"""Установить параметр.
|
|
12
|
+
|
|
13
|
+
Примеры:
|
|
14
|
+
apigw config set user-id shop_123
|
|
15
|
+
apigw config set base-url http://localhost:8080
|
|
16
|
+
"""
|
|
17
|
+
key_map = {"user-id": "user_id", "base-url": "base_url",
|
|
18
|
+
"api-key": "api_key"}
|
|
19
|
+
if key not in key_map:
|
|
20
|
+
typer.echo(f"Неизвестный параметр: {key}")
|
|
21
|
+
typer.echo(f"Доступные: {', '.join(key_map.keys())}")
|
|
22
|
+
raise typer.Exit(1)
|
|
23
|
+
cfg = load_config()
|
|
24
|
+
cfg[key_map[key]] = value
|
|
25
|
+
save_config(**{k: cfg.get(k) for k in
|
|
26
|
+
["api_key", "base_url", "user_id"]})
|
|
27
|
+
typer.echo(f" {key} = {value}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.command("show")
|
|
31
|
+
def config_show():
|
|
32
|
+
"""Показать текущий конфиг."""
|
|
33
|
+
cfg = load_config()
|
|
34
|
+
if not cfg:
|
|
35
|
+
typer.echo("Не авторизован. Запустите: apigw register")
|
|
36
|
+
return
|
|
37
|
+
display = cfg.copy()
|
|
38
|
+
if display.get("api_key"):
|
|
39
|
+
k = display["api_key"]
|
|
40
|
+
display["api_key"] = k[:16] + "..." + k[-4:]
|
|
41
|
+
Console().print_json(json.dumps(display))
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import webbrowser
|
|
2
|
+
import typer
|
|
3
|
+
import questionary
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from apigw_cli.client import APIGWClient
|
|
6
|
+
from apigw_cli.config import load_config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def connect(
|
|
10
|
+
service: str = typer.Argument(..., help="название сервиса"),
|
|
11
|
+
user: str = typer.Option(None, "--user", "-u"),
|
|
12
|
+
api_key_value: str = typer.Option(None, "--api-key",
|
|
13
|
+
help="API ключ сервиса"),
|
|
14
|
+
):
|
|
15
|
+
"""Подключить сервис для пользователя.
|
|
16
|
+
|
|
17
|
+
API ключ (WB, Telegram, Ozon, Kaspi):
|
|
18
|
+
apigw connect wildberries --api-key TOKEN
|
|
19
|
+
|
|
20
|
+
OAuth (Facebook, Google, Яндекс, Slack):
|
|
21
|
+
apigw connect facebook-ads
|
|
22
|
+
"""
|
|
23
|
+
console = Console()
|
|
24
|
+
client = APIGWClient()
|
|
25
|
+
user_id = user or load_config().get("user_id")
|
|
26
|
+
|
|
27
|
+
if not user_id:
|
|
28
|
+
console.print("[red]❌ Укажите user_id: --user <id>[/red]")
|
|
29
|
+
raise typer.Exit(1)
|
|
30
|
+
|
|
31
|
+
slug = service.replace("-", "_")
|
|
32
|
+
|
|
33
|
+
if api_key_value:
|
|
34
|
+
client.post(
|
|
35
|
+
f"/auth/connect/{slug}?user_id={user_id}",
|
|
36
|
+
json={"api_key": api_key_value},
|
|
37
|
+
)
|
|
38
|
+
console.print(
|
|
39
|
+
f"[green]✅ {service} подключён "
|
|
40
|
+
f"для пользователя {user_id}[/green]"
|
|
41
|
+
)
|
|
42
|
+
else:
|
|
43
|
+
data = client.get(
|
|
44
|
+
f"/auth/connect/{slug}",
|
|
45
|
+
params={"user_id": user_id,
|
|
46
|
+
"redirect_back": f"{client.base_url}/done"},
|
|
47
|
+
)
|
|
48
|
+
auth_url = data.get("auth_url")
|
|
49
|
+
if not auth_url:
|
|
50
|
+
console.print("[red]❌ Не удалось получить URL[/red]")
|
|
51
|
+
raise typer.Exit(1)
|
|
52
|
+
|
|
53
|
+
console.print(f"\n[cyan]Открываю браузер для {service}...[/cyan]")
|
|
54
|
+
console.print(f"[dim]Если не открылся:[/dim] {auth_url}\n")
|
|
55
|
+
webbrowser.open(auth_url)
|
|
56
|
+
|
|
57
|
+
questionary.text(
|
|
58
|
+
"Нажмите Enter после авторизации в браузере..."
|
|
59
|
+
).ask()
|
|
60
|
+
|
|
61
|
+
status = client.get(f"/auth/status/{slug}",
|
|
62
|
+
extra_headers={"X-User-ID": user_id})
|
|
63
|
+
if status.get("connected"):
|
|
64
|
+
console.print(f"[green]✅ {service} подключён![/green]")
|
|
65
|
+
else:
|
|
66
|
+
console.print("[yellow]⚠️ Проверьте позже.[/yellow]")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.syntax import Syntax
|
|
5
|
+
from apigw_cli.client import APIGWClient
|
|
6
|
+
from apigw_cli.config import load_config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def execute(
|
|
10
|
+
tool_name: str = typer.Argument(..., help="имя инструмента"),
|
|
11
|
+
user: str = typer.Option(None, "--user", "-u",
|
|
12
|
+
help="user_id (дефолт из конфига)"),
|
|
13
|
+
input_json: str = typer.Option(None, "--input", "-i",
|
|
14
|
+
help='JSON: \'{"key":"val"}\''),
|
|
15
|
+
idempotency_key: str = typer.Option(None, "--idempotency-key"),
|
|
16
|
+
):
|
|
17
|
+
"""Выполнить инструмент.
|
|
18
|
+
|
|
19
|
+
Примеры:
|
|
20
|
+
apigw execute telegram__send_message \\
|
|
21
|
+
--input '{"chat_id":"123","text":"Привет"}'
|
|
22
|
+
apigw execute wildberries__get_orders \\
|
|
23
|
+
--input '{"date_from":"2026-03-01","date_to":"2026-03-23"}'
|
|
24
|
+
"""
|
|
25
|
+
console = Console()
|
|
26
|
+
client = APIGWClient()
|
|
27
|
+
|
|
28
|
+
user_id = user or load_config().get("user_id")
|
|
29
|
+
if not user_id:
|
|
30
|
+
console.print("[red]❌ user_id не указан.[/red]")
|
|
31
|
+
console.print("Установите: [cyan]apigw config set user-id <id>[/cyan]")
|
|
32
|
+
raise typer.Exit(1)
|
|
33
|
+
|
|
34
|
+
input_data = {}
|
|
35
|
+
if input_json:
|
|
36
|
+
try:
|
|
37
|
+
input_data = json.loads(input_json)
|
|
38
|
+
except json.JSONDecodeError:
|
|
39
|
+
console.print("[red]❌ Невалидный JSON в --input[/red]")
|
|
40
|
+
raise typer.Exit(1)
|
|
41
|
+
|
|
42
|
+
with console.status(f"[dim]Выполняю {tool_name}...[/dim]"):
|
|
43
|
+
payload = {"tool_name": tool_name,
|
|
44
|
+
"user_id": user_id, "input": input_data}
|
|
45
|
+
if idempotency_key:
|
|
46
|
+
payload["idempotency_key"] = idempotency_key
|
|
47
|
+
result = client.post("/tools/execute", json=payload,
|
|
48
|
+
allow_errors=True)
|
|
49
|
+
|
|
50
|
+
if result.get("success"):
|
|
51
|
+
console.print(f"[green]✅ Успешно[/green] "
|
|
52
|
+
f"[dim]({result.get('duration_ms', 0)}мс)[/dim]")
|
|
53
|
+
console.print(Syntax(
|
|
54
|
+
json.dumps(result.get("data", {}),
|
|
55
|
+
ensure_ascii=False, indent=2),
|
|
56
|
+
"json", theme="monokai"
|
|
57
|
+
))
|
|
58
|
+
else:
|
|
59
|
+
error = result.get("error", "unknown")
|
|
60
|
+
if error == "reauth_required":
|
|
61
|
+
service = tool_name.split("__")[0]
|
|
62
|
+
console.print("[yellow]⚠️ Требуется переподключение[/yellow]")
|
|
63
|
+
console.print(f"Запустите: [cyan]apigw connect {service}[/cyan]")
|
|
64
|
+
else:
|
|
65
|
+
console.print(f"[red]❌ {result.get('message', error)}[/red]")
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import typer
|
|
3
|
+
import questionary
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from apigw_cli.config import load_config, save_config, get_base_url
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register():
|
|
9
|
+
"""Зарегистрироваться и получить API ключ."""
|
|
10
|
+
console = Console()
|
|
11
|
+
console.print("\n[bold cyan]API Gateway — Регистрация[/bold cyan]\n")
|
|
12
|
+
|
|
13
|
+
existing = load_config()
|
|
14
|
+
if existing.get("api_key"):
|
|
15
|
+
if not questionary.confirm(
|
|
16
|
+
"Вы уже зарегистрированы. Перерегистрироваться?"
|
|
17
|
+
).ask():
|
|
18
|
+
console.print("[green]Используется существующий ключ.[/green]")
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
email = questionary.text(
|
|
22
|
+
"Email:",
|
|
23
|
+
validate=lambda x: True if "@" in x and "." in x
|
|
24
|
+
else "Введите корректный email"
|
|
25
|
+
).ask()
|
|
26
|
+
|
|
27
|
+
name = questionary.text(
|
|
28
|
+
"Имя:",
|
|
29
|
+
validate=lambda x: True if len(x) > 1 else "Введите имя"
|
|
30
|
+
).ask()
|
|
31
|
+
|
|
32
|
+
company = questionary.text("Компания (необязательно):").ask()
|
|
33
|
+
|
|
34
|
+
console.print("\n[dim]Регистрирую...[/dim]")
|
|
35
|
+
try:
|
|
36
|
+
r = httpx.post(
|
|
37
|
+
f"{get_base_url()}/v1/auth/register",
|
|
38
|
+
json={"email": email, "name": name,
|
|
39
|
+
"company": company or None},
|
|
40
|
+
timeout=30,
|
|
41
|
+
)
|
|
42
|
+
except httpx.ConnectError:
|
|
43
|
+
console.print(f"[red]Не удалось подключиться к {get_base_url()}[/red]")
|
|
44
|
+
raise typer.Exit(1)
|
|
45
|
+
|
|
46
|
+
if r.status_code == 409:
|
|
47
|
+
console.print("[red]Email уже зарегистрирован.[/red]")
|
|
48
|
+
raise typer.Exit(1)
|
|
49
|
+
|
|
50
|
+
if r.status_code not in (200, 201):
|
|
51
|
+
console.print(f"[red]Ошибка: {r.text}[/red]")
|
|
52
|
+
raise typer.Exit(1)
|
|
53
|
+
|
|
54
|
+
api_key = r.json()["api_key"]
|
|
55
|
+
save_config(api_key=api_key, base_url=get_base_url())
|
|
56
|
+
|
|
57
|
+
console.print(f"""
|
|
58
|
+
[bold green]Готово![/bold green]
|
|
59
|
+
|
|
60
|
+
Ключ сохранён в ~/.apigw/config.json
|
|
61
|
+
|
|
62
|
+
Следующие шаги:
|
|
63
|
+
[cyan]apigw config set user-id <ваш-user-id>[/cyan]
|
|
64
|
+
[cyan]apigw tools list[/cyan]
|
|
65
|
+
[cyan]apigw chat[/cyan]
|
|
66
|
+
""")
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from apigw_cli.client import APIGWClient
|
|
6
|
+
from apigw_cli.output import print_json_data
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(help="Работа с инструментами")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command("list")
|
|
12
|
+
def tools_list(
|
|
13
|
+
region: str = typer.Option(None, help="cis или global"),
|
|
14
|
+
service: str = typer.Option(None, help="фильтр по сервису"),
|
|
15
|
+
limit: int = typer.Option(20),
|
|
16
|
+
cursor: str = typer.Option(None, help="Курсор пагинации"),
|
|
17
|
+
):
|
|
18
|
+
"""Показать список доступных инструментов."""
|
|
19
|
+
client = APIGWClient()
|
|
20
|
+
params: dict = {"limit": limit}
|
|
21
|
+
if region:
|
|
22
|
+
params["region"] = region
|
|
23
|
+
if service:
|
|
24
|
+
params["service_slug"] = service
|
|
25
|
+
if cursor:
|
|
26
|
+
params["cursor"] = cursor
|
|
27
|
+
|
|
28
|
+
data = client.get("/tools", params=params)
|
|
29
|
+
tools = data.get("tools", [])
|
|
30
|
+
|
|
31
|
+
console = Console()
|
|
32
|
+
|
|
33
|
+
if not tools:
|
|
34
|
+
console.print("[yellow]Инструменты не найдены.[/yellow]")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
table = Table(title=f"Инструменты ({len(tools)})")
|
|
38
|
+
table.add_column("Название", style="cyan")
|
|
39
|
+
table.add_column("Тип", style="yellow")
|
|
40
|
+
table.add_column("Регион")
|
|
41
|
+
table.add_column("Описание")
|
|
42
|
+
|
|
43
|
+
for t in tools:
|
|
44
|
+
table.add_row(
|
|
45
|
+
t["name"],
|
|
46
|
+
t.get("operation_type", "read"),
|
|
47
|
+
t.get("region", "global"),
|
|
48
|
+
(t.get("description") or "")[:60],
|
|
49
|
+
)
|
|
50
|
+
console.print(table)
|
|
51
|
+
|
|
52
|
+
pagination = data.get("pagination", {})
|
|
53
|
+
if pagination.get("has_more"):
|
|
54
|
+
console.print(
|
|
55
|
+
f"\n[dim]Ещё есть результаты. "
|
|
56
|
+
f"Используйте --cursor {pagination.get('next_cursor')}[/]"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@app.command("search")
|
|
61
|
+
def tools_search(query: str = typer.Argument(...)):
|
|
62
|
+
"""Найти инструмент по описанию.
|
|
63
|
+
|
|
64
|
+
Пример: apigw tools search "отзывы wildberries"
|
|
65
|
+
"""
|
|
66
|
+
client = APIGWClient()
|
|
67
|
+
data = client.post("/tools/discover",
|
|
68
|
+
json={"need": query, "limit": 5})
|
|
69
|
+
console = Console()
|
|
70
|
+
|
|
71
|
+
if not data.get("found"):
|
|
72
|
+
console.print("[yellow]Инструмент не найден.[/yellow]")
|
|
73
|
+
console.print("Создать: [cyan]apigw build <сервис> <действие>[/cyan]")
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
for t in data.get("tools", []):
|
|
77
|
+
score = t.get("relevance_score", 0)
|
|
78
|
+
console.print(Panel(
|
|
79
|
+
f"[bold]{t['name']}[/bold]\n"
|
|
80
|
+
f"{t.get('description', '')}\n\n"
|
|
81
|
+
f"[dim]Тип: {t.get('operation_type')} | "
|
|
82
|
+
f"Регион: {t.get('region')} | "
|
|
83
|
+
f"Релевантность: {score:.0%}[/dim]",
|
|
84
|
+
border_style="cyan",
|
|
85
|
+
))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@app.command("schema")
|
|
89
|
+
def tool_schema(
|
|
90
|
+
tool_name: str = typer.Argument(..., help="Имя инструмента"),
|
|
91
|
+
):
|
|
92
|
+
"""Показать JSON-схему инструмента (input/output)."""
|
|
93
|
+
client = APIGWClient()
|
|
94
|
+
data = client.get(f"/tools/{tool_name}/schema")
|
|
95
|
+
print_json_data(data)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@app.command("services")
|
|
99
|
+
def list_services():
|
|
100
|
+
"""Список доступных сервисов."""
|
|
101
|
+
client = APIGWClient()
|
|
102
|
+
data = client.get("/services")
|
|
103
|
+
console = Console()
|
|
104
|
+
|
|
105
|
+
services = data.get("services", data) if isinstance(data, dict) else data
|
|
106
|
+
if not isinstance(services, list):
|
|
107
|
+
print_json_data(data)
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
table = Table(title="Сервисы")
|
|
111
|
+
table.add_column("Slug", style="cyan")
|
|
112
|
+
table.add_column("Название")
|
|
113
|
+
table.add_column("Регион", style="yellow")
|
|
114
|
+
table.add_column("Auth")
|
|
115
|
+
table.add_column("Тулов", justify="right")
|
|
116
|
+
|
|
117
|
+
for s in services:
|
|
118
|
+
table.add_row(
|
|
119
|
+
s.get("slug", ""),
|
|
120
|
+
s.get("display_name", ""),
|
|
121
|
+
s.get("region", ""),
|
|
122
|
+
s.get("auth_type", ""),
|
|
123
|
+
str(s.get("tools_count", "")),
|
|
124
|
+
)
|
|
125
|
+
console.print(table)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
CONFIG_DIR = Path.home() / ".apigw"
|
|
6
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def save_config(api_key: str, base_url: str, user_id: str | None = None):
|
|
10
|
+
CONFIG_DIR.mkdir(exist_ok=True)
|
|
11
|
+
CONFIG_FILE.write_text(json.dumps(
|
|
12
|
+
{"api_key": api_key, "base_url": base_url, "user_id": user_id},
|
|
13
|
+
indent=2,
|
|
14
|
+
))
|
|
15
|
+
CONFIG_FILE.chmod(0o600)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_config() -> dict:
|
|
19
|
+
if not CONFIG_FILE.exists():
|
|
20
|
+
return {}
|
|
21
|
+
return json.loads(CONFIG_FILE.read_text())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_api_key() -> str:
|
|
25
|
+
key = load_config().get("api_key") or os.getenv("APIGW_API_KEY")
|
|
26
|
+
if not key:
|
|
27
|
+
raise SystemExit("❌ Не авторизован. Запустите: apigw register")
|
|
28
|
+
return key
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_base_url() -> str:
|
|
32
|
+
return (
|
|
33
|
+
load_config().get("base_url")
|
|
34
|
+
or os.getenv("APIGW_BASE_URL")
|
|
35
|
+
or "https://api-gateway-production-8bd8.up.railway.app"
|
|
36
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
app = typer.Typer(
|
|
4
|
+
name="apigw",
|
|
5
|
+
help="API Gateway CLI — управление интеграциями из терминала",
|
|
6
|
+
no_args_is_help=True,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from apigw_cli.commands import register, tools, execute, connect, build, config_cmd, chat
|
|
10
|
+
|
|
11
|
+
app.command("register")(register.register)
|
|
12
|
+
app.add_typer(tools.app, name="tools")
|
|
13
|
+
app.command("execute")(execute.execute)
|
|
14
|
+
app.command("connect")(connect.connect)
|
|
15
|
+
app.command("build")(build.build)
|
|
16
|
+
app.add_typer(config_cmd.app, name="config")
|
|
17
|
+
app.command("chat")(chat.chat)
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
app()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Helpers for pretty-printing CLI output via Rich."""
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
from rich.panel import Panel
|
|
6
|
+
from rich import print_json
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
console = Console()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def print_success(message: str):
|
|
13
|
+
console.print(f"[bold green]✅ {message}[/]")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def print_error(message: str):
|
|
17
|
+
console.print(f"[bold red]❌ {message}[/]")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def print_warning(message: str):
|
|
21
|
+
console.print(f"[bold yellow]⚠️ {message}[/]")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def print_info(message: str):
|
|
25
|
+
console.print(f"[bold blue]ℹ️ {message}[/]")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def print_json_data(data: dict | list):
|
|
29
|
+
print_json(json.dumps(data, ensure_ascii=False, indent=2))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def print_tools_table(tools: list[dict]):
|
|
33
|
+
table = Table(title="Инструменты", show_lines=True)
|
|
34
|
+
table.add_column("Имя", style="cyan", no_wrap=True)
|
|
35
|
+
table.add_column("Сервис", style="green")
|
|
36
|
+
table.add_column("Тип", style="yellow")
|
|
37
|
+
table.add_column("Описание")
|
|
38
|
+
|
|
39
|
+
for t in tools:
|
|
40
|
+
table.add_row(
|
|
41
|
+
t.get("name", ""),
|
|
42
|
+
t.get("service_slug", ""),
|
|
43
|
+
t.get("operation_type", ""),
|
|
44
|
+
t.get("description", ""),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
console.print(table)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def print_services_table(services: list[dict]):
|
|
51
|
+
table = Table(title="Сервисы", show_lines=True)
|
|
52
|
+
table.add_column("Slug", style="cyan", no_wrap=True)
|
|
53
|
+
table.add_column("Название", style="green")
|
|
54
|
+
table.add_column("Регион", style="yellow")
|
|
55
|
+
table.add_column("Auth", style="magenta")
|
|
56
|
+
table.add_column("Тулов", justify="right")
|
|
57
|
+
|
|
58
|
+
for s in services:
|
|
59
|
+
table.add_row(
|
|
60
|
+
s.get("slug", ""),
|
|
61
|
+
s.get("display_name", ""),
|
|
62
|
+
s.get("region", ""),
|
|
63
|
+
s.get("auth_type", ""),
|
|
64
|
+
str(s.get("tools_count", "")),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
console.print(table)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def print_panel(title: str, content: str):
|
|
71
|
+
console.print(Panel(content, title=title, border_style="blue"))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apigw-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI для API Gateway
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: typer[all]>=0.12
|
|
8
|
+
Requires-Dist: httpx>=0.27
|
|
9
|
+
Requires-Dist: rich>=13
|
|
10
|
+
Requires-Dist: anthropic>=0.25
|
|
11
|
+
Requires-Dist: questionary>=2
|
|
12
|
+
|
|
13
|
+
# apigw-cli
|
|
14
|
+
|
|
15
|
+
CLI для API Gateway — управляйте интеграциями из терминала.
|
|
16
|
+
|
|
17
|
+
## Установка
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install apigw-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Быстрый старт
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
apigw register
|
|
27
|
+
apigw config set user-id my_user
|
|
28
|
+
apigw tools list
|
|
29
|
+
apigw tools search "отзывы wildberries"
|
|
30
|
+
apigw connect wildberries --api-key MY_TOKEN
|
|
31
|
+
apigw execute wildberries__get_orders \
|
|
32
|
+
--input '{"date_from":"2026-03-01","date_to":"2026-03-23"}'
|
|
33
|
+
apigw chat
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Все команды
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
apigw --help
|
|
40
|
+
apigw register # регистрация
|
|
41
|
+
apigw config set # настройка
|
|
42
|
+
apigw config show # просмотр конфига
|
|
43
|
+
apigw tools list # список инструментов
|
|
44
|
+
apigw tools search # поиск инструментов
|
|
45
|
+
apigw execute # выполнить инструмент
|
|
46
|
+
apigw connect # подключить сервис
|
|
47
|
+
apigw build # создать новый инструмент
|
|
48
|
+
apigw chat # интерактивный режим
|
|
49
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
apigw_cli/__init__.py
|
|
4
|
+
apigw_cli/client.py
|
|
5
|
+
apigw_cli/config.py
|
|
6
|
+
apigw_cli/main.py
|
|
7
|
+
apigw_cli/output.py
|
|
8
|
+
apigw_cli.egg-info/PKG-INFO
|
|
9
|
+
apigw_cli.egg-info/SOURCES.txt
|
|
10
|
+
apigw_cli.egg-info/dependency_links.txt
|
|
11
|
+
apigw_cli.egg-info/entry_points.txt
|
|
12
|
+
apigw_cli.egg-info/requires.txt
|
|
13
|
+
apigw_cli.egg-info/top_level.txt
|
|
14
|
+
apigw_cli/commands/__init__.py
|
|
15
|
+
apigw_cli/commands/build.py
|
|
16
|
+
apigw_cli/commands/chat.py
|
|
17
|
+
apigw_cli/commands/config_cmd.py
|
|
18
|
+
apigw_cli/commands/connect.py
|
|
19
|
+
apigw_cli/commands/execute.py
|
|
20
|
+
apigw_cli/commands/register.py
|
|
21
|
+
apigw_cli/commands/tools.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
apigw_cli
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "apigw-cli"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "CLI для API Gateway"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"typer[all]>=0.12",
|
|
9
|
+
"httpx>=0.27",
|
|
10
|
+
"rich>=13",
|
|
11
|
+
"anthropic>=0.25",
|
|
12
|
+
"questionary>=2",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
apigw = "apigw_cli.main:app"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["setuptools>=68"]
|
|
20
|
+
build-backend = "setuptools.build_meta"
|