flyshell 1.0.0__tar.gz → 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.
Files changed (34) hide show
  1. {flyshell-1.0.0/src/flyshell.egg-info → flyshell-1.0.2}/PKG-INFO +4 -1
  2. {flyshell-1.0.0 → flyshell-1.0.2}/pyproject.toml +4 -1
  3. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/__init__.py +2 -1
  4. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/abstract/base_cmd.py +5 -2
  5. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/abstract/base_plugin.py +3 -2
  6. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/abstract/parent_cmd.py +16 -5
  7. flyshell-1.0.2/src/flyshell/builtin/fs.py +210 -0
  8. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/builtin/system.py +36 -18
  9. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/builtin/utils.py +15 -6
  10. flyshell-1.0.2/src/flyshell/constants.py +79 -0
  11. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/core/auth.py +9 -8
  12. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/core/console.py +11 -17
  13. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/core/data.py +90 -69
  14. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/core/directory.py +50 -22
  15. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/core/loader.py +18 -15
  16. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/main.py +43 -40
  17. {flyshell-1.0.0 → flyshell-1.0.2/src/flyshell.egg-info}/PKG-INFO +4 -1
  18. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell.egg-info/SOURCES.txt +1 -0
  19. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell.egg-info/requires.txt +3 -0
  20. {flyshell-1.0.0 → flyshell-1.0.2}/tests/test_auth.py +5 -1
  21. {flyshell-1.0.0 → flyshell-1.0.2}/tests/test_data.py +16 -3
  22. {flyshell-1.0.0 → flyshell-1.0.2}/tests/test_directory.py +17 -1
  23. {flyshell-1.0.0 → flyshell-1.0.2}/tests/test_loader.py +12 -8
  24. {flyshell-1.0.0 → flyshell-1.0.2}/tests/test_security.py +1 -1
  25. flyshell-1.0.0/src/flyshell/builtin/fs.py +0 -161
  26. {flyshell-1.0.0 → flyshell-1.0.2}/LICENSE +0 -0
  27. {flyshell-1.0.0 → flyshell-1.0.2}/README.md +0 -0
  28. {flyshell-1.0.0 → flyshell-1.0.2}/setup.cfg +0 -0
  29. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/abstract/__init__.py +0 -0
  30. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/builtin/__init__.py +0 -0
  31. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell/core/__init__.py +0 -0
  32. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell.egg-info/dependency_links.txt +0 -0
  33. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell.egg-info/entry_points.txt +0 -0
  34. {flyshell-1.0.0 → flyshell-1.0.2}/src/flyshell.egg-info/top_level.txt +0 -0
@@ -1,13 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flyshell
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: A modular, zero-dependency command-line environment in Python.
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Provides-Extra: dev
10
+ Requires-Dist: black; extra == "dev"
11
+ Requires-Dist: flake8; extra == "dev"
10
12
  Requires-Dist: pytest; extra == "dev"
13
+ Requires-Dist: pytest-cov; extra == "dev"
11
14
  Provides-Extra: readline
12
15
  Requires-Dist: pyreadline3; sys_platform == "win32" and extra == "readline"
13
16
  Dynamic: license-file
@@ -14,14 +14,17 @@ license = { text = "MIT" }
14
14
  flyshell = "flyshell.main:main"
15
15
 
16
16
  [tool.setuptools.dynamic]
17
- version = { attr = "flyshell.core.data.VERSION" }
17
+ version = { attr = "flyshell.constants.VERSION" }
18
18
 
19
19
  [tool.setuptools.packages.find]
20
20
  where = ["src"]
21
21
 
22
22
  [project.optional-dependencies]
23
23
  dev = [
24
+ "black",
25
+ "flake8",
24
26
  "pytest",
27
+ "pytest-cov",
25
28
  ]
