flyshell 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.
@@ -0,0 +1,90 @@
1
+ # src\flyshell\core\loader.py
2
+
3
+ from flyshell.abstract.base_plugin import BasePlugin
4
+ from flyshell.core import data
5
+ from flyshell.core.directory import COMMAND_REGISTRY
6
+
7
+ from pathlib import Path
8
+ import importlib.util
9
+ import inspect
10
+ import os
11
+ import sys
12
+
13
+ RESERVED_NAMES = set(COMMAND_REGISTRY.keys())
14
+
15
+ def scan_plugins(plugin_folder=None):
16
+ folder = Path(plugin_folder) if plugin_folder else data.USER_PLUGIN_DIR
17
+ data.PLUGINS.clear()
18
+ if not folder.exists():
19
+ folder.mkdir(parents=True, exist_ok=True)
20
+ print("\nNo plugins found.")
21
+ return
22
+ folder_str = str(folder)
23
+ if folder_str not in sys.path:
24
+ sys.path.insert(0, folder_str)
25
+ candidates = {}
26
+ for item in folder.iterdir():
27
+ file_path = None
28
+ plugin_name = None
29
+ if item.is_file() and item.suffix == ".py" and not item.name.startswith("_"):
30
+ file_path = item
31
+ plugin_name = item.stem
32
+ elif item.is_dir() and not item.name.startswith((".", "_")):
33
+ entry = item / "plugin.py"
34
+ if entry.exists():
35
+ file_path = entry
36
+ plugin_name = item.name
37
+ if not file_path:
38
+ continue
39
+ try:
40
+ spec = importlib.util.spec_from_file_location(plugin_name, file_path)
41
+ module = importlib.util.module_from_spec(spec)
42
+ spec.loader.exec_module(module)
43
+ for _, obj in inspect.getmembers(module, inspect.isclass):
44
+ if issubclass(obj, BasePlugin) and obj is not BasePlugin:
45
+ ctx = build_context(plugin_name, file_path)
46
+ instance = obj(ctx)
47
+ candidates.setdefault(plugin_name, []).append((instance, file_path))
48
+ break
49
+ except Exception as e:
50
+ print(f"\n⚠️ Plugin Failure - Failed to inspect '{plugin_name}'")
51
+ print(f"Exception code: {e}, Flyshell will still launch.")
52
+ loaded_count = 0
53
+ first_print = True
54
+ for name, entries in candidates.items():
55
+ failed = False
56
+ if name in RESERVED_NAMES:
57
+ print(f"\n⚠️ Collision Error - Plugin '{name}' conflicts with a built-in Flyshell command and will not be loaded.")
58
+ failed = True
59
+ if len(entries) > 1:
60
+ paths = "\n - ".join(str(path) for _, path in entries)
61
+ print(f"\n⚠️ Collision Error - Multiple plugins named '{name}' detected: {paths}")
62
+ print(f"The above plugins will not be loaded. Please rename them to unique names.")
63
+ failed = True
64
+ if failed:
65
+ continue
66
+ instance, _ = entries[0]
67
+ data.PLUGINS[name] = instance
68
+ if first_print:
69
+ print("\nInstalled Plugins:")
70
+ first_print = False
71
+ print(f"✅ - {name}")
72
+ loaded_count += 1
73
+ if loaded_count > 0:
74
+ print(f"\nTotal of {loaded_count} plugin(s) found.")
75
+ else:
76
+ print("\nNo plugins loaded.")
77
+
78
+ def build_context(plugin_name, file_path=None):
79
+ plugin_storage = data.get_all_plugin_data(plugin_name)
80
+ context = {
81
+ "plugin_name": plugin_name,
82
+ "file_path": str(file_path) if file_path else None,
83
+ "metadata": {
84
+ "host_os": data.HOST_OS,
85
+ "version": data.VERSION,
86
+ "build": data.BUILD
87
+ },
88
+ "storage": plugin_storage
89
+ }
90
+ return context
flyshell/main.py ADDED
@@ -0,0 +1,115 @@
1
+ # src\flyshell\main.py
2
+
3
+ from pathlib import Path
4
+ import os
5
+ import sys
6
+
7
+ PACKAGE_ROOT = Path(__file__).resolve().parent
8
+ SRC_DIR = PACKAGE_ROOT.parent
9
+ PROJECT_ROOT = SRC_DIR.parent
10
+
11
+ if str(SRC_DIR) not in sys.path:
12
+ sys.path.insert(0, str(SRC_DIR))
13
+
14
+ def boot_check():
15
+ print("\nFlyshell will now check that your modules exist.")
16
+ abstract_dir = PACKAGE_ROOT / "abstract"
17
+ builtin_dir = PACKAGE_ROOT / "builtin"
18
+ core_dir = PACKAGE_ROOT / "core"
19
+ REQUIRED_COMPONENTS = [
20
+ abstract_dir,
21
+ abstract_dir / "base_cmd.py",
22
+ abstract_dir / "base_plugin.py",
23
+ abstract_dir / "parent_cmd.py",
24
+ builtin_dir,
25
+ builtin_dir / "fs.py",
26
+ builtin_dir / "system.py",
27
+ builtin_dir / "utils.py",
28
+ core_dir,
29
+ core_dir / "auth.py",
30
+ core_dir / "console.py",
31
+ core_dir / "data.py",
32
+ core_dir / "directory.py",
33
+ core_dir / "loader.py",
34
+ ]
35
+ present = []
36
+ missing = []
37
+ for path in REQUIRED_COMPONENTS:
38
+ if path.exists():
39
+ present.append(str(path))
40
+ else:
41
+ missing.append(str(path))
42
+ if missing:
43
+ print("\nCRITICAL BOOT ERROR: Critical system module(s) could not be found.")
44
+ for i in present:
45
+ print(f"✅ {i} - File found")
46
+ for i in missing:
47
+ print(f"❌ {i} - FILE NOT FOUND")
48
+ if missing:
49
+ return False
50
+ tests_dir = PROJECT_ROOT / "tests"
51
+ if tests_dir.exists():
52
+ print("\nFlyshell will now check internal additional packages and features. These are not required.")
53
+ for path in tests_dir.glob("test_*.py"):
54
+ print(f"✅ {path.name} - Test found")
55
+ try:
56
+ import pytest
57
+ print(f"✅ Your tests can be run using Pytest.")
58
+ except ImportError:
59
+ print(f"⚠️ Your tests cannot be run as Pytest is not installed.")
60
+ print("\nFlyshell will now check external additional packages and features. These are not required.")
61
+ from flyshell.core import console
62
+ if console.check_readline():
63
+ print("✅ Terminal Readline - Available (Use arrow keys to navigate input history)")
64
+ else:
65
+ print("⚠️ Terminal Readline - Unavailable (This does not affect your performance)")
66
+ return True
67
+
68
+ def main():
69
+ os.system("")
70
+ print("\033[H\033[2J", end="")
71
+ print("\nLaunch process initiated. Flyshell is checking it is able to launch...")
72
+ try:
73
+ if boot_check():
74
+ from flyshell.core import auth, console, data, loader
75
+ if data.HOST_OS in ["Windows", "Darwin", "Linux"]:
76
+ if sys.version_info < (3, 10):
77
+ print("\nCRITICAL ERROR: Your Python version is too old to be supported by Flyshell.")
78
+ print("Flyshell cannot launch because it requires Python 3.10 or newer.")
79
+ sys.exit(1)
80
+ print(f"\nYour OS '{data.HOST_OS}' is compatible with Flyshell.")
81
+ print("Flyshell will now boot.")
82
+ loader.scan_plugins()
83
+ if auth.login_flow():
84
+ console.boot()
85
+ else:
86
+ print("Shutting down...\n")
87
+ sys.exit(0)
88
+ else:
89
+ print(f"\nCRITICAL ERROR: Host operating system '{data.HOST_OS}' is not supported.")
90
+ print("Flyshell cannot launch because your operating system is not supported.")
91
+ sys.exit(1)
92
+ else:
93
+ print("\nFlyshell cannot launch until the above file paths are restored.")
94
+ sys.exit(1)
95
+ except KeyboardInterrupt:
96
+ print("\n\nFlyshell session terminated, shutting down...\n")
97
+ sys.exit(0)
98
+ except Exception as e:
99
+ print("\n❌❌❌ Oops! An unexpected error occurred.")
100
+ print(f"Error details: {e}")
101
+ print("We don't really know what happened there. Sorry about that.\n")
102
+ print("Print traceback message? Default is N.")
103
+ choice = input("[y/n]?: ")
104
+ if choice.lower() == "y":
105
+ import traceback
106
+ raw = traceback.extract_tb(e.__traceback__)
107
+ last_frame = raw[-1]
108
+ print("\nTraceback details:")
109
+ print(f"File: '{last_frame[0]}'")
110
+ print(f"Line: {last_frame[1]}")
111
+ print(f"Exception message: '{e}'")
112
+ sys.exit(1)
113
+
114
+ if __name__ == "__main__":
115
+ main()
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: flyshell
3
+ Version: 1.0.0
4
+ Summary: A modular, zero-dependency command-line environment in Python.
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest; extra == "dev"
11
+ Provides-Extra: readline
12
+ Requires-Dist: pyreadline3; sys_platform == "win32" and extra == "readline"
13
+ Dynamic: license-file
14
+
15
+ # Flyshell
16
+
17
+ ![Python Version](https://img.shields.io/badge/python-3.10%2B-blue.svg)
18
+ ![Licence](https://img.shields.io/badge/license-MIT-green.svg)
19
+ ![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey.svg)
20
+ ![Dependencies](https://img.shields.io/badge/dependencies-zero-brightgreen.svg)
21
+ ![Tests](https://github.com/kianodev/flyshell/actions/workflows/pytest.yml/badge.svg)
22
+
23
+ > Documentation last updated: v1.0.0
24
+
25
+ A modular, zero-dependency command-line environment and extensible runtime interface built in Python.
26
+
27
+ Flyshell provides a modular, extensible shell environment featuring dynamic runtime plugin loading, session persistence, secure authentication, and cross-platform process isolation, using only Python standard libraries.
28
+
29
+ No extra packages required, just install and enjoy!
30
+
31
+ ## Key Features
32
+
33
+ 1. **Zero External Dependencies** - Flyshell is built entirely with native Python modules with absolutely zero third-party packages required to enjoy full functionality.
34
+ 2. **Dynamic Plugin Architecture**: Flyshell employs a dynamic plugin architecture that automatically scans for plugins in the User Directory following the standard defined in `src/flyshell/abstract/base_plugin.py`.
35
+ 3. **Secure Authentication**: Flyshell uses salted PBKDF2 HMAC (SHA-256, 600,000 iterations) with constant-time equality comparisons to prevent timing attacks on password information.
36
+ 4. **Cross-Platform Support**: Flyshell is fully supported by Windows (NT), macOS (Darwin) and Linux installations with native OS management and POSIX-aware parsing. Note that Flyshell is **not** supported by any other systems.
37
+ 5. **State and History Persistence**: Flyshell has a fully encapsulated SQL storage system using SQLite3 providing session metrics and ISO 8601 UTC audit logging.
38
+
39
+ ## System Requirements
40
+
41
+ 1. **Python**: Your Python installation or IDE must support **Python 3.10 or newer** as Flyshell utilises structural pattern matching features. Older Python installations will be rejected by Flyshell on boot or could crash the system.
42
+ 2. **Operating System**: Your operating system must be **Windows 10/11, macOS (Darwin) or Linux**. Any other operating system will be rejected by Flyshell on boot.
43
+
44
+ ## System Architecture
45
+
46
+ ```
47
+ flyshell/
48
+ ├── .github/
49
+ │ └── workflows/
50
+ │ └── pytest.yml
51
+ ├── src/
52
+ │ └── flyshell/
53
+ │ ├── abstract/
54
+ │ │ ├── __init__.py
55
+ │ │ ├── base_cmd.py
56
+ │ │ ├── base_plugin.py
57
+ │ │ └── parent_cmd.py
58
+ │ ├── builtin/
59
+ │ │ ├── __init__.py
60
+ │ │ ├── fs.py
61
+ │ │ ├── system.py
62
+ │ │ └── utils.py
63
+ │ ├── core/
64
+ │ │ ├── __init__.py
65
+ │ │ ├── auth.py
66
+ │ │ ├── console.py
67
+ │ │ ├── data.py
68
+ │ │ ├── directory.py
69
+ │ │ └── loader.py
70
+ │ ├── __init__.py
71
+ │ └── main.py
72
+ ├── tests/
73
+ │ ├── test_auth.py
74
+ │ ├── test_data.py
75
+ │ ├── test_directory.py
76
+ │ ├── test_loader.py
77
+ │ └── test_security.py
78
+ ├── .gitignore
79
+ ├── LICENSE
80
+ ├── pyproject.toml
81
+ └── README.md
82
+ ```
83
+
84
+ ## Installing Flyshell
85
+
86
+ There are two methods to install Flyshell.
87
+
88
+ ### Method 1: Install via `pip` (Best for General Use)
89
+
90
+ Install Flyshell through `pip`:
91
+ ```bash
92
+ pip install flyshell
93
+ ```
94
+
95
+ Then, you can simply run Flyshell:
96
+ ```bash
97
+ flyshell
98
+ ```
99
+
100
+ ### Method 2: Install from source via `git` (Best for Development & Custom Plugins)
101
+
102
+ Clone the GitHub repository:
103
+ ```bash
104
+ git clone https://github.com/kianodev/flyshell.git
105
+ ```
106
+
107
+ Switch to the Flyshell directory and install in editable mode:
108
+ ```bash
109
+ cd flyshell
110
+ pip install -e .
111
+ ```
112
+
113
+ ### Adding optional dependencies
114
+
115
+ To install Readline (on Windows):
116
+ ```bash
117
+ pip install -e .[readline]
118
+ ```
119
+
120
+ To install the development and testing suite (including Pytest):
121
+ ```bash
122
+ pip install -e .[dev]
123
+ ```
124
+
125
+ ## Exploring Flyshell
126
+
127
+ Once you have installed Flyshell using either of the methods above, you will be prompted to initialise your user profile. Once this is complete, you will be greeted with a prompt that looks like this:
128
+ ```console
129
+ Flyshell [v1.x.x] (current_dir)>>
130
+ ```
131
+
132
+ From this stage, you can enter a wide variety of commands. To see the commands list for your installation, type `cmds` or `help` to return a full up-to-date directory. Use `fs plugins` to return plugin information.
133
+
134
+ Flyshell also supports command chaining out of the box. Use `;` to chain commands together regardless of result, `&&` to execute only on success and `||` to execute only on failure.
135
+
136
+ Use `-h` or `--help` after any command to return that command's specific help list.
137
+
138
+ To close Flyshell once you are done, enter `kill` onto the command line then confirm, or press Ctrl + D to close the session immediately.
139
+
140
+ ## Creating Custom Plugins
141
+
142
+ Flyshell features an extensible plugin API. This API supports both single-file and multi-file plugins. Follow the below guide to create your own.
143
+
144
+ Plugins must inherit from the `BasePlugin` contract defined in `src/flyshell/abstract/base_plugin.py`. Plugins are stored in the User Directory. Navigate to your App Storage folder:
145
+ - **Windows**: `%APPDATA%\Flyshell\plugins`
146
+ - **macOS**: `~/Library/Application Support/Flyshell/plugins`
147
+ - **Linux**: `~/.local/share/Flyshell/plugins`
148
+
149
+ Place your plugins inside the `plugins/` folder.
150
+
151
+ All plugins should define the required attributes. Flyshell can read this information for both `fs plugins` and `[my_plugin] -h` / `--help` which are both supported out of the box. Flyshell will discover, inspect and import your plugin from the `plugins/` folder without you having to alter any internal code.
152
+
153
+ ### The Entry-Point
154
+
155
+ All plugins must have an entry-point file. For single-file plugins, this is the plugin file. For multi-file plugins, this file must be named `plugin.py` and stored directly inside of your plugin's own folder.
156
+
157
+ This file is what enables Flyshell to speak to your plugin.
158
+
159
+ ```python
160
+ # Entry-point (my_plugin.py or my_plugin\plugin.py)
161
+
162
+ from flyshell import BasePlugin # This is required
163
+
164
+ class MyPlugin(BasePlugin):
165
+ name = "My Plugin" # The name you want visible to Flyshell
166
+ description = "This is my plugin." # A short one-line explaining what your plugin does
167
+ version = "1.0.0" # Default is 1.0.0
168
+ author = "kianodev" # You can put your name, your GitHub handle, or leave blank to make it 'Unknown'
169
+ usage = "" # (OPTIONAL) Explain how to use your plugin
170
+ extended_help = "" # (OPTIONAL) A more detailed, multi-line guide containing specific assistance
171
+
172
+ def on_load(self): # (OPTIONAL) Enables your program to handle specialist set-up tasks before execute()
173
+ pass
174
+
175
+ def on_unload(self): # (OPTIONAL) Enables your program to handle specialist shut-down tasks
176
+ super().on_unload() # If you use a custom on_unload(), end it with this
177
+
178
+
179
+ def execute(self, args): # This is what Flyshell calls to execute
180
+ print(f"Hello! Arguments: {args}")
181
+ return
182
+ ```
183
+
184
+ ### Extra Functions
185
+
186
+ Additional functions are available for you to use:
187
+
188
+ 1. `get_data(key, default=None)`: Retrieves a value from local plugin memory.
189
+ 2. `set_data(key, value)`: Updates a key in local memory and automatically persists it to SQLite via `save_storage()`.
190
+ 3. `load_storage()`: Reloads fresh key-value pairs directly from SQLite into `self.storage`.
191
+ 4. `save_storage()`: Writes all in-memory key-value pairs in `self.storage` back to SQLite.
192
+
193
+ Plugins save automatically on shut-down. If `on_unload()` is not used, it is automatic. If `on_unload()` is used, `super().on_unload()` saves your files.
194
+
195
+ ### Built-in Attributes
196
+
197
+ Every plugin has direct access to the following instance variables:
198
+
199
+ - `self.plugin_name` (`str`): The unique name identifier used to invoke the plugin.
200
+ - `self.storage` (`dict`): In-memory key-value data automatically loaded from the persistent database.
201
+ - `self.context` (`dict`): Environmental metadata provided at start-up, within it:
202
+ - `self.context["metadata"]["host_os"]`: Operating system (e.g., `'Windows'`, `'Linux'`, `'Darwin'`).
203
+ - `self.context["metadata"]["version"]`: Current Flyshell version.
204
+ - `self.context["file_path"]`: Full path to the plugin's source file.
205
+
206
+ ### Extra Notes
207
+
208
+ You are welcome to catch errors yourself. However, Flyshell will handle them automatically. That said, if you would like your plugin to continue after an error, you need to implement error handling yourself as the default system closes your plugin.
@@ -0,0 +1,22 @@
1
+ flyshell/__init__.py,sha256=45hJEblK9nMHFJF0N_x53uhAX9oocKmzy2kF-qHoLBo,164
2
+ flyshell/main.py,sha256=hf2B2Ad35FMTXynwXyZk29wVdVrIPsVbOkICtcl86Rc,4492
3
+ flyshell/abstract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ flyshell/abstract/base_cmd.py,sha256=kyKDLAmZJrBFbce0epBT6nom05GXYqZGhl0Fy1cImNU,982
5
+ flyshell/abstract/base_plugin.py,sha256=W3E-ysd6zqSgwcqtATLujV9Bxf2_YXSifepTRqWU7W8,2408
6
+ flyshell/abstract/parent_cmd.py,sha256=WSVDKCklQkHHOkKPsrY8F0ltTAuCzbl7bHEY9Sp0OIY,2387
7
+ flyshell/builtin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ flyshell/builtin/fs.py,sha256=dItltDzduKAexjmbsIVl_OUb-G4fCyX86tFwK60Sb0I,7215
9
+ flyshell/builtin/system.py,sha256=sgk4GPV8mO6luQgWMUyuTQVBFL4Y0NzB2w37XQogEJE,4974
10
+ flyshell/builtin/utils.py,sha256=cvzP43dU3oJm1a39fEBScUv-WJ5U9zoRuedvRIPpmr4,3478
11
+ flyshell/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ flyshell/core/auth.py,sha256=MwTM6uM-kddDi6B-yXc4B6TQlh0xuqOQJOZ4QhnbI9A,4271
13
+ flyshell/core/console.py,sha256=Ktu2Qvaa6ZGFZCw7Vld_6HDJPQBnwYJDFuX9FkEeLxM,2155
14
+ flyshell/core/data.py,sha256=0FlwssulyntbPoEjhHGfMd7YAoJEEDOGOjkOeA8IfGI,8842
15
+ flyshell/core/directory.py,sha256=bXe6sEGfjKDVrYkX2Bc6mD7hIWjVvKr1VdCatV_otWI,8360
16
+ flyshell/core/loader.py,sha256=2QUCAhkxf6p75O6nz5e0HG4jfIZvksBzQyEwVt-fvnU,3466
17
+ flyshell-1.0.0.dist-info/licenses/LICENSE,sha256=0dEPo6haQq5yozzi3aql0C0FnUCTZ0WRakYTNQakEEw,1086
18
+ flyshell-1.0.0.dist-info/METADATA,sha256=UVKhQ6WJxT9jxSueb9kJOCMA6uiUNW2nOVKprz9VCxE,9198
19
+ flyshell-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
20
+ flyshell-1.0.0.dist-info/entry_points.txt,sha256=MIfpdu-2yd0LEUu2gpmHiPxcytTQNrMgpQDNEmQH0UI,48
21
+ flyshell-1.0.0.dist-info/top_level.txt,sha256=oOEQzHfYVg-GEu9H5YnHffSAMkyiB6ud9KUAj_TlHRw,9
22
+ flyshell-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ flyshell = flyshell.main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kianodev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ flyshell