trinity-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.
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: trinity-cli
3
+ Version: 0.1.0
4
+ Summary: CLI for the Trinity Autonomous Agent Orchestration Platform
5
+ Author-email: Ability AI <hello@ability.ai>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/abilityai/trinity
8
+ Project-URL: Documentation, https://github.com/abilityai/trinity/blob/main/docs/CLI.md
9
+ Project-URL: Repository, https://github.com/abilityai/trinity
10
+ Project-URL: Issues, https://github.com/abilityai/trinity/issues
11
+ Keywords: trinity,ai,agents,orchestration,cli
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Classifier: Topic :: System :: Systems Administration
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: click>=8.0
25
+ Requires-Dist: httpx>=0.24
26
+ Requires-Dist: rich>=13.0
27
+
28
+ # Trinity CLI
29
+
30
+ Command-line interface for the [Trinity](https://github.com/abilityai/trinity) Autonomous Agent Orchestration Platform.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ # With pip
36
+ pip install trinity-cli
37
+
38
+ # With pipx (recommended — isolated environment)
39
+ pipx install trinity-cli
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ```bash
45
+ # Connect to your Trinity instance
46
+ trinity init
47
+
48
+ # List your agents
49
+ trinity agents list
50
+
51
+ # Chat with an agent
52
+ trinity chat my-agent "Hello, what can you do?"
53
+
54
+ # Check fleet health
55
+ trinity health fleet
56
+ ```
57
+
58
+ ## Multi-Instance Profiles
59
+
60
+ Manage multiple Trinity instances (local dev, staging, production):
61
+
62
+ ```bash
63
+ # First instance (created during init)
64
+ trinity init
65
+
66
+ # Add another instance
67
+ trinity init --profile production
68
+
69
+ # Switch between instances
70
+ trinity profile use production
71
+ trinity profile list
72
+ ```
73
+
74
+ ## Commands
75
+
76
+ | Command | Description |
77
+ |---------|-------------|
78
+ | `trinity init` | Connect to a Trinity instance |
79
+ | `trinity login` | Re-authenticate with stored instance |
80
+ | `trinity agents list` | List all agents |
81
+ | `trinity agents create <name>` | Create a new agent |
82
+ | `trinity agents start <name>` | Start an agent |
83
+ | `trinity agents stop <name>` | Stop an agent |
84
+ | `trinity chat <agent> "msg"` | Chat with an agent |
85
+ | `trinity history <agent>` | View chat history |
86
+ | `trinity logs <agent>` | View agent logs |
87
+ | `trinity health fleet` | Fleet health overview |
88
+ | `trinity health agent <name>` | Single agent health |
89
+ | `trinity skills list` | Browse skill library |
90
+ | `trinity schedules list <agent>` | View agent schedules |
91
+ | `trinity profile list` | List configured profiles |
92
+ | `trinity profile use <name>` | Switch active profile |
93
+
94
+ ## Output Formats
95
+
96
+ ```bash
97
+ # JSON (default)
98
+ trinity agents list
99
+
100
+ # Table
101
+ trinity agents list --format table
102
+ ```
103
+
104
+ ## Environment Variables
105
+
106
+ | Variable | Description |
107
+ |----------|-------------|
108
+ | `TRINITY_URL` | Override instance URL |
109
+ | `TRINITY_API_KEY` | Override auth token |
110
+ | `TRINITY_PROFILE` | Override active profile |
111
+
112
+ ## Documentation
113
+
114
+ - [Full CLI docs](https://github.com/abilityai/trinity/blob/main/docs/CLI.md)
115
+ - [Trinity Platform](https://github.com/abilityai/trinity)
116
+
117
+ ## License
118
+
119
+ MIT
@@ -0,0 +1,92 @@
1
+ # Trinity CLI
2
+
3
+ Command-line interface for the [Trinity](https://github.com/abilityai/trinity) Autonomous Agent Orchestration Platform.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ # With pip
9
+ pip install trinity-cli
10
+
11
+ # With pipx (recommended — isolated environment)
12
+ pipx install trinity-cli
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```bash
18
+ # Connect to your Trinity instance
19
+ trinity init
20
+
21
+ # List your agents
22
+ trinity agents list
23
+
24
+ # Chat with an agent
25
+ trinity chat my-agent "Hello, what can you do?"
26
+
27
+ # Check fleet health
28
+ trinity health fleet
29
+ ```
30
+
31
+ ## Multi-Instance Profiles
32
+
33
+ Manage multiple Trinity instances (local dev, staging, production):
34
+
35
+ ```bash
36
+ # First instance (created during init)
37
+ trinity init
38
+
39
+ # Add another instance
40
+ trinity init --profile production
41
+
42
+ # Switch between instances
43
+ trinity profile use production
44
+ trinity profile list
45
+ ```
46
+
47
+ ## Commands
48
+
49
+ | Command | Description |
50
+ |---------|-------------|
51
+ | `trinity init` | Connect to a Trinity instance |
52
+ | `trinity login` | Re-authenticate with stored instance |
53
+ | `trinity agents list` | List all agents |
54
+ | `trinity agents create <name>` | Create a new agent |
55
+ | `trinity agents start <name>` | Start an agent |
56
+ | `trinity agents stop <name>` | Stop an agent |
57
+ | `trinity chat <agent> "msg"` | Chat with an agent |
58
+ | `trinity history <agent>` | View chat history |
59
+ | `trinity logs <agent>` | View agent logs |
60
+ | `trinity health fleet` | Fleet health overview |
61
+ | `trinity health agent <name>` | Single agent health |
62
+ | `trinity skills list` | Browse skill library |
63
+ | `trinity schedules list <agent>` | View agent schedules |
64
+ | `trinity profile list` | List configured profiles |
65
+ | `trinity profile use <name>` | Switch active profile |
66
+
67
+ ## Output Formats
68
+
69
+ ```bash
70
+ # JSON (default)
71
+ trinity agents list
72
+
73
+ # Table
74
+ trinity agents list --format table
75
+ ```
76
+
77
+ ## Environment Variables
78
+
79
+ | Variable | Description |
80
+ |----------|-------------|
81
+ | `TRINITY_URL` | Override instance URL |
82
+ | `TRINITY_API_KEY` | Override auth token |
83
+ | `TRINITY_PROFILE` | Override active profile |
84
+
85
+ ## Documentation
86
+
87
+ - [Full CLI docs](https://github.com/abilityai/trinity/blob/main/docs/CLI.md)
88
+ - [Trinity Platform](https://github.com/abilityai/trinity)
89
+
90
+ ## License
91
+
92
+ MIT
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "trinity-cli"
7
+ version = "0.1.0"
8
+ description = "CLI for the Trinity Autonomous Agent Orchestration Platform"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ {name = "Ability AI", email = "hello@ability.ai"},
14
+ ]
15
+ keywords = ["trinity", "ai", "agents", "orchestration", "cli"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Software Development :: Libraries",
26
+ "Topic :: System :: Systems Administration",
27
+ ]
28
+ dependencies = [
29
+ "click>=8.0",
30
+ "httpx>=0.24",
31
+ "rich>=13.0",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/abilityai/trinity"
36
+ Documentation = "https://github.com/abilityai/trinity/blob/main/docs/CLI.md"
37
+ Repository = "https://github.com/abilityai/trinity"
38
+ Issues = "https://github.com/abilityai/trinity/issues"
39
+
40
+ [project.scripts]
41
+ trinity = "trinity_cli.main:cli"
42
+
43
+ [tool.setuptools.packages.find]
44
+ include = ["trinity_cli*"]
45
+
46
+ [tool.setuptools.package-data]
47
+ trinity_cli = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Trinity CLI — command-line interface for the Trinity Agent Platform."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,107 @@
1
+ """HTTP client for the Trinity Backend API."""
2
+
3
+ import sys
4
+ from typing import Any, Optional
5
+
6
+ import click
7
+ import httpx
8
+
9
+ from .config import get_api_key, get_instance_url
10
+
11
+
12
+ def _profile_from_context() -> Optional[str]:
13
+ """Try to get the --profile value from the current Click context."""
14
+ try:
15
+ ctx = click.get_current_context(silent=True)
16
+ if ctx:
17
+ root = ctx.find_root()
18
+ if root.obj and "profile" in root.obj:
19
+ return root.obj["profile"]
20
+ except RuntimeError:
21
+ pass
22
+ return None
23
+
24
+
25
+ class TrinityAPIError(Exception):
26
+ def __init__(self, status_code: int, detail: str):
27
+ self.status_code = status_code
28
+ self.detail = detail
29
+ super().__init__(f"HTTP {status_code}: {detail}")
30
+
31
+
32
+ class TrinityClient:
33
+ """Thin HTTP wrapper around the Trinity FastAPI backend."""
34
+
35
+ def __init__(self, base_url: Optional[str] = None, token: Optional[str] = None,
36
+ profile: Optional[str] = None):
37
+ resolved_profile = profile or _profile_from_context()
38
+ self.base_url = base_url or get_instance_url(resolved_profile)
39
+ self.token = token or get_api_key(resolved_profile)
40
+ if not self.base_url:
41
+ print("Error: No Trinity instance configured. Run 'trinity init' or 'trinity login' first.", file=sys.stderr)
42
+ sys.exit(1)
43
+
44
+ def _headers(self) -> dict:
45
+ h = {"Content-Type": "application/json"}
46
+ if self.token:
47
+ h["Authorization"] = f"Bearer {self.token}"
48
+ return h
49
+
50
+ def _handle_response(self, resp: httpx.Response) -> Any:
51
+ if resp.status_code == 401:
52
+ print("Error: Authentication failed. Run 'trinity login' to re-authenticate.", file=sys.stderr)
53
+ sys.exit(1)
54
+ if resp.status_code >= 400:
55
+ try:
56
+ detail = resp.json().get("detail", resp.text)
57
+ except Exception:
58
+ detail = resp.text
59
+ raise TrinityAPIError(resp.status_code, str(detail))
60
+ if resp.status_code == 204:
61
+ return None
62
+ return resp.json()
63
+
64
+ def get(self, path: str, params: Optional[dict] = None) -> Any:
65
+ with httpx.Client(timeout=30) as c:
66
+ resp = c.get(f"{self.base_url}{path}", headers=self._headers(), params=params)
67
+ return self._handle_response(resp)
68
+
69
+ def post(self, path: str, json: Optional[dict] = None) -> Any:
70
+ with httpx.Client(timeout=60) as c:
71
+ resp = c.post(f"{self.base_url}{path}", headers=self._headers(), json=json)
72
+ return self._handle_response(resp)
73
+
74
+ def put(self, path: str, json: Optional[dict] = None) -> Any:
75
+ with httpx.Client(timeout=30) as c:
76
+ resp = c.put(f"{self.base_url}{path}", headers=self._headers(), json=json)
77
+ return self._handle_response(resp)
78
+
79
+ def delete(self, path: str) -> Any:
80
+ with httpx.Client(timeout=30) as c:
81
+ resp = c.delete(f"{self.base_url}{path}", headers=self._headers())
82
+ return self._handle_response(resp)
83
+
84
+ def post_form(self, path: str, data: dict) -> Any:
85
+ """POST with form-encoded body (for OAuth2 token endpoint)."""
86
+ with httpx.Client(timeout=30) as c:
87
+ headers = {}
88
+ if self.token:
89
+ headers["Authorization"] = f"Bearer {self.token}"
90
+ resp = c.post(f"{self.base_url}{path}", data=data, headers=headers)
91
+ return self._handle_response(resp)
92
+
93
+ def post_unauthenticated(self, path: str, json: Optional[dict] = None) -> Any:
94
+ """POST without auth header (for login/registration flows)."""
95
+ with httpx.Client(timeout=30) as c:
96
+ resp = c.post(
97
+ f"{self.base_url}{path}",
98
+ headers={"Content-Type": "application/json"},
99
+ json=json,
100
+ )
101
+ return self._handle_response(resp)
102
+
103
+ def get_unauthenticated(self, path: str) -> Any:
104
+ """GET without auth header."""
105
+ with httpx.Client(timeout=30) as c:
106
+ resp = c.get(f"{self.base_url}{path}")
107
+ return self._handle_response(resp)
File without changes
@@ -0,0 +1,96 @@
1
+ """Agent management commands."""
2
+
3
+ import click
4
+
5
+ from ..client import TrinityClient, TrinityAPIError
6
+ from ..output import format_output
7
+
8
+
9
+ @click.group()
10
+ def agents():
11
+ """Manage agents."""
12
+ pass
13
+
14
+
15
+ @agents.command("list")
16
+ @click.option("--format", "fmt", type=click.Choice(["json", "table"]), default="json", help="Output format")
17
+ def list_agents(fmt):
18
+ """List all agents."""
19
+ client = TrinityClient()
20
+ data = client.get("/api/agents")
21
+ if fmt == "table" and isinstance(data, list):
22
+ # Slim down for table view
23
+ rows = [
24
+ {
25
+ "name": a.get("name", ""),
26
+ "status": a.get("status", ""),
27
+ "template": a.get("template", ""),
28
+ "type": a.get("type", ""),
29
+ }
30
+ for a in data
31
+ ]
32
+ format_output(rows, fmt)
33
+ else:
34
+ format_output(data, fmt)
35
+
36
+
37
+ @agents.command("get")
38
+ @click.argument("name")
39
+ @click.option("--format", "fmt", type=click.Choice(["json", "table"]), default="json", help="Output format")
40
+ def get_agent(name, fmt):
41
+ """Get agent details."""
42
+ client = TrinityClient()
43
+ data = client.get(f"/api/agents/{name}")
44
+ format_output(data, fmt)
45
+
46
+
47
+ @agents.command("create")
48
+ @click.argument("name")
49
+ @click.option("--template", default=None, help="Template (e.g. github:Org/repo)")
50
+ @click.option("--format", "fmt", type=click.Choice(["json", "table"]), default="json", help="Output format")
51
+ def create_agent(name, template, fmt):
52
+ """Create a new agent."""
53
+ client = TrinityClient()
54
+ payload = {"name": name}
55
+ if template:
56
+ payload["template"] = template
57
+ data = client.post("/api/agents", json=payload)
58
+ format_output(data, fmt)
59
+
60
+
61
+ @agents.command("delete")
62
+ @click.argument("name")
63
+ @click.confirmation_option(prompt="Are you sure you want to delete this agent?")
64
+ def delete_agent(name):
65
+ """Delete an agent."""
66
+ client = TrinityClient()
67
+ client.delete(f"/api/agents/{name}")
68
+ click.echo(f"Deleted agent '{name}'")
69
+
70
+
71
+ @agents.command("start")
72
+ @click.argument("name")
73
+ def start_agent(name):
74
+ """Start an agent container."""
75
+ client = TrinityClient()
76
+ client.post(f"/api/agents/{name}/start")
77
+ click.echo(f"Started agent '{name}'")
78
+
79
+
80
+ @agents.command("stop")
81
+ @click.argument("name")
82
+ def stop_agent(name):
83
+ """Stop an agent container."""
84
+ client = TrinityClient()
85
+ client.post(f"/api/agents/{name}/stop")
86
+ click.echo(f"Stopped agent '{name}'")
87
+
88
+
89
+ @agents.command("rename")
90
+ @click.argument("name")
91
+ @click.argument("new_name")
92
+ def rename_agent(name, new_name):
93
+ """Rename an agent."""
94
+ client = TrinityClient()
95
+ client.put(f"/api/agents/{name}/rename", json={"new_name": new_name})
96
+ click.echo(f"Renamed '{name}' -> '{new_name}'")
@@ -0,0 +1,177 @@
1
+ """Authentication commands: login, logout, status, init."""
2
+
3
+ import click
4
+
5
+ from ..client import TrinityClient, TrinityAPIError
6
+ from ..config import (
7
+ clear_auth, get_instance_url, get_user, load_config,
8
+ profile_name_from_url, set_auth, _resolve_profile_name,
9
+ )
10
+
11
+
12
+ def _get_profile_name(ctx: click.Context) -> str | None:
13
+ """Extract the --profile value from the root context."""
14
+ root = ctx.find_root()
15
+ return root.obj.get("profile") if root.obj else None
16
+
17
+
18
+ @click.command()
19
+ @click.option("--instance", help="Trinity instance URL (e.g. https://trinity.example.com)")
20
+ @click.option("--profile", "profile_opt", default=None,
21
+ help="Profile name to store credentials under (default: hostname)")
22
+ @click.pass_context
23
+ def login(ctx, instance, profile_opt):
24
+ """Log in to a Trinity instance with email verification."""
25
+ profile_name = profile_opt or _get_profile_name(ctx)
26
+ url = instance or get_instance_url(profile_name)
27
+ if not url:
28
+ url = click.prompt("Trinity instance URL")
29
+ url = url.rstrip("/")
30
+
31
+ client = TrinityClient(base_url=url, token="none")
32
+
33
+ email = click.prompt("Email")
34
+
35
+ # Request verification code
36
+ try:
37
+ client.post_unauthenticated("/api/auth/email/request", {"email": email})
38
+ except TrinityAPIError as e:
39
+ click.echo(f"Error requesting code: {e.detail}", err=True)
40
+ raise SystemExit(1)
41
+
42
+ click.echo(f"Verification code sent to {email}")
43
+ code = click.prompt("Enter 6-digit code")
44
+
45
+ # Verify code and get token
46
+ try:
47
+ result = client.post_unauthenticated("/api/auth/email/verify", {
48
+ "email": email,
49
+ "code": code,
50
+ })
51
+ except TrinityAPIError as e:
52
+ click.echo(f"Verification failed: {e.detail}", err=True)
53
+ raise SystemExit(1)
54
+
55
+ token = result["access_token"]
56
+ user = result.get("user")
57
+
58
+ # Determine profile name: explicit > global flag > derive from URL
59
+ target_profile = profile_name or profile_name_from_url(url)
60
+ set_auth(url, token, user, profile_name=target_profile)
61
+ name = user.get("name") or user.get("email") or user.get("username") if user else email
62
+ click.echo(f"Logged in as {name} [profile: {target_profile}]")
63
+
64
+
65
+ @click.command()
66
+ @click.pass_context
67
+ def logout(ctx):
68
+ """Clear stored credentials for the current profile."""
69
+ profile_name = _get_profile_name(ctx)
70
+ clear_auth(profile_name)
71
+ resolved = _resolve_profile_name(profile_name)
72
+ click.echo(f"Logged out [profile: {resolved}]")
73
+
74
+
75
+ @click.command()
76
+ @click.pass_context
77
+ def status(ctx):
78
+ """Show current login status and instance info."""
79
+ profile_name = _get_profile_name(ctx)
80
+ resolved = _resolve_profile_name(profile_name)
81
+ url = get_instance_url(profile_name)
82
+
83
+ click.echo(f"Profile: {resolved}")
84
+
85
+ if not url:
86
+ click.echo("Instance: Not configured. Run 'trinity init' or 'trinity login'.")
87
+ return
88
+
89
+ user = get_user(profile_name)
90
+ config = load_config()
91
+ profile_data = config.get("profiles", {}).get(resolved, {})
92
+
93
+ click.echo(f"Instance: {url}")
94
+ if user:
95
+ click.echo(f"User: {user.get('email') or user.get('username')}")
96
+ click.echo(f"Role: {user.get('role', 'unknown')}")
97
+ elif profile_data.get("token"):
98
+ click.echo("User: (API key auth)")
99
+ else:
100
+ click.echo("User: Not logged in")
101
+
102
+ # Check connectivity
103
+ try:
104
+ client = TrinityClient(base_url=url, token=profile_data.get("token", "none"))
105
+ client.get_unauthenticated("/api/auth/mode")
106
+ click.echo("Status: Connected")
107
+ except Exception:
108
+ click.echo("Status: Unreachable")
109
+
110
+
111
+ @click.command()
112
+ @click.option("--profile", "profile_opt", default=None,
113
+ help="Profile name (default: derived from instance hostname)")
114
+ @click.pass_context
115
+ def init(ctx, profile_opt):
116
+ """Set up Trinity CLI: configure instance, request access, and log in.
117
+
118
+ One command to go from zero to authenticated. Creates a named profile
119
+ for the instance (defaults to hostname).
120
+ """
121
+ url = click.prompt("Trinity instance URL", default="http://localhost:8000")
122
+ url = url.rstrip("/")
123
+
124
+ client = TrinityClient(base_url=url, token="none")
125
+
126
+ # Verify instance is reachable
127
+ try:
128
+ client.get_unauthenticated("/api/auth/mode")
129
+ except Exception:
130
+ click.echo(f"Cannot reach {url}. Check the URL and try again.", err=True)
131
+ raise SystemExit(1)
132
+
133
+ click.echo(f"Connected to {url}")
134
+
135
+ # Determine profile name
136
+ profile_name = profile_opt or _get_profile_name(ctx) or profile_name_from_url(url)
137
+
138
+ email = click.prompt("Email")
139
+
140
+ # Request access (auto-approve endpoint)
141
+ try:
142
+ client.post_unauthenticated("/api/access/request", {"email": email})
143
+ click.echo("Access granted")
144
+ except TrinityAPIError as e:
145
+ if e.status_code == 409:
146
+ click.echo("Already registered")
147
+ else:
148
+ click.echo(f"Access request failed: {e.detail}", err=True)
149
+ raise SystemExit(1)
150
+
151
+ # Send verification code
152
+ try:
153
+ client.post_unauthenticated("/api/auth/email/request", {"email": email})
154
+ except TrinityAPIError as e:
155
+ click.echo(f"Error requesting code: {e.detail}", err=True)
156
+ raise SystemExit(1)
157
+
158
+ click.echo(f"Verification code sent to {email}")
159
+ code = click.prompt("Enter 6-digit code")
160
+
161
+ # Verify and get token
162
+ try:
163
+ result = client.post_unauthenticated("/api/auth/email/verify", {
164
+ "email": email,
165
+ "code": code,
166
+ })
167
+ except TrinityAPIError as e:
168
+ click.echo(f"Verification failed: {e.detail}", err=True)
169
+ raise SystemExit(1)
170
+
171
+ token = result["access_token"]
172
+ user = result.get("user")
173
+
174
+ set_auth(url, token, user, profile_name=profile_name)
175
+ name = user.get("name") or user.get("email") or user.get("username") if user else email
176
+ click.echo(f"Logged in as {name} [profile: {profile_name}]")
177
+ click.echo(f"\nTrinity CLI is ready. Try 'trinity agents list'.")