proxima-cli 1.0.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.
- prox/__init__.py +3 -0
- prox/api.py +107 -0
- prox/auth.py +157 -0
- prox/cli.py +121 -0
- prox/commands/__init__.py +0 -0
- prox/commands/agent.py +345 -0
- prox/commands/catalog.py +242 -0
- prox/commands/governance.py +63 -0
- prox/commands/knowledge.py +141 -0
- prox/commands/model.py +69 -0
- prox/commands/ontology.py +209 -0
- prox/commands/platform.py +50 -0
- prox/commands/routine.py +84 -0
- prox/commands/secret.py +59 -0
- prox/commands/team.py +82 -0
- prox/commands/toolbox.py +136 -0
- prox/commands/workflow.py +129 -0
- prox/config.py +98 -0
- proxima_cli-1.0.0.dist-info/METADATA +115 -0
- proxima_cli-1.0.0.dist-info/RECORD +22 -0
- proxima_cli-1.0.0.dist-info/WHEEL +4 -0
- proxima_cli-1.0.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""prox knowledge — manage knowledge sources and bases."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from ..api import gateway_get, gateway_post, gateway_delete, APIError
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(help="Manage knowledge sources and bases.")
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
source_app = typer.Typer(help="Manage knowledge sources.")
|
|
16
|
+
app.add_typer(source_app, name="source")
|
|
17
|
+
|
|
18
|
+
base_app = typer.Typer(help="Manage knowledge bases.")
|
|
19
|
+
app.add_typer(base_app, name="base")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@source_app.command("list")
|
|
23
|
+
def list_sources():
|
|
24
|
+
"""List knowledge sources."""
|
|
25
|
+
try:
|
|
26
|
+
data = gateway_get("/build/knowledge/sources")
|
|
27
|
+
sources = data.get("sources", [])
|
|
28
|
+
if not sources:
|
|
29
|
+
console.print("[dim]No sources.[/dim]")
|
|
30
|
+
return
|
|
31
|
+
table = Table(title="Knowledge Sources")
|
|
32
|
+
table.add_column("ID", style="bold")
|
|
33
|
+
table.add_column("Name")
|
|
34
|
+
table.add_column("Provider")
|
|
35
|
+
for s in sources:
|
|
36
|
+
table.add_row(s.get("id"), s.get("name", ""), s.get("provider", ""))
|
|
37
|
+
console.print(table)
|
|
38
|
+
except APIError as e:
|
|
39
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
40
|
+
raise typer.Exit(1)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@source_app.command("create")
|
|
44
|
+
def create_source(
|
|
45
|
+
id: str = typer.Option(..., "--id"),
|
|
46
|
+
name: str = typer.Option(..., "--name"),
|
|
47
|
+
provider: str = typer.Option(..., "--provider", help="azure_blob, s3, snowflake, etc."),
|
|
48
|
+
secret: Optional[str] = typer.Option(None, "--secret", help="Secret name for credentials"),
|
|
49
|
+
container: Optional[str] = typer.Option(None, "--container"),
|
|
50
|
+
path: Optional[str] = typer.Option(None, "--path"),
|
|
51
|
+
):
|
|
52
|
+
"""Create a knowledge source."""
|
|
53
|
+
payload = {"id": id, "name": name, "provider": provider}
|
|
54
|
+
if secret:
|
|
55
|
+
payload["credential_secret"] = secret
|
|
56
|
+
if container:
|
|
57
|
+
payload["container"] = container
|
|
58
|
+
if path:
|
|
59
|
+
payload["path"] = path
|
|
60
|
+
try:
|
|
61
|
+
gateway_post("/build/knowledge/sources", payload)
|
|
62
|
+
console.print(f"[green]✓[/green] Source created: {id}")
|
|
63
|
+
except APIError as e:
|
|
64
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
65
|
+
raise typer.Exit(1)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@source_app.command("test")
|
|
69
|
+
def test_source(source_id: str = typer.Argument(help="Source ID to test")):
|
|
70
|
+
"""Test a source connection."""
|
|
71
|
+
try:
|
|
72
|
+
data = gateway_post(f"/build/knowledge/sources/{source_id}/test")
|
|
73
|
+
if data.get("success"):
|
|
74
|
+
console.print(f"[green]✓[/green] Connection successful ({data.get('rows', '?')} rows)")
|
|
75
|
+
else:
|
|
76
|
+
console.print(f"[red]✗[/red] {data.get('error', 'Connection failed')}")
|
|
77
|
+
except APIError as e:
|
|
78
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
79
|
+
raise typer.Exit(1)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@source_app.command("delete")
|
|
83
|
+
def delete_source(source_id: str = typer.Argument(), confirm: bool = typer.Option(False, "--yes", "-y")):
|
|
84
|
+
"""Delete a knowledge source."""
|
|
85
|
+
if not confirm:
|
|
86
|
+
typer.confirm(f"Delete source '{source_id}'?", abort=True)
|
|
87
|
+
try:
|
|
88
|
+
gateway_delete(f"/build/knowledge/sources/{source_id}")
|
|
89
|
+
console.print(f"[green]✓[/green] Deleted: {source_id}")
|
|
90
|
+
except APIError as e:
|
|
91
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
92
|
+
raise typer.Exit(1)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@base_app.command("list")
|
|
96
|
+
def list_bases():
|
|
97
|
+
"""List knowledge bases."""
|
|
98
|
+
try:
|
|
99
|
+
data = gateway_get("/build/knowledge/bases")
|
|
100
|
+
bases = data.get("bases", [])
|
|
101
|
+
if not bases:
|
|
102
|
+
console.print("[dim]No bases.[/dim]")
|
|
103
|
+
return
|
|
104
|
+
table = Table(title="Knowledge Bases")
|
|
105
|
+
table.add_column("ID", style="bold")
|
|
106
|
+
table.add_column("Name")
|
|
107
|
+
table.add_column("Sources", justify="right")
|
|
108
|
+
for b in bases:
|
|
109
|
+
table.add_row(b.get("id"), b.get("name", ""), str(len(b.get("sources", []))))
|
|
110
|
+
console.print(table)
|
|
111
|
+
except APIError as e:
|
|
112
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
113
|
+
raise typer.Exit(1)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@base_app.command("create")
|
|
117
|
+
def create_base(
|
|
118
|
+
id: str = typer.Option(..., "--id"),
|
|
119
|
+
name: str = typer.Option(..., "--name"),
|
|
120
|
+
sources: str = typer.Option(..., "--sources", help="Comma-separated source IDs"),
|
|
121
|
+
):
|
|
122
|
+
"""Create a knowledge base."""
|
|
123
|
+
try:
|
|
124
|
+
gateway_post("/build/knowledge/bases", {"id": id, "name": name, "sources": sources.split(",")})
|
|
125
|
+
console.print(f"[green]✓[/green] Base created: {id}")
|
|
126
|
+
except APIError as e:
|
|
127
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
128
|
+
raise typer.Exit(1)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@base_app.command("delete")
|
|
132
|
+
def delete_base(base_id: str = typer.Argument(), confirm: bool = typer.Option(False, "--yes", "-y")):
|
|
133
|
+
"""Delete a knowledge base."""
|
|
134
|
+
if not confirm:
|
|
135
|
+
typer.confirm(f"Delete base '{base_id}'?", abort=True)
|
|
136
|
+
try:
|
|
137
|
+
gateway_delete(f"/build/knowledge/bases/{base_id}")
|
|
138
|
+
console.print(f"[green]✓[/green] Deleted: {base_id}")
|
|
139
|
+
except APIError as e:
|
|
140
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
141
|
+
raise typer.Exit(1)
|
prox/commands/model.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""prox model — manage LLM model connections."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
import typer
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
from ..api import gateway_get, gateway_post, gateway_delete, APIError
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Manage LLM models.")
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@app.command("list")
|
|
14
|
+
def list_models():
|
|
15
|
+
"""List deployed models."""
|
|
16
|
+
try:
|
|
17
|
+
data = gateway_get("/build/models")
|
|
18
|
+
models = data.get("models", [])
|
|
19
|
+
if not models:
|
|
20
|
+
console.print("[dim]No models.[/dim]")
|
|
21
|
+
return
|
|
22
|
+
table = Table(title="Models")
|
|
23
|
+
table.add_column("ID", style="bold")
|
|
24
|
+
table.add_column("Provider")
|
|
25
|
+
table.add_column("Model")
|
|
26
|
+
table.add_column("Status")
|
|
27
|
+
for m in models:
|
|
28
|
+
st = m.get("status", "unknown")
|
|
29
|
+
st_style = "green" if st == "active" else "yellow"
|
|
30
|
+
table.add_row(m.get("id"), m.get("provider", ""), m.get("model", ""), f"[{st_style}]{st}[/{st_style}]")
|
|
31
|
+
console.print(table)
|
|
32
|
+
except APIError as e:
|
|
33
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
34
|
+
raise typer.Exit(1)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@app.command("deploy")
|
|
38
|
+
def deploy_model(
|
|
39
|
+
id: str = typer.Option(..., "--id"),
|
|
40
|
+
provider: str = typer.Option(..., "--provider", help="openai, anthropic, bedrock, watsonx, ollama"),
|
|
41
|
+
model: str = typer.Option(..., "--model", help="Model name (e.g. gpt-4.1-mini)"),
|
|
42
|
+
endpoint_secret: Optional[str] = typer.Option(None, "--endpoint-secret"),
|
|
43
|
+
key_secret: Optional[str] = typer.Option(None, "--key-secret"),
|
|
44
|
+
):
|
|
45
|
+
"""Deploy a model connection."""
|
|
46
|
+
payload = {"id": id, "provider": provider, "model": model}
|
|
47
|
+
if endpoint_secret:
|
|
48
|
+
payload["endpoint_secret"] = endpoint_secret
|
|
49
|
+
if key_secret:
|
|
50
|
+
payload["key_secret"] = key_secret
|
|
51
|
+
try:
|
|
52
|
+
gateway_post("/build/models", payload)
|
|
53
|
+
console.print(f"[green]✓[/green] Model deployed: {id}")
|
|
54
|
+
except APIError as e:
|
|
55
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
56
|
+
raise typer.Exit(1)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@app.command("delete")
|
|
60
|
+
def delete_model(model_id: str = typer.Argument(), confirm: bool = typer.Option(False, "--yes", "-y")):
|
|
61
|
+
"""Delete a model."""
|
|
62
|
+
if not confirm:
|
|
63
|
+
typer.confirm(f"Delete model '{model_id}'?", abort=True)
|
|
64
|
+
try:
|
|
65
|
+
gateway_delete(f"/build/models/{model_id}")
|
|
66
|
+
console.print(f"[green]✓[/green] Deleted: {model_id}")
|
|
67
|
+
except APIError as e:
|
|
68
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
69
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""prox ontology — manage ontologies on the platform."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
import yaml
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from ..api import gateway_get, gateway_post, gateway_put, gateway_delete, APIError
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(help="Manage ontologies.")
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.command("list")
|
|
18
|
+
def list_ontologies():
|
|
19
|
+
"""List all ontologies on the platform."""
|
|
20
|
+
try:
|
|
21
|
+
data = gateway_get("/build/ontology")
|
|
22
|
+
except APIError as e:
|
|
23
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
24
|
+
raise typer.Exit(1)
|
|
25
|
+
|
|
26
|
+
ontologies = data.get("ontologies", [])
|
|
27
|
+
if not ontologies:
|
|
28
|
+
console.print("[dim]No ontologies.[/dim]")
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
table = Table(title=f"Ontologies ({len(ontologies)})")
|
|
32
|
+
table.add_column("ID", style="bold")
|
|
33
|
+
table.add_column("Name")
|
|
34
|
+
table.add_column("Entities", justify="right")
|
|
35
|
+
table.add_column("Actions", justify="right")
|
|
36
|
+
table.add_column("Version")
|
|
37
|
+
|
|
38
|
+
for o in ontologies:
|
|
39
|
+
table.add_row(o.get("id"), o.get("name", ""), str(o.get("entities", 0)), str(o.get("actions", 0)), o.get("version", "—"))
|
|
40
|
+
|
|
41
|
+
console.print(table)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.command("show")
|
|
45
|
+
def show_ontology(ontology_id: str = typer.Argument(help="Ontology ID")):
|
|
46
|
+
"""Show ontology details."""
|
|
47
|
+
try:
|
|
48
|
+
data = gateway_get(f"/build/ontology/{ontology_id}")
|
|
49
|
+
except APIError as e:
|
|
50
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
51
|
+
raise typer.Exit(1)
|
|
52
|
+
|
|
53
|
+
o = data.get("ontology", {})
|
|
54
|
+
console.print(f"\n[bold]{o.get('name', ontology_id)}[/bold] (v{o.get('version', '?')})")
|
|
55
|
+
console.print(f"[dim]{o.get('description', '')}[/dim]\n")
|
|
56
|
+
|
|
57
|
+
entities = o.get("entities", {})
|
|
58
|
+
relationships = o.get("relationships", [])
|
|
59
|
+
actions = o.get("actions", {})
|
|
60
|
+
events = o.get("events", [])
|
|
61
|
+
|
|
62
|
+
console.print(f" Entities: {len(entities)}")
|
|
63
|
+
console.print(f" Relationships: {len(relationships)}")
|
|
64
|
+
console.print(f" Actions: {len(actions)}")
|
|
65
|
+
console.print(f" Events: {len(events)}")
|
|
66
|
+
console.print()
|
|
67
|
+
|
|
68
|
+
if entities:
|
|
69
|
+
console.print(" [bold]Entities:[/bold]")
|
|
70
|
+
for name, edef in entities.items():
|
|
71
|
+
props = len(edef.get("properties", {}))
|
|
72
|
+
console.print(f" • {name} ({props} properties)")
|
|
73
|
+
if actions:
|
|
74
|
+
console.print(f"\n [bold]Actions:[/bold]")
|
|
75
|
+
for aname, adef in actions.items():
|
|
76
|
+
console.print(f" • {aname} → {adef.get('entity', '?')}")
|
|
77
|
+
console.print()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@app.command("create")
|
|
81
|
+
def create_ontology(
|
|
82
|
+
from_file: Path = typer.Option(..., "--from", help="YAML ontology definition file"),
|
|
83
|
+
):
|
|
84
|
+
"""Create an ontology from a YAML file."""
|
|
85
|
+
if not from_file.exists():
|
|
86
|
+
console.print(f"[red]Error:[/red] File not found: {from_file}")
|
|
87
|
+
raise typer.Exit(1)
|
|
88
|
+
|
|
89
|
+
data = yaml.safe_load(from_file.read_text())
|
|
90
|
+
if not data.get("id"):
|
|
91
|
+
console.print("[red]Error:[/red] Ontology YAML must have an 'id' field")
|
|
92
|
+
raise typer.Exit(1)
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
gateway_post("/build/ontology", data)
|
|
96
|
+
console.print(f"[green]✓[/green] Ontology created: {data['id']}")
|
|
97
|
+
except APIError as e:
|
|
98
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
99
|
+
raise typer.Exit(1)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@app.command("update")
|
|
103
|
+
def update_ontology(
|
|
104
|
+
ontology_id: str = typer.Argument(help="Ontology ID"),
|
|
105
|
+
from_file: Path = typer.Option(..., "--from", help="YAML ontology definition file"),
|
|
106
|
+
):
|
|
107
|
+
"""Update an existing ontology from a YAML file."""
|
|
108
|
+
if not from_file.exists():
|
|
109
|
+
console.print(f"[red]Error:[/red] File not found: {from_file}")
|
|
110
|
+
raise typer.Exit(1)
|
|
111
|
+
|
|
112
|
+
data = yaml.safe_load(from_file.read_text())
|
|
113
|
+
data["id"] = ontology_id
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
gateway_put(f"/build/ontology/{ontology_id}", data)
|
|
117
|
+
console.print(f"[green]✓[/green] Ontology updated: {ontology_id}")
|
|
118
|
+
except APIError as e:
|
|
119
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
120
|
+
raise typer.Exit(1)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@app.command("validate")
|
|
124
|
+
def validate_ontology_cmd(
|
|
125
|
+
from_file: Path = typer.Option(..., "--from", help="YAML ontology file to validate"),
|
|
126
|
+
):
|
|
127
|
+
"""Validate an ontology YAML file without creating it."""
|
|
128
|
+
if not from_file.exists():
|
|
129
|
+
console.print(f"[red]Error:[/red] File not found: {from_file}")
|
|
130
|
+
raise typer.Exit(1)
|
|
131
|
+
|
|
132
|
+
data = yaml.safe_load(from_file.read_text())
|
|
133
|
+
|
|
134
|
+
# Import the validator
|
|
135
|
+
import sys
|
|
136
|
+
from pathlib import Path as P
|
|
137
|
+
platform_path = str(P(__file__).resolve().parent.parent.parent.parent.parent)
|
|
138
|
+
sys.path.insert(0, platform_path)
|
|
139
|
+
try:
|
|
140
|
+
from ontology import validate_ontology
|
|
141
|
+
errors = validate_ontology(data)
|
|
142
|
+
if errors:
|
|
143
|
+
console.print(f"[red]✗ Validation failed ({len(errors)} errors):[/red]")
|
|
144
|
+
for err in errors:
|
|
145
|
+
console.print(f" • {err}")
|
|
146
|
+
raise typer.Exit(1)
|
|
147
|
+
else:
|
|
148
|
+
entities = len(data.get("entities", {}))
|
|
149
|
+
actions = len(data.get("actions", {}))
|
|
150
|
+
console.print(f"[green]✓[/green] Valid ontology: {data.get('id', '?')} ({entities} entities, {actions} actions)")
|
|
151
|
+
except ImportError:
|
|
152
|
+
# Fallback: basic validation
|
|
153
|
+
if not data.get("id"):
|
|
154
|
+
console.print("[red]✗[/red] Missing 'id' field")
|
|
155
|
+
raise typer.Exit(1)
|
|
156
|
+
console.print(f"[green]✓[/green] Basic validation passed: {data.get('id', '?')}")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@app.command("publish")
|
|
160
|
+
def publish_ontology(ontology_id: str = typer.Argument(help="Ontology ID to publish")):
|
|
161
|
+
"""Publish an ontology (creates immutable version)."""
|
|
162
|
+
try:
|
|
163
|
+
data = gateway_post(f"/build/ontology/{ontology_id}/publish")
|
|
164
|
+
console.print(f"[green]✓[/green] Published: {ontology_id} → v{data.get('version', '?')}")
|
|
165
|
+
except APIError as e:
|
|
166
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
167
|
+
raise typer.Exit(1)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@app.command("versions")
|
|
171
|
+
def list_versions(ontology_id: str = typer.Argument(help="Ontology ID")):
|
|
172
|
+
"""List published versions of an ontology."""
|
|
173
|
+
try:
|
|
174
|
+
data = gateway_get(f"/build/ontology/{ontology_id}/versions")
|
|
175
|
+
except APIError as e:
|
|
176
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
177
|
+
raise typer.Exit(1)
|
|
178
|
+
|
|
179
|
+
versions = data.get("versions", [])
|
|
180
|
+
if not versions:
|
|
181
|
+
console.print("[dim]No published versions yet.[/dim]")
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
table = Table(title=f"Versions — {ontology_id}")
|
|
185
|
+
table.add_column("Version", style="bold")
|
|
186
|
+
table.add_column("Entities", justify="right")
|
|
187
|
+
table.add_column("Published")
|
|
188
|
+
|
|
189
|
+
for v in versions:
|
|
190
|
+
table.add_row(f"v{v.get('version', '?')}", str(v.get("entities", 0)), v.get("published_at", "—"))
|
|
191
|
+
|
|
192
|
+
console.print(table)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@app.command("delete")
|
|
196
|
+
def delete_ontology(
|
|
197
|
+
ontology_id: str = typer.Argument(help="Ontology ID to delete"),
|
|
198
|
+
confirm: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
|
|
199
|
+
):
|
|
200
|
+
"""Delete an ontology."""
|
|
201
|
+
if not confirm:
|
|
202
|
+
typer.confirm(f"Delete ontology '{ontology_id}'? This cannot be undone", abort=True)
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
gateway_delete(f"/build/ontology/{ontology_id}")
|
|
206
|
+
console.print(f"[green]✓[/green] Deleted: {ontology_id}")
|
|
207
|
+
except APIError as e:
|
|
208
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
209
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""prox platform — health, status, service management."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from ..api import gateway_get, APIError
|
|
7
|
+
from ..config import get_value
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Platform health and status.")
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@app.command("health")
|
|
14
|
+
def health():
|
|
15
|
+
"""Check platform health."""
|
|
16
|
+
gateway = get_value("gateway") or "not configured"
|
|
17
|
+
console.print(f"\n [bold]Platform Health[/bold] ({gateway})\n")
|
|
18
|
+
|
|
19
|
+
services = [
|
|
20
|
+
("Gateway", "/health"),
|
|
21
|
+
("Orchestrator", "/orchestrator/health"),
|
|
22
|
+
("Knowledge", "/knowledge/health"),
|
|
23
|
+
("Runtime", "/runtime/health"),
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
for name, path in services:
|
|
27
|
+
try:
|
|
28
|
+
data = gateway_get(path)
|
|
29
|
+
status = data.get("status", "ok")
|
|
30
|
+
console.print(f" [green]●[/green] {name:15} {status}")
|
|
31
|
+
except APIError:
|
|
32
|
+
console.print(f" [red]●[/red] {name:15} unreachable")
|
|
33
|
+
except Exception:
|
|
34
|
+
console.print(f" [red]●[/red] {name:15} error")
|
|
35
|
+
|
|
36
|
+
console.print()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@app.command("status")
|
|
40
|
+
def status():
|
|
41
|
+
"""Show platform status summary."""
|
|
42
|
+
try:
|
|
43
|
+
data = gateway_get("/health")
|
|
44
|
+
console.print(f"\n Gateway: [green]healthy[/green]")
|
|
45
|
+
console.print(f" Version: {data.get('version', '—')}")
|
|
46
|
+
console.print(f" Agents: {data.get('agents_registered', '—')}")
|
|
47
|
+
console.print(f" Uptime: {data.get('uptime', '—')}")
|
|
48
|
+
except APIError as e:
|
|
49
|
+
console.print(f" Gateway: [red]error[/red] ({e.detail[:100]})")
|
|
50
|
+
console.print()
|
prox/commands/routine.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""prox routine — manage scheduled agent execution."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from ..api import gateway_get, gateway_post, gateway_delete, APIError
|
|
9
|
+
|
|
10
|
+
app = typer.Typer(help="Manage routines (scheduled agent execution).")
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command("list")
|
|
15
|
+
def list_routines():
|
|
16
|
+
"""List routines."""
|
|
17
|
+
try:
|
|
18
|
+
data = gateway_get("/build/routines")
|
|
19
|
+
routines = data.get("routines", [])
|
|
20
|
+
if not routines:
|
|
21
|
+
console.print("[dim]No routines.[/dim]")
|
|
22
|
+
return
|
|
23
|
+
table = Table(title="Routines")
|
|
24
|
+
table.add_column("ID", style="bold")
|
|
25
|
+
table.add_column("Agent")
|
|
26
|
+
table.add_column("Trigger")
|
|
27
|
+
table.add_column("Status")
|
|
28
|
+
for r in routines:
|
|
29
|
+
trigger = r.get("trigger", {})
|
|
30
|
+
ttype = trigger.get("type", "manual") if isinstance(trigger, dict) else str(trigger)
|
|
31
|
+
st = r.get("status", "draft")
|
|
32
|
+
st_style = "green" if st == "active" else "dim"
|
|
33
|
+
table.add_row(r.get("id"), r.get("agent_id", ""), ttype, f"[{st_style}]{st}[/{st_style}]")
|
|
34
|
+
console.print(table)
|
|
35
|
+
except APIError as e:
|
|
36
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
37
|
+
raise typer.Exit(1)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@app.command("create")
|
|
41
|
+
def create_routine(
|
|
42
|
+
id: str = typer.Option(..., "--id"),
|
|
43
|
+
agent: str = typer.Option(..., "--agent", help="Agent ID"),
|
|
44
|
+
prompt: str = typer.Option(..., "--prompt", help="Prompt to send"),
|
|
45
|
+
cron: Optional[str] = typer.Option(None, "--cron", help="Cron schedule (e.g. '0 6 * * *')"),
|
|
46
|
+
):
|
|
47
|
+
"""Create a routine."""
|
|
48
|
+
payload = {"id": id, "agent_id": agent, "prompt": prompt, "status": "active"}
|
|
49
|
+
if cron:
|
|
50
|
+
payload["trigger"] = {"type": "cron", "schedule": cron}
|
|
51
|
+
else:
|
|
52
|
+
payload["trigger"] = {"type": "manual"}
|
|
53
|
+
try:
|
|
54
|
+
gateway_post("/build/routines", payload)
|
|
55
|
+
console.print(f"[green]✓[/green] Routine created: {id}")
|
|
56
|
+
except APIError as e:
|
|
57
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
58
|
+
raise typer.Exit(1)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@app.command("trigger")
|
|
62
|
+
def trigger_routine(routine_id: str = typer.Argument(help="Routine ID")):
|
|
63
|
+
"""Manually trigger a routine."""
|
|
64
|
+
try:
|
|
65
|
+
data = gateway_post(f"/build/routines/{routine_id}/trigger")
|
|
66
|
+
console.print(f"[green]✓[/green] Triggered: {routine_id}")
|
|
67
|
+
if data.get("run_id"):
|
|
68
|
+
console.print(f" Run ID: {data['run_id']}")
|
|
69
|
+
except APIError as e:
|
|
70
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
71
|
+
raise typer.Exit(1)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@app.command("delete")
|
|
75
|
+
def delete_routine(routine_id: str = typer.Argument(), confirm: bool = typer.Option(False, "--yes", "-y")):
|
|
76
|
+
"""Delete a routine."""
|
|
77
|
+
if not confirm:
|
|
78
|
+
typer.confirm(f"Delete routine '{routine_id}'?", abort=True)
|
|
79
|
+
try:
|
|
80
|
+
gateway_delete(f"/build/routines/{routine_id}")
|
|
81
|
+
console.print(f"[green]✓[/green] Deleted: {routine_id}")
|
|
82
|
+
except APIError as e:
|
|
83
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
84
|
+
raise typer.Exit(1)
|
prox/commands/secret.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""prox secret — manage platform secrets."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
import typer
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
from ..api import gateway_get, gateway_post, gateway_delete, APIError
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Manage platform secrets.")
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@app.command("list")
|
|
14
|
+
def list_secrets():
|
|
15
|
+
"""List secrets (names only, never values)."""
|
|
16
|
+
try:
|
|
17
|
+
data = gateway_get("/build/secrets")
|
|
18
|
+
secrets = data.get("secrets", [])
|
|
19
|
+
if not secrets:
|
|
20
|
+
console.print("[dim]No secrets.[/dim]")
|
|
21
|
+
return
|
|
22
|
+
for s in secrets:
|
|
23
|
+
console.print(f" • {s.get('name', s) if isinstance(s, dict) else s}")
|
|
24
|
+
except APIError as e:
|
|
25
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
26
|
+
raise typer.Exit(1)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.command("set")
|
|
30
|
+
def set_secret(
|
|
31
|
+
name: str = typer.Argument(help="Secret name"),
|
|
32
|
+
value: Optional[str] = typer.Option(None, "--value", "-v", help="Secret value"),
|
|
33
|
+
from_file: Optional[str] = typer.Option(None, "--from-file", help="Read value from file"),
|
|
34
|
+
):
|
|
35
|
+
"""Set a secret."""
|
|
36
|
+
if from_file:
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
value = Path(from_file).read_text().strip()
|
|
39
|
+
if not value:
|
|
40
|
+
value = typer.prompt("Secret value", hide_input=True)
|
|
41
|
+
try:
|
|
42
|
+
gateway_post("/build/secrets", {"name": name, "value": value})
|
|
43
|
+
console.print(f"[green]✓[/green] Secret set: {name}")
|
|
44
|
+
except APIError as e:
|
|
45
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
46
|
+
raise typer.Exit(1)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.command("delete")
|
|
50
|
+
def delete_secret(name: str = typer.Argument(), confirm: bool = typer.Option(False, "--yes", "-y")):
|
|
51
|
+
"""Delete a secret."""
|
|
52
|
+
if not confirm:
|
|
53
|
+
typer.confirm(f"Delete secret '{name}'?", abort=True)
|
|
54
|
+
try:
|
|
55
|
+
gateway_delete(f"/build/secrets/{name}")
|
|
56
|
+
console.print(f"[green]✓[/green] Deleted: {name}")
|
|
57
|
+
except APIError as e:
|
|
58
|
+
console.print(f"[red]Error:[/red] {e.detail}")
|
|
59
|
+
raise typer.Exit(1)
|