nexinal 1.0.2__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.
nexinal-1.0.2/PKG-INFO ADDED
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: nexinal
3
+ Version: 1.0.2
4
+ Summary: CLI client for Nexuss Bash remote execution API
5
+ Home-page: https://github.com/nexuss0781/Nexuss-Bash
6
+ Author: Nexuss
7
+ Author-email: Nexuss <nexuss@proton.me>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/nexuss0781/Nexuss-Bash
10
+ Project-URL: Bug Reports, https://github.com/nexuss0781/Nexuss-Bash/issues
11
+ Project-URL: Source, https://github.com/nexuss0781/Nexuss-Bash
12
+ Keywords: cli,bash,remote,execution,api,nexuss,nexinal
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: System :: Systems Administration
25
+ Classifier: Topic :: Software Development :: Build Tools
26
+ Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.8
28
+ Description-Content-Type: text/markdown
29
+ Requires-Dist: click>=8.0
30
+ Requires-Dist: requests>=2.28
31
+ Requires-Dist: pyyaml>=6.0
32
+ Requires-Dist: rich>=13.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: pytest>=7.0; extra == "dev"
35
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
36
+ Dynamic: author
37
+ Dynamic: home-page
38
+ Dynamic: requires-python
39
+
40
+ # nexinal
41
+
42
+ CLI client for the **Nexuss Bash** remote execution API. Run shell commands on a remote server directly from your terminal.
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install nexinal
48
+ ```
49
+
50
+ Or from source:
51
+
52
+ ```bash
53
+ git clone https://github.com/nexuss0781/Nexuss-Bash
54
+ cd Nexuss-Bash/cli
55
+ pip install -e .
56
+ ```
57
+
58
+ ## Quick Start
59
+
60
+ ```bash
61
+ # Authenticate
62
+ nexinal auth YOUR_API_TOKEN
63
+
64
+ # Run commands
65
+ nexinal run "echo hello" "whoami" "ls /workspace"
66
+
67
+ # Execute a YAML file
68
+ nexinal execute runbook.yaml
69
+
70
+ # Check status
71
+ nexinal status
72
+ ```
73
+
74
+ ## Commands
75
+
76
+ | Command | Description |
77
+ |---------|-------------|
78
+ | `nexinal auth <token>` | Save authentication token |
79
+ | `nexinal run <commands...>` | Run shell commands sequentially |
80
+ | `nexinal execute <yaml_file>` | Execute commands from a YAML file |
81
+ | `nexinal status` | Show connection and token info |
82
+ | `nexinal history` | List past runs |
83
+ | `nexinal health` | Quick health check |
84
+ | `nexinal sessions` | List active sessions |
85
+ | `nexinal packages list` | List installed packages |
86
+ | `nexinal packages install <name>` | Install a package |
87
+ | `nexinal config` | Show or set API URL |
88
+ | `nexinal logout` | Remove saved token |
89
+
90
+ ## YAML File Format
91
+
92
+ ### Simple format
93
+
94
+ ```yaml
95
+ commands:
96
+ - "echo hello"
97
+ - "whoami"
98
+ - "ls -la /workspace"
99
+ ```
100
+
101
+ ### Detailed format
102
+
103
+ ```yaml
104
+ commands:
105
+ - name: "Print hello"
106
+ command: "echo hello"
107
+ timeout: 30
108
+ stop_on_fail: true
109
+ - name: "List files"
110
+ command: "ls -la"
111
+ timeout: 15
112
+ ```
113
+
114
+ ## Configuration
115
+
116
+ Config is stored at `~/.nexinal/config.json`.
117
+
118
+ ```bash
119
+ # Set custom API URL
120
+ nexinal config set https://my-server.example.com
121
+
122
+ # Show current config
123
+ nexinal config
124
+ ```
125
+
126
+ ## Environment
127
+
128
+ - Python 3.8+
129
+ - Works on Linux, macOS, Windows
130
+
131
+ ## License
132
+
133
+ MIT
@@ -0,0 +1,94 @@
1
+ # nexinal
2
+
3
+ CLI client for the **Nexuss Bash** remote execution API. Run shell commands on a remote server directly from your terminal.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install nexinal
9
+ ```
10
+
11
+ Or from source:
12
+
13
+ ```bash
14
+ git clone https://github.com/nexuss0781/Nexuss-Bash
15
+ cd Nexuss-Bash/cli
16
+ pip install -e .
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Authenticate
23
+ nexinal auth YOUR_API_TOKEN
24
+
25
+ # Run commands
26
+ nexinal run "echo hello" "whoami" "ls /workspace"
27
+
28
+ # Execute a YAML file
29
+ nexinal execute runbook.yaml
30
+
31
+ # Check status
32
+ nexinal status
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ | Command | Description |
38
+ |---------|-------------|
39
+ | `nexinal auth <token>` | Save authentication token |
40
+ | `nexinal run <commands...>` | Run shell commands sequentially |
41
+ | `nexinal execute <yaml_file>` | Execute commands from a YAML file |
42
+ | `nexinal status` | Show connection and token info |
43
+ | `nexinal history` | List past runs |
44
+ | `nexinal health` | Quick health check |
45
+ | `nexinal sessions` | List active sessions |
46
+ | `nexinal packages list` | List installed packages |
47
+ | `nexinal packages install <name>` | Install a package |
48
+ | `nexinal config` | Show or set API URL |
49
+ | `nexinal logout` | Remove saved token |
50
+
51
+ ## YAML File Format
52
+
53
+ ### Simple format
54
+
55
+ ```yaml
56
+ commands:
57
+ - "echo hello"
58
+ - "whoami"
59
+ - "ls -la /workspace"
60
+ ```
61
+
62
+ ### Detailed format
63
+
64
+ ```yaml
65
+ commands:
66
+ - name: "Print hello"
67
+ command: "echo hello"
68
+ timeout: 30
69
+ stop_on_fail: true
70
+ - name: "List files"
71
+ command: "ls -la"
72
+ timeout: 15
73
+ ```
74
+
75
+ ## Configuration
76
+
77
+ Config is stored at `~/.nexinal/config.json`.
78
+
79
+ ```bash
80
+ # Set custom API URL
81
+ nexinal config set https://my-server.example.com
82
+
83
+ # Show current config
84
+ nexinal config
85
+ ```
86
+
87
+ ## Environment
88
+
89
+ - Python 3.8+
90
+ - Works on Linux, macOS, Windows
91
+
92
+ ## License
93
+
94
+ MIT
@@ -0,0 +1,4 @@
1
+ """nexinal - CLI client for Nexuss Bash remote execution API."""
2
+
3
+ __version__ = "1.0.2"
4
+ __app_name__ = "nexinal"
@@ -0,0 +1,6 @@
1
+ """Allow running nexinal as a module: python -m nexinal."""
2
+
3
+ from nexinal.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,149 @@
1
+ """API client for Nexuss Bash."""
2
+
3
+ import json
4
+ from typing import Optional, Any
5
+
6
+ import requests
7
+ from requests.exceptions import ConnectionError, HTTPError, RequestException, Timeout
8
+
9
+ from nexinal.config import get_api_url, get_token
10
+
11
+
12
+ class APIError(Exception):
13
+ def __init__(self, message: str, status_code: Optional[int] = None):
14
+ self.message = message
15
+ self.status_code = status_code
16
+ super().__init__(self.message)
17
+
18
+
19
+ class AuthError(APIError):
20
+ pass
21
+
22
+
23
+ class ConnectionError_(APIError):
24
+ pass
25
+
26
+
27
+ def _build_headers() -> dict:
28
+ headers = {"Content-Type": "application/json"}
29
+ token = get_token()
30
+ if token:
31
+ headers["Authorization"] = f"Bearer {token}"
32
+ return headers
33
+
34
+
35
+ def _build_url(path: str) -> str:
36
+ base = get_api_url().rstrip("/")
37
+ path = path.lstrip("/")
38
+ return f"{base}/{path}"
39
+
40
+
41
+ def api_get(path: str) -> dict:
42
+ url = _build_url(path)
43
+ try:
44
+ resp = requests.get(url, headers=_build_headers(), timeout=30)
45
+ resp.raise_for_status()
46
+ return resp.json()
47
+ except HTTPError as e:
48
+ status = e.response.status_code if e.response is not None else None
49
+ if status == 401:
50
+ raise AuthError("Authentication failed. Run: nexinal auth <token>", status)
51
+ if status == 404:
52
+ raise APIError(f"Not found: {path}", status)
53
+ raise APIError(f"HTTP {status}: {e}", status)
54
+ except ConnectionError:
55
+ raise ConnectionError_(f"Cannot connect to {url}. Check your network and API URL.")
56
+ except Timeout:
57
+ raise APIError(f"Request timed out: {url}")
58
+ except RequestException as e:
59
+ raise APIError(f"Request failed: {e}")
60
+ except json.JSONDecodeError:
61
+ raise APIError("Invalid JSON response from server")
62
+
63
+
64
+ def api_post(path: str, data: Optional[dict] = None) -> dict:
65
+ url = _build_url(path)
66
+ try:
67
+ resp = requests.post(url, headers=_build_headers(), json=data or {}, timeout=30)
68
+ resp.raise_for_status()
69
+ return resp.json()
70
+ except HTTPError as e:
71
+ status = e.response.status_code if e.response is not None else None
72
+ if status == 401:
73
+ raise AuthError("Authentication failed. Run: nexinal auth <token>", status)
74
+ if status == 404:
75
+ raise APIError(f"Not found: {path}", status)
76
+ body = ""
77
+ if e.response is not None:
78
+ try:
79
+ body = e.response.json().get("detail", e.response.text)
80
+ except Exception:
81
+ body = e.response.text
82
+ raise APIError(f"HTTP {status}: {body}", status)
83
+ except ConnectionError:
84
+ raise ConnectionError_(f"Cannot connect to {url}. Check your network and API URL.")
85
+ except Timeout:
86
+ raise APIError(f"Request timed out: {url}")
87
+ except RequestException as e:
88
+ raise APIError(f"Request failed: {e}")
89
+ except json.JSONDecodeError:
90
+ raise APIError("Invalid JSON response from server")
91
+
92
+
93
+ def api_post_file(path: str, file_path: str) -> dict:
94
+ url = _build_url(path)
95
+ token = get_token()
96
+ headers = {}
97
+ if token:
98
+ headers["Authorization"] = f"Bearer {token}"
99
+ try:
100
+ with open(file_path, "rb") as f:
101
+ files = {"file": (file_path.split("/")[-1], f)}
102
+ resp = requests.post(url, headers=headers, files=files, timeout=60)
103
+ resp.raise_for_status()
104
+ return resp.json()
105
+ except HTTPError as e:
106
+ status = e.response.status_code if e.response is not None else None
107
+ if status == 401:
108
+ raise AuthError("Authentication failed. Run: nexinal auth <token>", status)
109
+ raise APIError(f"HTTP {status}: {e}", status)
110
+ except ConnectionError:
111
+ raise ConnectionError_(f"Cannot connect to {url}. Check your network and API URL.")
112
+ except Timeout:
113
+ raise APIError(f"Request timed out: {url}")
114
+ except RequestException as e:
115
+ raise APIError(f"Request failed: {e}")
116
+ except FileNotFoundError:
117
+ raise APIError(f"File not found: {file_path}")
118
+ except json.JSONDecodeError:
119
+ raise APIError("Invalid JSON response from server")
120
+
121
+
122
+ def api_delete(path: str) -> dict:
123
+ url = _build_url(path)
124
+ try:
125
+ resp = requests.delete(url, headers=_build_headers(), timeout=30)
126
+ resp.raise_for_status()
127
+ return resp.json()
128
+ except HTTPError as e:
129
+ status = e.response.status_code if e.response is not None else None
130
+ if status == 401:
131
+ raise AuthError("Authentication failed. Run: nexinal auth <token>", status)
132
+ raise APIError(f"HTTP {status}: {e}", status)
133
+ except ConnectionError:
134
+ raise ConnectionError_(f"Cannot connect to {url}. Check your network and API URL.")
135
+ except Timeout:
136
+ raise APIError(f"Request timed out: {url}")
137
+ except RequestException as e:
138
+ raise APIError(f"Request failed: {e}")
139
+ except json.JSONDecodeError:
140
+ raise APIError("Invalid JSON response from server")
141
+
142
+
143
+ def test_connection() -> bool:
144
+ try:
145
+ url = _build_url("/health")
146
+ resp = requests.get(url, timeout=10)
147
+ return resp.status_code == 200
148
+ except Exception:
149
+ return False