26
29
  readline = [
27
30
  "pyreadline3; sys_platform == 'win32'",
@@ -1,7 +1,8 @@
1
1
  try:
2
2
  from flyshell.abstract.base_plugin import BasePlugin
3
+
3
4
  __all__ = ["BasePlugin"]
4
5
  except ImportError:
5
6
  BasePlugin = None
6
7
  __all__ = []
7
- pass
8
+ pass
@@ -2,6 +2,7 @@
2
2
 
3
3
  from typing import List, Optional, Tuple, Union
4
4
 
5
+
5
6
  class BaseCmd:
6
7
  name: str = ""
7
8
  description: str = ""
@@ -10,7 +11,9 @@ class BaseCmd:
10
11
  aliases: List[str] = []
11
12
 
12
13
  def execute(self, args: List[str]) -> Union[int, Tuple[int, str], str, None]:
13
- raise NotImplementedError(f"Command '{self.__class__.__name__}' has not implemented the execute method")
14
+ raise NotImplementedError(
15
+ f"Command '{self.__class__.__name__}' has not implemented the execute method"
16
+ )
14
17
 
15
18
  def help(self, prefix: str = ""):
16
19
  full_name = f"{prefix} {self.name}".strip() if prefix else self.name
@@ -23,4 +26,4 @@ class BaseCmd:
23
26
  print(f"Required Arguments: {self.min_args}")
24
27
  if self.options:
25
28
  print(f"Accepted Arguments: {self.options}")
26
- print()
29
+ print()
@@ -3,6 +3,7 @@
3
3
  from flyshell.core import data
4
4
  from pathlib import Path
5
5
 
6
+
6
7
  class BasePlugin:
7
8
  name: str = "Unnamed Plugin"
8
9
  description: str = "No description provided."
@@ -30,7 +31,7 @@ class BasePlugin:
30
31
  lines = [
31
32
  f"\nPlugin: {self.name} (v{self.version})",
32
33
  f"Description: {self.description}",
33
- f"Author: {self.author}"
34
+ f"Author: {self.author}",
34
35
  ]
35
36
  if self.usage:
36
37
  lines.append(f"Usage: {self.usage}")
@@ -72,4 +73,4 @@ class BasePlugin:
72
73
  def save_storage(self):
73
74
  for k, v in self.storage.items():
74
75
  data.set_plugin_data(self.plugin_name, k, v)
75
- self.context["storage"] = self.storage
76
+ self.context["storage"] = self.storage
@@ -4,6 +4,7 @@ from flyshell.abstract.base_cmd import BaseCmd
4
4
 
5
5
  from typing import Dict, List, Optional, Tuple, Union
6
6
 
7
+
7
8
  class ParentCmd(BaseCmd):
8
9
  min_args: int = 1
9
10
  subcommands: Dict[str, BaseCmd] = {}
@@ -31,10 +32,16 @@ class ParentCmd(BaseCmd):
31
32
  if sub in seen:
32
33
  continue
33
34
  seen.add(sub)
34
- label = f"{sub.name} (alias: {', '.join(sub.aliases)})" if sub.aliases else sub.name
35
+ label = (
36
+ f"{sub.name} (alias: {', '.join(sub.aliases)})"
37
+ if sub.aliases
38
+ else sub.name
39
+ )
35
40
  opt = f" [args: {sub.options}]" if sub.options else ""
36
41
  print(f"{label} - {sub.description}{opt}")
37
- print(f"\nFor subcommand assistance use '{self.name} <subcmd> -h' or '--help'\n")
42
+ print(
43
+ f"\nFor subcommand assistance use '{self.name} <subcmd> -h' or '--help'\n"
44
+ )
38
45
 
39
46
  def execute(self, args: List[str]) -> Union[int, Tuple[int, str], str, None]:
40
47
  if not args or args[0] in ("-h", "--help"):
@@ -43,7 +50,9 @@ class ParentCmd(BaseCmd):
43
50
  sub_name = args[0].lower()
44
51
  sub_args = args[1:]
45
52
  if sub_name not in self._sub_registry:
46
- print(f"\nCommand Error: Unknown subcommand '{sub_name}' for '{self.name}'.")
53
+ print(
54
+ f"\nCommand Error: Unknown subcommand '{sub_name}' for '{self.name}'."
55
+ )
47
56
  print(f"Use '{self.name} -h' or '--help' to see all valid subcommands.\n")
48
57
  return 1
49
58
  target = self._sub_registry[sub_name]
@@ -51,9 +60,11 @@ class ParentCmd(BaseCmd):
51
60
  target.help(prefix=self.name)
52
61
  return 0
53
62
  if len(sub_args) < target.min_args:
54
- print(f"\nCommand Error: '{self.name} {target.name}' requires at least {target.min_args} argument(s).")
63
+ print(
64
+ f"\nCommand Error: '{self.name} {target.name}' requires at least {target.min_args} argument(s)."
65
+ )
55
66
  if target.options:
56
67
  print(f"Accepted arguments: {target.options}")
57
68
  print()
58
69
  return 1
59
- return target.execute(sub_args)
70
+ return target.execute(sub_args)
@@ -0,0 +1,210 @@
1
+ # src\flyshell\builtin\fs.py
2
+
3
+ from flyshell import constants as c
4
+ from flyshell.abstract.base_cmd import BaseCmd
5
+ from flyshell.abstract.parent_cmd import ParentCmd
6
+ from flyshell.core import data
7
+
8
+ import os
9
+ import sys
10
+ import time
11
+
12
+
13
+ class FsDel(BaseCmd):
14
+ name = "del"
15
+ description = "Delete / purge an area of Flyshell system storage"
16
+ min_args = 1
17
+ options = "'history', 'all', 'plugin <file>'"
18
+ aliases = ["purge"]
19
+
20
+ def execute(self, args):
21
+ if len(args) < 1:
22
+ return (1, "\nCommand Error: 'fs del' requires a target.\n")
23
+ target = args[0].lower()
24
+ match target:
25
+ case "history":
26
+ choice = (
27
+ input(
28
+ "\nAre you sure you want to clear all command history? [y/n]: "
29
+ )
30
+ .strip()
31
+ .lower()
32
+ )
33
+ if choice == "y":
34
+ data.clear_history()
35
+ return (0, "\nCommand history purged successfully.\n")
36
+ return (1, "\nAction cancelled.\n")
37
+ case "config":
38
+ if len(args) < 2:
39
+ return (
40
+ 1,
41
+ "\nCommand Error: Please specify a configuration key to delete.\n",
42
+ )
43
+ key = args[1]
44
+ if data.delete_config(key):
45
+ return (0, f"\nConfig setting '{key}' deleted.\n")
46
+ return (1, f"\nConfig Error: Setting '{key}' not found.\n")
47
+ case "plugin":
48
+ if len(args) < 2:
49
+ return (
50
+ 1,
51
+ "\nCommand Error: Please specify a plugin name to purge.\n",
52
+ )
53
+ plugin_name = args[1]
54
+ choice = (
55
+ input(
56
+ f"\nAre you sure you want to purge all data for plugin '{plugin_name}'? [y/n]: "
57
+ )
58
+ .strip()
59
+ .lower()
60
+ )
61
+ if choice == "y":
62
+ if data.purge_plugin(plugin_name):
63
+ return (
64
+ 0,
65
+ f"\nPlugin '{plugin_name}' and its storage purged successfully.\n",
66
+ )
67
+ return (
68
+ 1,
69
+ f"\nPlugin Error: No data found for plugin '{plugin_name}'.\n",
70
+ )
71
+ return (1, "\nAction cancelled.\n")
72
+ case "all":
73
+ choice = (
74
+ input(
75
+ "\n⚠️ WARNING: This will wipe history, configurations, and plugin data. Proceed? [y/n]: "
76
+ )
77
+ .strip()
78
+ .lower()
79
+ )
80
+ if choice == "y":
81
+ data.clear_history()
82
+ for name in list(data.PLUGINS.keys()):
83
+ data.purge_plugin(name)
84
+ return (0, "\nAll non-auth system data purged successfully.\n")
85
+ return (1, "\nAction cancelled.\n")
86
+ case _:
87
+ return (1, f"\nCommand Error: Unknown target '{target}'.\n")
88
+
89
+
90
+ class FsInfo(BaseCmd):
91
+ name = "info"
92
+ description = "Display Flyshell system information"
93
+ min_args = 0
94
+ options = None
95
+ aliases = []
96
+
97
+ def execute(self, args):
98
+ return (
99
+ 0,
100
+ (
101
+ "\nFlyshell System Information:\n"
102
+ "System Name: Flyshell\n"
103
+ f"System Version: {c.VERSION}\n"
104
+ f"System Build: {c.BUILD}\n"
105
+ f"Host Operating System: '{c.HOST_OS}'\n"
106
+ f"Installed Plugin Count: {len(c.PLUGINS)}\n"
107
+ "Original Release Date: 8th August 2026\n"
108
+ ),
109
+ )
110
+
111
+
112
+ class FsLicence(BaseCmd):
113
+ name = "licence"
114
+ description = "Display Flyshell licence conditions"
115
+ min_args = 0
116
+ options = None
117
+ aliases = ["license"]
118
+
119
+ def execute(self, args):
120
+ return (
121
+ 0,
122
+ (
123
+ "\nFlyshell Licensing Information:\n"
124
+ "Flyshell is licensed under the MIT Licence\n\n"
125
+ "Copyright (c) 2026 kianodev\n\n"
126
+ "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files [the 'Software'], to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n"
127
+ "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\n"
128
+ "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
129
+ ),
130
+ )
131
+
132
+
133
+ class FsPlugins(BaseCmd):
134
+ name = "plugins"
135
+ description = "Display installed plugins and their details"
136
+ min_args = 0
137
+ options = None
138
+ aliases = ["plugin"]
139
+
140
+ def execute(self, args):
141
+ lines = ["\nInstalled Plugins:"]
142
+ if data.PLUGINS:
143
+ for name, plugin in c.PLUGINS.items():
144
+ p_name = getattr(plugin, "name", name)
145
+ p_desc = getattr(plugin, "description", "No description provided.")
146
+ lines.append(f"Plugin '{name}' [{p_name}]: {p_desc}")
147
+ else:
148
+ lines.append("No plugins installed.")
149
+ lines.append(f"\nTotal available plugins: {len(c.PLUGINS)}\n")
150
+ return (0, "\n".join(lines))
151
+
152
+
153
+ class FsStatus(BaseCmd):
154
+ name = "status"
155
+ description = "Return Flyshell status and runtime details"
156
+ min_args = 0
157
+ options = None
158
+ aliases = []
159
+
160
+ def execute(self, args):
161
+ uptime = int(time.time() - c.SESSION_START_TIME)
162
+ mins, secs = divmod(uptime, 60)
163
+ hours, mins = divmod(mins, 60)
164
+ uptime_str = f"{hours}h {mins}m {secs}s"
165
+ auth_data = data.get_auth() or {}
166
+ current_user = auth_data.get("username", "Unknown")
167
+ cmd_history = data.get_history()
168
+ total_history_count = len(cmd_history) if isinstance(cmd_history, list) else 0
169
+ storage_size_str = data.get_storage_size()
170
+ return (
171
+ 0,
172
+ (
173
+ "\nFlyshell System Status:\n"
174
+ f"User: '{current_user}'\n"
175
+ f"Session Uptime: {uptime_str}\n"
176
+ f"Commands (Session): {c.SESSION_CMD_COUNT}\n"
177
+ f"Commands (Lifetime): {total_history_count}\n"
178
+ f"Working Directory: '{os.getcwd()}'\n"
179
+ f"Storage File Size: {storage_size_str}\n"
180
+ f"Python Environment: v{sys.version.split()[0]}\n"
181
+ f"\nFlyshell Version {c.VERSION} (Build {c.BUILD})\n"
182
+ ),
183
+ )
184
+
185
+
186
+ class FsVersion(BaseCmd):
187
+ name = "version"
188
+ description = "Return Flyshell version and build"
189
+ min_args = 0
190
+ options = None
191
+ aliases = []
192
+
193
+ def execute(self, args):
194
+ return (0, f"\nYou are on Flyshell Version {c.VERSION} (Build {c.BUILD})\n")
195
+
196
+
197
+ class Fs(ParentCmd):
198
+ name = "fs"
199
+ description = "Execute core Flyshell control and utility functions"
200
+ aliases = []
201
+
202
+ def get_subcommands(self):
203
+ return [
204
+ FsDel(),
205
+ FsInfo(),
206
+ FsLicence(),
207
+ FsPlugins(),
208
+ FsStatus(),
209
+ FsVersion(),
210
+ ]
@@ -1,5 +1,6 @@
1
1
  # src\flyshell\builtin\system.py
2
2
 
3
+ from flyshell import constants as c
3
4
  from flyshell.abstract.base_cmd import BaseCmd
4
5
  from flyshell.core import auth, data
5
6
 
@@ -8,7 +9,7 @@ import os
8
9
  import re
9
10
  import shutil
10
11
  import subprocess
11
- import sys
12
+
12
13
 
13
14
  class Cd(BaseCmd):
14
15
  name = "cd"
@@ -27,7 +28,11 @@ class Cd(BaseCmd):
27
28
  except PermissionError:
28
29
  return (1, f"\nSystem Error: Permission denied. Cannot access '{target}'\n")
29
30
  except OSError as e:
30
- return (1, f"\nCommand Error: Invalid path syntax '{target}'. ({e.strerror})\n")
31
+ return (
32
+ 1,
33
+ f"\nCommand Error: Invalid path syntax '{target}'. ({e.strerror})\n",
34
+ )
35
+
31
36
 
32
37
  class Dir(BaseCmd):
33
38
  name = "dir"
@@ -51,6 +56,7 @@ class Dir(BaseCmd):
51
56
  except Exception as e:
52
57
  return (1, f"\nSystem Error: Unable to list directory ({e})\n")
53
58
 
59
+
54
60
  class OpenFile(BaseCmd):
55
61
  name = "open"
56
62
  description = "Open the specified file path"
@@ -59,18 +65,21 @@ class OpenFile(BaseCmd):
59
65
  aliases = []
60
66
 
61
67
  def execute(self, args):
62
- target = " ".join(args).strip().strip('"\'')
68
+ target = " ".join(args).strip().strip("\"'")
63
69
  if not target:
64
70
  return (0, "")
65
71
  is_path = os.path.exists(target)
66
72
  is_url = target.startswith(("http://", "https://"))
67
73
  is_bin = shutil.which(target) is not None
68
74
  if not (is_path or is_url or is_bin):
69
- return (1, f"\nCommand Error: File or application '{target}' could not be found.\n")
75
+ return (
76
+ 1,
77
+ f"\nCommand Error: File or application '{target}' could not be found.\n",
78
+ )
70
79
  try:
71
- if data.HOST_OS == "Windows":
80
+ if c.HOST_OS == "Windows":
72
81
  os.startfile(target)
73
- elif data.HOST_OS == "Darwin":
82
+ elif c.HOST_OS == "Darwin":
74
83
  subprocess.Popen(["open", target], stderr=subprocess.DEVNULL)
75
84
  else:
76
85
  subprocess.Popen(["xdg-open", target], stderr=subprocess.DEVNULL)
@@ -78,6 +87,7 @@ class OpenFile(BaseCmd):
78
87
  except Exception as e:
79
88
  return (1, f"\nSystem Error: Failed to open '{target}': {e}\n")
80
89
 
90
+
81
91
  class SysExe(BaseCmd):
82
92
  name = "sys"
83
93
  description = "Execute the subsequent command on the host system"
@@ -93,20 +103,28 @@ class SysExe(BaseCmd):
93
103
  return (1, "\nCommand Error: 'sys' requires at least 1 parameter(s).\n")
94
104
  else:
95
105
  override = False
96
- cmd = " ".join(args).strip().strip('"\'')
106
+ cmd = " ".join(args).strip().strip("\"'")
97
107
  cmd_normalised = " ".join(cmd.lower().split())
98
- BLOCKED_COMMANDS = {"format", "diskpart", "dd"}
99
- is_blocked = any(re.search(rf"\b{re.escape(b)}\b", cmd_normalised) for b in BLOCKED_COMMANDS)
100
- DANGER_COMMANDS = {"del", "rmdir", "rd", "rm", "taskkill", "killall"}
101
- is_dangerous = any(re.search(rf"\b{re.escape(d)}\b", cmd_normalised) for d in DANGER_COMMANDS)
108
+ is_blocked = any(
109
+ re.search(rf"\b{re.escape(b)}\b", cmd_normalised)
110
+ for b in c.BLOCKED_COMMANDS
111
+ )
112
+ is_dangerous = any(
113
+ re.search(rf"\b{re.escape(d)}\b", cmd_normalised) for d in c.DANGER_COMMANDS
114
+ )
102
115
  if is_blocked:
103
- return (1, (
104
- "\nSecurity Error: Flyshell has blocked execution of this command.\n"
105
- f"This command '{cmd}' is permanently restricted by Flyshell safety policy.\n"
106
- "Flyshell has blocked your command to prevent damage to your computer.\n"
107
- ))
116
+ return (
117
+ 1,
118
+ (
119
+ "\nSecurity Error: Flyshell has blocked execution of this command.\n"
120
+ f"This command '{cmd}' is permanently restricted by Flyshell safety policy.\n"
121
+ "Flyshell has blocked your command to prevent damage to your computer.\n"
122
+ ),
123
+ )
108
124
  elif is_dangerous and not override:
109
- password = getpass.getpass("\nThis command is restricted. To execute, enter your password: ")
125
+ password = getpass.getpass(
126
+ "\nThis command is restricted. To execute, enter your password: "
127
+ )
110
128
  auth_info = data.get_auth() or {}
111
129
  stored_hash = auth_info.get("hash")
112
130
  stored_salt = auth_info.get("salt")
@@ -121,4 +139,4 @@ class SysExe(BaseCmd):
121
139
  print()
122
140
  return (130, "")
123
141
  except Exception as e:
124
- return (1, f"\nCommand Error: Failed to run host command ({e})\n")
142
+ return (1, f"\nCommand Error: Failed to run host command ({e})\n")
@@ -1,5 +1,6 @@
1
1
  # src\flyshell\builtin\utils.py
2
2
 
3
+ from flyshell import constants as c
3
4
  from flyshell.abstract.base_cmd import BaseCmd
4
5
  from flyshell.core import data
5
6
 
@@ -9,6 +10,7 @@ import subprocess
9
10
  import sys
10
11
  import time
11
12
 
13
+
12
14
  class Clear(BaseCmd):
13
15
  name = "clear"
14
16
  description = "Clear the screen"
@@ -17,7 +19,8 @@ class Clear(BaseCmd):
17
19
  aliases = ["cls"]
18
20
 
19
21
  def execute(self, args):
20
- return (0, "\033[H\033[2J")
22
+ return (0, c.CLEAR_SCREEN)
23
+
21
24
 
22
25
  class History(BaseCmd):
23
26
  name = "history"
@@ -41,7 +44,7 @@ class History(BaseCmd):
41
44
  return (0, "\nHistory is empty.\n")
42
45
  lines = [
43
46
  "\nCommand History:",
44
- f"Showing last {len(history_entries)} entries.\n"
47
+ f"Showing last {len(history_entries)} entries.\n",
45
48
  ]
46
49
  for i, entry in enumerate(history_entries, start=1):
47
50
  cmd = entry.get("command", "")
@@ -51,6 +54,7 @@ class History(BaseCmd):
51
54
  lines.append("")
52
55
  return (0, "\n".join(lines))
53
56
 
57
+
54
58
  class Kill(BaseCmd):
55
59
  name = "kill"
56
60
  description = "Shut down the Flyshell system"
@@ -66,6 +70,7 @@ class Kill(BaseCmd):
66
70
  else:
67
71
  return (1, "\nAction cancelled.\n")
68
72
 
73
+
69
74
  class Reboot(BaseCmd):
70
75
  name = "reboot"
71
76
  description = "Reboot the Flyshell system"
@@ -76,14 +81,14 @@ class Reboot(BaseCmd):
76
81
  def execute(self, args):
77
82
  print("\nRestarting Flyshell...\n")
78
83
  is_direct_script = (
79
- sys.argv[0].lower().endswith(".exe")
84
+ sys.argv[0].lower().endswith(".exe")
80
85
  or os.path.basename(sys.argv[0]) == "flyshell"
81
86
  )
82
87
  if is_direct_script:
83
88
  cmd = sys.argv
84
89
  else:
85
90
  cmd = [sys.executable] + sys.argv
86
- if data.HOST_OS == "Windows":
91
+ if c.HOST_OS == "Windows":
87
92
  old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
88
93
  try:
89
94
  p = subprocess.run(cmd)
@@ -93,6 +98,7 @@ class Reboot(BaseCmd):
93
98
  else:
94
99
  os.execv(cmd[0], cmd)
95
100
 
101
+
96
102
  class Sleep(BaseCmd):
97
103
  name = "sleep"
98
104
  description = "Sleep the system for a specified time"
@@ -102,7 +108,10 @@ class Sleep(BaseCmd):
102
108
 
103
109
  def execute(self, args):
104
110
  if not args or not args[0].replace(".", "", 1).isdigit():
105
- return (1, f"\nCommand Error: Invalid argument '{args[0] if args else ''}'\n")
111
+ return (
112
+ 1,
113
+ f"\nCommand Error: Invalid argument '{args[0] if args else ''}'\n",
114
+ )
106
115
  secs = float(args[0])
107
116
  try:
108
117
  print(f"\nPausing Flyshell for {secs} second(s).")
@@ -110,4 +119,4 @@ class Sleep(BaseCmd):
110
119
  time.sleep(secs)
111
120
  return (0, "\nReturning...\n")
112
121
  except KeyboardInterrupt:
113
- return (1, "\nSleep cancelled.\n")
122
+ return (1, "\nSleep cancelled.\n")
@@ -0,0 +1,79 @@
1
+ # src\flyshell\constants.py
2
+
3
+ from flyshell.core.directory import COMMAND_REGISTRY
4
+
5
+ from pathlib import Path
6
+ import os
7
+ import platform
8
+ import time
9
+
10
+ BUILD = 101
11
+ VERSION = "1.0.2"
12
+
13
+
14
+ def _resolve_app_dir() -> Path:
15
+ home = Path.home()
16
+ if HOST_OS == "Windows":
17
+ base = Path(os.environ.get("APPDATA", home / "AppData" / "Roaming"))
18
+ elif HOST_OS == "Darwin":
19
+ base = home / "Library" / "Application Support"
20
+ else:
21
+ base = Path(os.environ.get("XDG_DATA_HOME", home / ".local" / "share"))
22
+ target = base / "Flyshell"
23
+ target.mkdir(parents=True, exist_ok=True)
24
+ return target
25
+
26
+
27
+ HOST_OS = platform.system()
28
+ PACKAGE_ROOT = Path(__file__).resolve().parent
29
+ SRC_DIR = PACKAGE_ROOT.parent
30
+ PROJECT_ROOT = SRC_DIR.parent
31
+ APP_DIR = _resolve_app_dir()
32
+ FILE_PATH = APP_DIR / "flyshell.db"
33
+ USER_PLUGIN_DIR = APP_DIR / "plugins"
34
+
35
+ SESSION_START_TIME = time.time()
36
+ SESSION_CMD_COUNT = 0
37
+
38
+ PLUGINS = {}
39
+ RESERVED_NAMES = set(COMMAND_REGISTRY.keys())
40
+
41
+ BLOCKED_COMMANDS = frozenset({"format", "diskpart", "dd"})
42
+ DANGER_COMMANDS = frozenset({"del", "rmdir", "rd", "rm", "taskkill", "killall"})
43
+ SUPPORTED_OS = frozenset({"Windows", "Darwin", "Linux"})
44
+ SUPPORTED_PYTHON = (3, 10)
45
+
46
+ CLEAR_SCREEN = "\033[H\033[2J"
47
+
48
+ INITIALISED = set()
49
+
50
+ REQUIRED_COMPONENTS = [
51
+ "abstract",
52
+ "abstract/base_cmd.py",
53
+ "abstract/base_plugin.py",
54
+ "abstract/parent_cmd.py",
55
+ "builtin",
56
+ "builtin/fs.py",
57
+ "builtin/system.py",
58
+ "builtin/utils.py",
59
+ "constants.py",
60
+ "core",
61
+ "core/auth.py",
62
+ "core/console.py",
63
+ "core/data.py",
64
+ "core/directory.py",
65
+ "core/loader.py",
66
+ ]
67
+
68
+ GREETINGS = [
69
+ "Hi {username}, what's it going to be today?",
70
+ "The mic is yours, {username}, go and blow me away!",
71
+ "Hey {username}, let's get this party started!",
72
+ "What's up, {username}? Let's go!",
73
+ "I'm awaiting your first command, {username}!",
74
+ "How's it going {username}? I'm here to help.",
75
+ "{username}, it's your call! Show me what you've got, what are we doing today?",
76
+ "My wish is your command, {username}! (unless it's not on my command list)",
77
+ "I have goods for you {username}, it's called a Command Interface.",
78
+ "I'm your boss! Wait, no... sorry... you, {username}, are my boss!",
79
+ ]