proxima-cli 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.
- proxima_cli-1.0.0/.gitignore +35 -0
- proxima_cli-1.0.0/PKG-INFO +115 -0
- proxima_cli-1.0.0/README.md +93 -0
- proxima_cli-1.0.0/pyproject.toml +39 -0
- proxima_cli-1.0.0/src/prox/__init__.py +3 -0
- proxima_cli-1.0.0/src/prox/api.py +107 -0
- proxima_cli-1.0.0/src/prox/auth.py +157 -0
- proxima_cli-1.0.0/src/prox/cli.py +121 -0
- proxima_cli-1.0.0/src/prox/commands/__init__.py +0 -0
- proxima_cli-1.0.0/src/prox/commands/agent.py +345 -0
- proxima_cli-1.0.0/src/prox/commands/catalog.py +242 -0
- proxima_cli-1.0.0/src/prox/commands/governance.py +63 -0
- proxima_cli-1.0.0/src/prox/commands/knowledge.py +141 -0
- proxima_cli-1.0.0/src/prox/commands/model.py +69 -0
- proxima_cli-1.0.0/src/prox/commands/ontology.py +209 -0
- proxima_cli-1.0.0/src/prox/commands/platform.py +50 -0
- proxima_cli-1.0.0/src/prox/commands/routine.py +84 -0
- proxima_cli-1.0.0/src/prox/commands/secret.py +59 -0
- proxima_cli-1.0.0/src/prox/commands/team.py +82 -0
- proxima_cli-1.0.0/src/prox/commands/toolbox.py +136 -0
- proxima_cli-1.0.0/src/prox/commands/workflow.py +129 -0
- proxima_cli-1.0.0/src/prox/config.py +98 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Synthetic data (generated, ~1.3GB)
|
|
2
|
+
agents/supply-chain/demand-forecasting/data/synthetic/
|
|
3
|
+
|
|
4
|
+
# Python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.pyc
|
|
7
|
+
*.pyo
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
env/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
|
|
13
|
+
# Environment
|
|
14
|
+
.env
|
|
15
|
+
.env.local
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.vscode/
|
|
19
|
+
.idea/
|
|
20
|
+
*.swp
|
|
21
|
+
|
|
22
|
+
# OS
|
|
23
|
+
.DS_Store
|
|
24
|
+
Thumbs.db
|
|
25
|
+
|
|
26
|
+
# Original reference docs (large PDFs, keep .md conversions only)
|
|
27
|
+
docs/reference/daimler/*.pdf
|
|
28
|
+
docs/reference/daimler/*.pptx
|
|
29
|
+
docs/reference/daimler/*.docx
|
|
30
|
+
docs/reference/daimler/*.msg
|
|
31
|
+
docs/reference/daimler/*.xlsx
|
|
32
|
+
docs/reference/daimler/*.zip
|
|
33
|
+
docs/reference/daimler/SAP Proposal - Supply Chain RFP/*.pdf
|
|
34
|
+
docs/reference/daimler/SAP Proposal - Supply Chain RFP/*.xlsx
|
|
35
|
+
platform/data/secrets.json
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: proxima-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Proxima AIP CLI — manage your Proxima Intelligence platform
|
|
5
|
+
Project-URL: Homepage, https://proximaintel.com/aip
|
|
6
|
+
Project-URL: Documentation, https://docs.proximaintel.com/cli
|
|
7
|
+
Project-URL: Repository, https://github.com/proximaintel/proxima-cli
|
|
8
|
+
Author-email: Proxima Intelligence <hello@proximaintel.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: agents,ai,cli,platform,proxima
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: httpx>=0.27
|
|
18
|
+
Requires-Dist: pyyaml>=6
|
|
19
|
+
Requires-Dist: rich>=13
|
|
20
|
+
Requires-Dist: typer[all]>=0.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Proxima CLI
|
|
24
|
+
|
|
25
|
+
The official command-line interface for **Proxima AIP** — manage your AI agent platform from the terminal.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pipx install proxima-cli
|
|
31
|
+
# or
|
|
32
|
+
pip install proxima-cli
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Login (opens browser for SSO)
|
|
39
|
+
prox login
|
|
40
|
+
|
|
41
|
+
# Check connection
|
|
42
|
+
prox whoami
|
|
43
|
+
|
|
44
|
+
# List agents
|
|
45
|
+
prox agent list
|
|
46
|
+
|
|
47
|
+
# Create a domain
|
|
48
|
+
prox domain create --id finance --name "Finance"
|
|
49
|
+
|
|
50
|
+
# Deploy an agent from a package
|
|
51
|
+
prox agent deploy --from ./my-agent/
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Set your platform gateway URL
|
|
58
|
+
prox config set gateway https://gateway.your-platform.com
|
|
59
|
+
|
|
60
|
+
# Set catalog (for pulling pre-built agents)
|
|
61
|
+
prox config set catalog https://catalog.proximaintel.com
|
|
62
|
+
|
|
63
|
+
# View config
|
|
64
|
+
prox config list
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Commands
|
|
68
|
+
|
|
69
|
+
| Command Group | Description |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `prox login / logout / whoami` | Authentication |
|
|
72
|
+
| `prox config` | Environment configuration |
|
|
73
|
+
| `prox catalog` | Browse and pull agents from Proxima Catalog |
|
|
74
|
+
| `prox agent` | Create, configure, deploy, publish agents |
|
|
75
|
+
| `prox toolbox` | Build and register toolboxes |
|
|
76
|
+
| `prox knowledge` | Manage data sources and knowledge bases |
|
|
77
|
+
| `prox secret` | Manage credentials |
|
|
78
|
+
| `prox model` | Register LLM providers |
|
|
79
|
+
| `prox ontology` | Manage entity models |
|
|
80
|
+
| `prox routine` | Schedule autonomous agent execution |
|
|
81
|
+
| `prox workflow` | Multi-agent orchestration pipelines |
|
|
82
|
+
| `prox governance` | Audit logs and usage stats |
|
|
83
|
+
| `prox platform` | Health checks and service management |
|
|
84
|
+
|
|
85
|
+
## Agent Deployment
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
prox agent deploy --from ./my-agent/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Expects:
|
|
92
|
+
```
|
|
93
|
+
my-agent/
|
|
94
|
+
├── agent.yaml # Agent config
|
|
95
|
+
├── toolbox/ # Optional: custom tools
|
|
96
|
+
│ ├── tools.py
|
|
97
|
+
│ ├── Dockerfile
|
|
98
|
+
│ └── requirements.txt
|
|
99
|
+
├── knowledge.yaml # Data source requirements
|
|
100
|
+
└── workspace.json # Dashboard layout
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
One command. Full agent. Production-ready.
|
|
104
|
+
|
|
105
|
+
## CI/CD
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
prox login --api-key $PLATFORM_API_KEY
|
|
109
|
+
prox config set gateway $GATEWAY_URL
|
|
110
|
+
prox agent deploy --from ./agents/my-agent/
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Documentation
|
|
114
|
+
|
|
115
|
+
Full docs at [docs.proximaintel.com/cli](https://docs.proximaintel.com/cli)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Proxima CLI
|
|
2
|
+
|
|
3
|
+
The official command-line interface for **Proxima AIP** — manage your AI agent platform from the terminal.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pipx install proxima-cli
|
|
9
|
+
# or
|
|
10
|
+
pip install proxima-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Login (opens browser for SSO)
|
|
17
|
+
prox login
|
|
18
|
+
|
|
19
|
+
# Check connection
|
|
20
|
+
prox whoami
|
|
21
|
+
|
|
22
|
+
# List agents
|
|
23
|
+
prox agent list
|
|
24
|
+
|
|
25
|
+
# Create a domain
|
|
26
|
+
prox domain create --id finance --name "Finance"
|
|
27
|
+
|
|
28
|
+
# Deploy an agent from a package
|
|
29
|
+
prox agent deploy --from ./my-agent/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Set your platform gateway URL
|
|
36
|
+
prox config set gateway https://gateway.your-platform.com
|
|
37
|
+
|
|
38
|
+
# Set catalog (for pulling pre-built agents)
|
|
39
|
+
prox config set catalog https://catalog.proximaintel.com
|
|
40
|
+
|
|
41
|
+
# View config
|
|
42
|
+
prox config list
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Commands
|
|
46
|
+
|
|
47
|
+
| Command Group | Description |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `prox login / logout / whoami` | Authentication |
|
|
50
|
+
| `prox config` | Environment configuration |
|
|
51
|
+
| `prox catalog` | Browse and pull agents from Proxima Catalog |
|
|
52
|
+
| `prox agent` | Create, configure, deploy, publish agents |
|
|
53
|
+
| `prox toolbox` | Build and register toolboxes |
|
|
54
|
+
| `prox knowledge` | Manage data sources and knowledge bases |
|
|
55
|
+
| `prox secret` | Manage credentials |
|
|
56
|
+
| `prox model` | Register LLM providers |
|
|
57
|
+
| `prox ontology` | Manage entity models |
|
|
58
|
+
| `prox routine` | Schedule autonomous agent execution |
|
|
59
|
+
| `prox workflow` | Multi-agent orchestration pipelines |
|
|
60
|
+
| `prox governance` | Audit logs and usage stats |
|
|
61
|
+
| `prox platform` | Health checks and service management |
|
|
62
|
+
|
|
63
|
+
## Agent Deployment
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
prox agent deploy --from ./my-agent/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Expects:
|
|
70
|
+
```
|
|
71
|
+
my-agent/
|
|
72
|
+
├── agent.yaml # Agent config
|
|
73
|
+
├── toolbox/ # Optional: custom tools
|
|
74
|
+
│ ├── tools.py
|
|
75
|
+
│ ├── Dockerfile
|
|
76
|
+
│ └── requirements.txt
|
|
77
|
+
├── knowledge.yaml # Data source requirements
|
|
78
|
+
└── workspace.json # Dashboard layout
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
One command. Full agent. Production-ready.
|
|
82
|
+
|
|
83
|
+
## CI/CD
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
prox login --api-key $PLATFORM_API_KEY
|
|
87
|
+
prox config set gateway $GATEWAY_URL
|
|
88
|
+
prox agent deploy --from ./agents/my-agent/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
Full docs at [docs.proximaintel.com/cli](https://docs.proximaintel.com/cli)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "proxima-cli"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Proxima AIP CLI — manage your Proxima Intelligence platform"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Proxima Intelligence", email = "hello@proximaintel.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ai", "agents", "platform", "cli", "proxima"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"typer[all]>=0.12",
|
|
25
|
+
"httpx>=0.27",
|
|
26
|
+
"rich>=13",
|
|
27
|
+
"pyyaml>=6",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
prox = "prox.cli:app"
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://proximaintel.com/aip"
|
|
35
|
+
Documentation = "https://docs.proximaintel.com/cli"
|
|
36
|
+
Repository = "https://github.com/proximaintel/proxima-cli"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/prox"]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""HTTP client for gateway and catalog APIs."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from . import config
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class APIError(Exception):
|
|
9
|
+
def __init__(self, status: int, detail: str):
|
|
10
|
+
self.status = status
|
|
11
|
+
self.detail = detail
|
|
12
|
+
super().__init__(f"[{status}] {detail}")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _gateway_url() -> str:
|
|
16
|
+
url = config.get_value("gateway")
|
|
17
|
+
if not url:
|
|
18
|
+
raise APIError(0, "Gateway URL not configured. Run: prox config set gateway <url>")
|
|
19
|
+
return url.rstrip("/")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _catalog_url() -> str:
|
|
23
|
+
url = config.get_value("catalog") or "https://catalog.proximaintel.com"
|
|
24
|
+
return url.rstrip("/")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _headers() -> dict:
|
|
28
|
+
headers = {"Content-Type": "application/json"}
|
|
29
|
+
token = config.get_token()
|
|
30
|
+
if token:
|
|
31
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
32
|
+
return headers
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _catalog_headers() -> dict:
|
|
36
|
+
headers = {"Content-Type": "application/json"}
|
|
37
|
+
master = config.get_master_key()
|
|
38
|
+
if master:
|
|
39
|
+
headers["X-Master-Key"] = master
|
|
40
|
+
else:
|
|
41
|
+
lic = config.get_license_key()
|
|
42
|
+
if lic:
|
|
43
|
+
headers["X-License-Key"] = lic
|
|
44
|
+
return headers
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# --- Gateway calls ---
|
|
48
|
+
|
|
49
|
+
def gateway_get(path: str, params: Optional[dict] = None) -> dict:
|
|
50
|
+
url = f"{_gateway_url()}{path}"
|
|
51
|
+
r = httpx.get(url, headers=_headers(), params=params, timeout=30, verify=False)
|
|
52
|
+
if r.status_code >= 400:
|
|
53
|
+
raise APIError(r.status_code, r.text[:500])
|
|
54
|
+
return r.json()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def gateway_post(path: str, data: Optional[dict] = None) -> dict:
|
|
58
|
+
url = f"{_gateway_url()}{path}"
|
|
59
|
+
r = httpx.post(url, headers=_headers(), json=data, timeout=60, verify=False)
|
|
60
|
+
if r.status_code >= 400:
|
|
61
|
+
raise APIError(r.status_code, r.text[:500])
|
|
62
|
+
return r.json()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def gateway_put(path: str, data: Optional[dict] = None) -> dict:
|
|
66
|
+
url = f"{_gateway_url()}{path}"
|
|
67
|
+
r = httpx.put(url, headers=_headers(), json=data, timeout=60, verify=False)
|
|
68
|
+
if r.status_code >= 400:
|
|
69
|
+
raise APIError(r.status_code, r.text[:500])
|
|
70
|
+
return r.json()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def gateway_delete(path: str) -> dict:
|
|
74
|
+
url = f"{_gateway_url()}{path}"
|
|
75
|
+
r = httpx.delete(url, headers=_headers(), timeout=30, verify=False)
|
|
76
|
+
if r.status_code >= 400:
|
|
77
|
+
raise APIError(r.status_code, r.text[:500])
|
|
78
|
+
return r.json()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# --- Catalog calls ---
|
|
82
|
+
|
|
83
|
+
def catalog_get(path: str, params: Optional[dict] = None) -> dict:
|
|
84
|
+
url = f"{_catalog_url()}{path}"
|
|
85
|
+
r = httpx.get(url, headers=_catalog_headers(), params=params, timeout=30)
|
|
86
|
+
if r.status_code >= 400:
|
|
87
|
+
raise APIError(r.status_code, r.text[:500])
|
|
88
|
+
return r.json()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def catalog_post(path: str, data: Optional[dict] = None) -> dict:
|
|
92
|
+
url = f"{_catalog_url()}{path}"
|
|
93
|
+
r = httpx.post(url, headers=_catalog_headers(), json=data, timeout=60)
|
|
94
|
+
if r.status_code >= 400:
|
|
95
|
+
raise APIError(r.status_code, r.text[:500])
|
|
96
|
+
return r.json()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def catalog_download(path: str, dest: str):
|
|
100
|
+
"""Download a file from the catalog to a local path."""
|
|
101
|
+
url = f"{_catalog_url()}{path}"
|
|
102
|
+
with httpx.stream("GET", url, headers=_catalog_headers(), timeout=120) as r:
|
|
103
|
+
if r.status_code >= 400:
|
|
104
|
+
raise APIError(r.status_code, f"Download failed: {r.status_code}")
|
|
105
|
+
with open(dest, "wb") as f:
|
|
106
|
+
for chunk in r.iter_bytes(chunk_size=8192):
|
|
107
|
+
f.write(chunk)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""Authentication — login, logout, token management."""
|
|
2
|
+
|
|
3
|
+
import http.server
|
|
4
|
+
import json
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
import urllib.parse
|
|
8
|
+
import webbrowser
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from . import config
|
|
12
|
+
from .api import gateway_get
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def login_sso():
|
|
16
|
+
"""Open browser for SSO login, capture token via local callback server."""
|
|
17
|
+
# Get auth config from gateway
|
|
18
|
+
gateway = config.get_value("gateway")
|
|
19
|
+
if not gateway:
|
|
20
|
+
raise RuntimeError("Gateway not configured. Run: prox config set gateway <url>")
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
auth_config = gateway_get("/auth/config")
|
|
24
|
+
except Exception as e:
|
|
25
|
+
raise RuntimeError(f"Cannot reach gateway: {e}")
|
|
26
|
+
|
|
27
|
+
if not auth_config.get("enabled"):
|
|
28
|
+
raise RuntimeError("SSO not enabled on this gateway (dev mode). Use --api-key instead.")
|
|
29
|
+
|
|
30
|
+
authority = auth_config.get("authority", "")
|
|
31
|
+
client_id = auth_config.get("client_id", "")
|
|
32
|
+
scopes = auth_config.get("scopes", "openid profile email")
|
|
33
|
+
|
|
34
|
+
if not authority or not client_id:
|
|
35
|
+
raise RuntimeError("Gateway auth config missing authority or client_id")
|
|
36
|
+
|
|
37
|
+
# Start local server to receive the callback
|
|
38
|
+
token_result = {"token": None}
|
|
39
|
+
port = 8399
|
|
40
|
+
redirect_uri = f"http://localhost:{port}/callback"
|
|
41
|
+
|
|
42
|
+
class CallbackHandler(http.server.BaseHTTPRequestHandler):
|
|
43
|
+
def do_GET(self):
|
|
44
|
+
parsed = urllib.parse.urlparse(self.path)
|
|
45
|
+
if parsed.path == "/callback":
|
|
46
|
+
# Token comes in fragment (implicit) — serve a page that sends it back
|
|
47
|
+
self.send_response(200)
|
|
48
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
49
|
+
self.end_headers()
|
|
50
|
+
html = """
|
|
51
|
+
<html><head><meta charset="utf-8"></head><body><script>
|
|
52
|
+
const hash = window.location.hash.substring(1);
|
|
53
|
+
const params = new URLSearchParams(hash);
|
|
54
|
+
const token = params.get('access_token');
|
|
55
|
+
if (token) {
|
|
56
|
+
fetch('/token?t=' + token).then(() => {
|
|
57
|
+
document.body.innerHTML = '<h2>Authenticated. You can close this tab.</h2>';
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
document.body.innerHTML = '<h2>No token received.</h2>';
|
|
61
|
+
}
|
|
62
|
+
</script><p>Authenticating...</p></body></html>
|
|
63
|
+
"""
|
|
64
|
+
self.wfile.write(html.encode("utf-8"))
|
|
65
|
+
elif parsed.path == "/token":
|
|
66
|
+
qs = urllib.parse.parse_qs(parsed.query)
|
|
67
|
+
token_result["token"] = qs.get("t", [None])[0]
|
|
68
|
+
self.send_response(200)
|
|
69
|
+
self.end_headers()
|
|
70
|
+
self.wfile.write(b"ok")
|
|
71
|
+
else:
|
|
72
|
+
self.send_response(404)
|
|
73
|
+
self.end_headers()
|
|
74
|
+
|
|
75
|
+
def log_message(self, format, *args):
|
|
76
|
+
pass # Suppress server logs
|
|
77
|
+
|
|
78
|
+
server = http.server.HTTPServer(("localhost", port), CallbackHandler)
|
|
79
|
+
thread = threading.Thread(target=server.handle_request) # Handle callback page
|
|
80
|
+
thread.daemon = True
|
|
81
|
+
thread.start()
|
|
82
|
+
|
|
83
|
+
# Also need to handle the /token request
|
|
84
|
+
thread2 = threading.Thread(target=server.handle_request)
|
|
85
|
+
thread2.daemon = True
|
|
86
|
+
thread2.start()
|
|
87
|
+
|
|
88
|
+
# Build auth URL (implicit grant)
|
|
89
|
+
auth_url = (
|
|
90
|
+
f"{authority}/oauth2/v2.0/authorize?"
|
|
91
|
+
f"client_id={client_id}"
|
|
92
|
+
f"&response_type=token"
|
|
93
|
+
f"&redirect_uri={urllib.parse.quote(redirect_uri)}"
|
|
94
|
+
f"&scope={urllib.parse.quote(scopes)}"
|
|
95
|
+
f"&response_mode=fragment"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
webbrowser.open(auth_url)
|
|
99
|
+
|
|
100
|
+
# Wait for token (timeout 120s)
|
|
101
|
+
deadline = time.time() + 120
|
|
102
|
+
while not token_result["token"] and time.time() < deadline:
|
|
103
|
+
time.sleep(0.5)
|
|
104
|
+
|
|
105
|
+
server.server_close()
|
|
106
|
+
|
|
107
|
+
if not token_result["token"]:
|
|
108
|
+
raise RuntimeError("Login timed out. No token received within 120 seconds.")
|
|
109
|
+
|
|
110
|
+
# Store token
|
|
111
|
+
creds = config.load_credentials()
|
|
112
|
+
creds["token"] = token_result["token"]
|
|
113
|
+
creds["method"] = "sso"
|
|
114
|
+
creds["authenticated_at"] = time.time()
|
|
115
|
+
config.save_credentials(creds)
|
|
116
|
+
return token_result["token"]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def login_api_key(api_key: str):
|
|
120
|
+
"""Login with a platform API key."""
|
|
121
|
+
creds = config.load_credentials()
|
|
122
|
+
creds["token"] = api_key
|
|
123
|
+
creds["method"] = "api_key"
|
|
124
|
+
config.save_credentials(creds)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def login_license_key(license_key: str):
|
|
128
|
+
"""Store catalog license key."""
|
|
129
|
+
creds = config.load_credentials()
|
|
130
|
+
creds["license_key"] = license_key
|
|
131
|
+
config.save_credentials(creds)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def login_master_key(master_key: str):
|
|
135
|
+
"""Store catalog master key (Proxima team only)."""
|
|
136
|
+
creds = config.load_credentials()
|
|
137
|
+
creds["master_key"] = master_key
|
|
138
|
+
config.save_credentials(creds)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def logout():
|
|
142
|
+
"""Clear all stored credentials."""
|
|
143
|
+
config.save_credentials({})
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def whoami() -> dict:
|
|
147
|
+
"""Get current auth state."""
|
|
148
|
+
creds = config.load_credentials()
|
|
149
|
+
return {
|
|
150
|
+
"authenticated": bool(creds.get("token") or creds.get("master_key")),
|
|
151
|
+
"method": creds.get("method", "none"),
|
|
152
|
+
"has_license_key": bool(creds.get("license_key")),
|
|
153
|
+
"has_master_key": bool(creds.get("master_key")),
|
|
154
|
+
"environment": config.current_environment(),
|
|
155
|
+
"gateway": config.get_value("gateway"),
|
|
156
|
+
"catalog": config.get_value("catalog"),
|
|
157
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Proxima Platform CLI — entry point."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
from . import __version__, auth, config
|
|
7
|
+
from .commands import catalog, agent, toolbox, knowledge, secret, model, routine, workflow, governance, platform, ontology, team
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(
|
|
10
|
+
name="prox",
|
|
11
|
+
help="Proxima Intelligence Platform CLI",
|
|
12
|
+
no_args_is_help=True,
|
|
13
|
+
pretty_exceptions_enable=False,
|
|
14
|
+
)
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
# --- Command groups ---
|
|
18
|
+
app.add_typer(catalog.app, name="catalog")
|
|
19
|
+
app.add_typer(agent.app, name="agent")
|
|
20
|
+
app.add_typer(toolbox.app, name="toolbox")
|
|
21
|
+
app.add_typer(knowledge.app, name="knowledge")
|
|
22
|
+
app.add_typer(ontology.app, name="ontology")
|
|
23
|
+
app.add_typer(secret.app, name="secret")
|
|
24
|
+
app.add_typer(model.app, name="model")
|
|
25
|
+
app.add_typer(routine.app, name="routine")
|
|
26
|
+
app.add_typer(workflow.app, name="workflow")
|
|
27
|
+
app.add_typer(governance.app, name="governance")
|
|
28
|
+
app.add_typer(platform.app, name="platform")
|
|
29
|
+
app.add_typer(team.app, name="team")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# --- Top-level commands ---
|
|
33
|
+
|
|
34
|
+
@app.command()
|
|
35
|
+
def login(
|
|
36
|
+
api_key: str = typer.Option(None, "--api-key", help="Platform API key"),
|
|
37
|
+
license_key: str = typer.Option(None, "--license-key", help="Catalog license key"),
|
|
38
|
+
master_key: str = typer.Option(None, "--master-key", help="Catalog master key (Proxima only)"),
|
|
39
|
+
):
|
|
40
|
+
"""Authenticate with the platform and/or catalog.
|
|
41
|
+
|
|
42
|
+
With no flags: opens browser for SSO login.
|
|
43
|
+
"""
|
|
44
|
+
if not api_key and not license_key and not master_key:
|
|
45
|
+
# SSO flow
|
|
46
|
+
console.print("Opening browser for SSO login...")
|
|
47
|
+
try:
|
|
48
|
+
auth.login_sso()
|
|
49
|
+
console.print("[green]✓[/green] Authenticated via SSO")
|
|
50
|
+
except RuntimeError as e:
|
|
51
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
52
|
+
raise typer.Exit(1)
|
|
53
|
+
return
|
|
54
|
+
if api_key:
|
|
55
|
+
auth.login_api_key(api_key)
|
|
56
|
+
console.print("[green]✓[/green] Platform API key stored")
|
|
57
|
+
if license_key:
|
|
58
|
+
auth.login_license_key(license_key)
|
|
59
|
+
console.print("[green]✓[/green] Catalog license key stored")
|
|
60
|
+
if master_key:
|
|
61
|
+
auth.login_master_key(master_key)
|
|
62
|
+
console.print("[green]✓[/green] Catalog master key stored")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@app.command()
|
|
66
|
+
def logout():
|
|
67
|
+
"""Clear all stored credentials."""
|
|
68
|
+
auth.logout()
|
|
69
|
+
console.print("[green]✓[/green] Logged out")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@app.command()
|
|
73
|
+
def whoami():
|
|
74
|
+
"""Show current authentication state."""
|
|
75
|
+
info = auth.whoami()
|
|
76
|
+
console.print(f"\n Environment: [bold]{info['environment']}[/bold]")
|
|
77
|
+
console.print(f" Gateway: {info['gateway'] or '[dim]not set[/dim]'}")
|
|
78
|
+
console.print(f" Catalog: {info['catalog'] or '[dim]not set[/dim]'}")
|
|
79
|
+
console.print(f" Auth: {'[green]authenticated[/green]' if info['authenticated'] else '[yellow]not authenticated[/yellow]'} ({info['method']})")
|
|
80
|
+
console.print(f" License key: {'[green]✓[/green]' if info['has_license_key'] else '[dim]—[/dim]'}")
|
|
81
|
+
console.print(f" Master key: {'[green]✓[/green]' if info['has_master_key'] else '[dim]—[/dim]'}")
|
|
82
|
+
console.print()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# --- Config commands ---
|
|
86
|
+
|
|
87
|
+
config_app = typer.Typer(help="Manage CLI configuration.")
|
|
88
|
+
app.add_typer(config_app, name="config")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@config_app.command("set")
|
|
92
|
+
def config_set(key: str = typer.Argument(help="Config key"), value: str = typer.Argument(help="Config value")):
|
|
93
|
+
"""Set a configuration value for the current environment."""
|
|
94
|
+
config.set_value(key, value)
|
|
95
|
+
console.print(f"[green]✓[/green] {key} = {value} (env: {config.current_environment()})")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@config_app.command("list")
|
|
99
|
+
def config_list():
|
|
100
|
+
"""Show current configuration."""
|
|
101
|
+
env = config.current_environment()
|
|
102
|
+
env_config = config.get_env_config()
|
|
103
|
+
console.print(f"\n [bold]Environment:[/bold] {env}\n")
|
|
104
|
+
for k, v in env_config.items():
|
|
105
|
+
console.print(f" {k}: {v or '[dim]not set[/dim]'}")
|
|
106
|
+
console.print()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@config_app.command("use")
|
|
110
|
+
def config_use(environment: str = typer.Argument(help="Environment name")):
|
|
111
|
+
"""Switch to a different environment."""
|
|
112
|
+
config.use_environment(environment)
|
|
113
|
+
console.print(f"[green]✓[/green] Switched to: {environment}")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# --- Version ---
|
|
117
|
+
|
|
118
|
+
@app.command()
|
|
119
|
+
def version():
|
|
120
|
+
"""Show CLI version."""
|
|
121
|
+
console.print(f"prox {__version__}")
|
|
File without changes
|