flyshell 1.0.0__tar.gz → 1.0.1__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 (33) hide show
  1. {flyshell-1.0.0/src/flyshell.egg-info → flyshell-1.0.1}/PKG-INFO +1 -1
  2. {flyshell-1.0.0 → flyshell-1.0.1}/pyproject.toml +1 -1
  3. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/builtin/fs.py +11 -10
  4. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/builtin/system.py +5 -7
  5. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/builtin/utils.py +3 -2
  6. flyshell-1.0.1/src/flyshell/constants.py +77 -0
  7. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/core/console.py +5 -16
  8. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/core/data.py +21 -52
  9. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/core/directory.py +10 -9
  10. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/core/loader.py +8 -9
  11. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/main.py +11 -31
  12. {flyshell-1.0.0 → flyshell-1.0.1/src/flyshell.egg-info}/PKG-INFO +1 -1
  13. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell.egg-info/SOURCES.txt +1 -0
  14. {flyshell-1.0.0 → flyshell-1.0.1}/tests/test_loader.py +9 -8
  15. {flyshell-1.0.0 → flyshell-1.0.1}/LICENSE +0 -0
  16. {flyshell-1.0.0 → flyshell-1.0.1}/README.md +0 -0
  17. {flyshell-1.0.0 → flyshell-1.0.1}/setup.cfg +0 -0
  18. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/__init__.py +0 -0
  19. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/abstract/__init__.py +0 -0
  20. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/abstract/base_cmd.py +0 -0
  21. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/abstract/base_plugin.py +0 -0
  22. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/abstract/parent_cmd.py +0 -0
  23. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/builtin/__init__.py +0 -0
  24. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/core/__init__.py +0 -0
  25. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell/core/auth.py +0 -0
  26. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell.egg-info/dependency_links.txt +0 -0
  27. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell.egg-info/entry_points.txt +0 -0
  28. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell.egg-info/requires.txt +0 -0
  29. {flyshell-1.0.0 → flyshell-1.0.1}/src/flyshell.egg-info/top_level.txt +0 -0
  30. {flyshell-1.0.0 → flyshell-1.0.1}/tests/test_auth.py +0 -0
  31. {flyshell-1.0.0 → flyshell-1.0.1}/tests/test_data.py +0 -0
  32. {flyshell-1.0.0 → flyshell-1.0.1}/tests/test_directory.py +0 -0
  33. {flyshell-1.0.0 → flyshell-1.0.1}/tests/test_security.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flyshell
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: A modular, zero-dependency command-line environment in Python.
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
@@ -14,7 +14,7 @@ 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"]
@@ -1,5 +1,6 @@
1
1
  # src\flyshell\builtin\fs.py
2
2
 
3
+ from flyshell import constants as c
3
4
  from flyshell.abstract.base_cmd import BaseCmd
4
5
  from flyshell.abstract.parent_cmd import ParentCmd
5
6
  from flyshell.core import data
@@ -64,10 +65,10 @@ class FsInfo(BaseCmd):
64
65
  return (0, (
65
66
  "\nFlyshell System Information:\n"
66
67
  "System Name: Flyshell\n"
67
- f"System Version: {data.VERSION}\n"
68
- f"System Build: {data.BUILD}\n"
69
- f"Host Operating System: '{data.HOST_OS}'\n"
70
- f"Installed Plugin Count: {len(data.PLUGINS)}\n"
68
+ f"System Version: {c.VERSION}\n"
69
+ f"System Build: {c.BUILD}\n"
70
+ f"Host Operating System: '{c.HOST_OS}'\n"
71
+ f"Installed Plugin Count: {len(c.PLUGINS)}\n"
71
72
  "Original Release Date: 8th August 2026\n"
72
73
  ))
73
74
 
@@ -98,13 +99,13 @@ class FsPlugins(BaseCmd):
98
99
  def execute(self, args):
99
100
  lines = ["\nInstalled Plugins:"]
100
101
  if data.PLUGINS:
101
- for name, plugin in data.PLUGINS.items():
102
+ for name, plugin in c.PLUGINS.items():
102
103
  p_name = getattr(plugin, "name", name)
103
104
  p_desc = getattr(plugin, "description", "No description provided.")
104
105
  lines.append(f"Plugin '{name}' [{p_name}]: {p_desc}")
105
106
  else:
106
107
  lines.append("No plugins installed.")
107
- lines.append(f"\nTotal available plugins: {len(data.PLUGINS)}\n")
108
+ lines.append(f"\nTotal available plugins: {len(c.PLUGINS)}\n")
108
109
  return (0, "\n".join(lines))
109
110
 
110
111
  class FsStatus(BaseCmd):
@@ -115,7 +116,7 @@ class FsStatus(BaseCmd):
115
116
  aliases = []
116
117
 
117
118
  def execute(self, args):
118
- uptime = int(time.time() - data.SESSION_START_TIME)
119
+ uptime = int(time.time() - c.SESSION_START_TIME)
119
120
  mins, secs = divmod(uptime, 60)
120
121
  hours, mins = divmod(mins, 60)
121
122
  uptime_str = f"{hours}h {mins}m {secs}s"
@@ -128,12 +129,12 @@ class FsStatus(BaseCmd):
128
129
  "\nFlyshell System Status:\n"
129
130
  f"User: '{current_user}'\n"
130
131
  f"Session Uptime: {uptime_str}\n"
131
- f"Commands (Session): {data.SESSION_CMD_COUNT}\n"
132
+ f"Commands (Session): {c.SESSION_CMD_COUNT}\n"
132
133
  f"Commands (Lifetime): {total_history_count}\n"
133
134
  f"Working Directory: '{os.getcwd()}'\n"
134
135
  f"Storage File Size: {storage_size_str}\n"
135
136
  f"Python Environment: v{sys.version.split()[0]}\n"
136
- f"\nFlyshell Version {data.VERSION} (Build {data.BUILD})\n"
137
+ f"\nFlyshell Version {c.VERSION} (Build {c.BUILD})\n"
137
138
  ))
138
139
 
139
140
  class FsVersion(BaseCmd):
@@ -143,7 +144,7 @@ class FsVersion(BaseCmd):
143
144
  options = None
144
145
  aliases = []
145
146
  def execute(self, args):
146
- return (0, f"\nYou are on Flyshell Version {data.VERSION} (Build {data.BUILD})\n")
147
+ return (0, f"\nYou are on Flyshell Version {c.VERSION} (Build {c.BUILD})\n")
147
148
 
148
149
  class Fs(ParentCmd):
149
150
  name = "fs"
@@ -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,6 @@ import os
8
9
  import re
9
10
  import shutil
10
11
  import subprocess
11
- import sys
12
12
 
13
13
  class Cd(BaseCmd):
14
14
  name = "cd"
@@ -68,9 +68,9 @@ class OpenFile(BaseCmd):
68
68
  if not (is_path or is_url or is_bin):
69
69
  return (1, f"\nCommand Error: File or application '{target}' could not be found.\n")
70
70
  try:
71
- if data.HOST_OS == "Windows":
71
+ if c.HOST_OS == "Windows":
72
72
  os.startfile(target)
73
- elif data.HOST_OS == "Darwin":
73
+ elif c.HOST_OS == "Darwin":
74
74
  subprocess.Popen(["open", target], stderr=subprocess.DEVNULL)
75
75
  else:
76
76
  subprocess.Popen(["xdg-open", target], stderr=subprocess.DEVNULL)
@@ -95,10 +95,8 @@ class SysExe(BaseCmd):
95
95
  override = False
96
96
  cmd = " ".join(args).strip().strip('"\'')
97
97
  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)
98
+ is_blocked = any(re.search(rf"\b{re.escape(b)}\b", cmd_normalised) for b in c.BLOCKED_COMMANDS)
99
+ is_dangerous = any(re.search(rf"\b{re.escape(d)}\b", cmd_normalised) for d in c.DANGER_COMMANDS)
102
100
  if is_blocked:
103
101
  return (1, (
104
102
  "\nSecurity Error: Flyshell has blocked execution of this command.\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
 
@@ -17,7 +18,7 @@ class Clear(BaseCmd):
17
18
  aliases = ["cls"]
18
19
 
19
20
  def execute(self, args):
20
- return (0, "\033[H\033[2J")
21
+ return (0, c.CLEAR_SCREEN)
21
22
 
22
23
  class History(BaseCmd):
23
24
  name = "history"
@@ -83,7 +84,7 @@ class Reboot(BaseCmd):
83
84
  cmd = sys.argv
84
85
  else:
85
86
  cmd = [sys.executable] + sys.argv
86
- if data.HOST_OS == "Windows":
87
+ if c.HOST_OS == "Windows":
87
88
  old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
88
89
  try:
89
90
  p = subprocess.run(cmd)
@@ -0,0 +1,77 @@
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 = 100
11
+ VERSION = "1.0.1"
12
+
13
+ def _resolve_app_dir() -> Path:
14
+ home = Path.home()
15
+ if HOST_OS == "Windows":
16
+ base = Path(os.environ.get("APPDATA", home / "AppData" / "Roaming"))
17
+ elif HOST_OS == "Darwin":
18
+ base = home / "Library" / "Application Support"
19
+ else:
20
+ base = Path(os.environ.get("XDG_DATA_HOME", home / ".local" / "share"))
21
+ target = base / "Flyshell"
22
+ target.mkdir(parents=True, exist_ok=True)
23
+ return target
24
+
25
+ HOST_OS = platform.system()
26
+ PACKAGE_ROOT = Path(__file__).resolve().parent
27
+ SRC_DIR = PACKAGE_ROOT.parent
28
+ PROJECT_ROOT = SRC_DIR.parent
29
+ APP_DIR = _resolve_app_dir()
30
+ FILE_PATH = APP_DIR / "flyshell.db"
31
+ USER_PLUGIN_DIR = APP_DIR / "plugins"
32
+
33
+ SESSION_START_TIME = time.time()
34
+ SESSION_CMD_COUNT = 0
35
+
36
+ PLUGINS = {}
37
+ RESERVED_NAMES = set(COMMAND_REGISTRY.keys())
38
+
39
+ BLOCKED_COMMANDS = frozenset({"format", "diskpart", "dd"})
40
+ DANGER_COMMANDS = frozenset({"del", "rmdir", "rd", "rm", "taskkill", "killall"})
41
+ SUPPORTED_OS = frozenset({"Windows", "Darwin", "Linux"})
42
+ SUPPORTED_PYTHON = (3, 10)
43
+
44
+ CLEAR_SCREEN = "\033[H\033[2J"
45
+
46
+ INITIALISED = set()
47
+
48
+ REQUIRED_COMPONENTS = [
49
+ "abstract",
50
+ "abstract/base_cmd.py",
51
+ "abstract/base_plugin.py",
52
+ "abstract/parent_cmd.py",
53
+ "builtin",
54
+ "builtin/fs.py",
55
+ "builtin/system.py",
56
+ "builtin/utils.py",
57
+ "constants.py",
58
+ "core",
59
+ "core/auth.py",
60
+ "core/console.py",
61
+ "core/data.py",
62
+ "core/directory.py",
63
+ "core/loader.py",
64
+ ]
65
+
66
+ GREETINGS = [
67
+ "Hi {username}, what's it going to be today?",
68
+ "The mic is yours, {username}, go and blow me away!",
69
+ "Hey {username}, let's get this party started!",
70
+ "What's up, {username}? Let's go!",
71
+ "I'm awaiting your first command, {username}!",
72
+ "How's it going {username}? I'm here to help.",
73
+ "{username}, it's your call! Show me what you've got, what are we doing today?",
74
+ "My wish is your command, {username}! (unless it's not on my command list)",
75
+ "I have goods for you {username}, it's called a Command Interface.",
76
+ "I'm your boss! Wait, no... sorry... you, {username}, are my boss!",
77
+ ]
@@ -1,5 +1,6 @@
1
1
  # src\flyshell\core\console.py
2
2
 
3
+ from flyshell import constants as c
3
4
  from flyshell.core import data
4
5
  from flyshell.core.directory import execute_line
5
6
 
@@ -32,28 +33,16 @@ def load_history():
32
33
 
33
34
  def boot():
34
35
  load_history()
35
- print("\033[H\033[2J", end="")
36
- print(f"\nBoot successful, welcome to Flyshell! (Version {data.VERSION})")
36
+ print(c.CLEAR_SCREEN, end="")
37
+ print(f"\nBoot successful, welcome to Flyshell! (Version {c.VERSION})")
37
38
  user_info = data.get_auth() or {}
38
39
  username = user_info.get("username")
39
- GREETINGS = [
40
- f"Hi {username}, what's it going to be today?",
41
- f"The mic is yours, {username}, go and blow me away!",
42
- f"Hey {username}, let's get this party started!",
43
- f"What's up, {username}? Let's go!",
44
- f"I'm awaiting your first command, {username}!",
45
- f"How's it going {username}? I'm here to help.",
46
- f"{username}, it's your call! Show me what you've got, what are we doing today?",
47
- f"My wish is your command, {username}! (unless it's not on my command list)",
48
- f"I have goods for you {username}, it's called a Command Interface.",
49
- f"I'm your boss! Wait, no... sorry... you, {username}, are my boss!",
50
- ]
51
- greeting = random.choice(GREETINGS)
40
+ greeting = random.choice(c.GREETINGS).format(username=username)
52
41
  print(f"\n{greeting}\n")
53
42
  while True:
54
43
  try:
55
44
  folder = os.path.basename(os.getcwd())
56
- raw_cmd = input(f"Flyshell [v{data.VERSION}] ({folder})>>")
45
+ raw_cmd = input(f"Flyshell [v{c.VERSION}] ({folder})>>")
57
46
  if not raw_cmd.strip():
58
47
  continue
59
48
  execute_line(raw_cmd)
@@ -1,60 +1,29 @@
1
1
  # src\flyshell\core\data.py
2
2
 
3
- BUILD = 99
4
- VERSION = "1.0.0"
3
+ from flyshell import constants as c
5
4
 
6
- from pathlib import Path
7
5
  import json
8
- import os
9
- import platform
10
6
  import sqlite3
11
7
  import time
12
8
 
13
- HOST_OS = platform.system()
14
-
15
- PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
16
-
17
- def get_app_dir() -> Path:
18
- home = Path.home()
19
- if HOST_OS == "Windows":
20
- base = Path(os.environ.get("APPDATA", home / "AppData" / "Roaming"))
21
- elif HOST_OS == "Darwin":
22
- base = home / "Library" / "Application Support"
23
- else:
24
- base = Path(os.environ.get("XDG_DATA_HOME", home / ".local" / "share"))
25
- app_dir = base / "Flyshell"
26
- app_dir.mkdir(parents=True, exist_ok=True)
27
- return app_dir
28
-
29
- APP_DIR = get_app_dir()
30
- FILE_PATH = APP_DIR / "flyshell.db"
31
- USER_PLUGIN_DIR = APP_DIR / "plugins"
32
-
33
- SESSION_START_TIME = time.time()
34
- SESSION_CMD_COUNT = 0
35
- PLUGINS = {}
36
-
37
9
  def get_storage_size() -> str:
38
- if not FILE_PATH.exists():
10
+ if not c.FILE_PATH.exists():
39
11
  return "File not found"
40
- num_bytes = float(FILE_PATH.stat().st_size)
12
+ num_bytes = float(c.FILE_PATH.stat().st_size)
41
13
  for unit in ["B", "KB", "MB", "GB"]:
42
14
  if num_bytes < 1024.0:
43
15
  return f"{int(num_bytes)} {unit}" if unit == "B" else f"{num_bytes:.2f} {unit}"
44
16
  num_bytes /= 1024.0
45
17
  return f"{num_bytes:.2f} TB"
46
18
 
47
- def _get_connection(filename=FILE_PATH):
19
+ def _get_connection(filename=c.FILE_PATH):
48
20
  conn = sqlite3.connect(filename)
49
21
  conn.execute("PRAGMA foreign_keys = ON;")
50
22
  return conn
51
23
 
52
- INITIALISED = set()
53
-
54
- def initialise(filename=FILE_PATH):
55
- global INITIALISED
24
+ def initialise(filename=c.FILE_PATH, initialised=c.INITIALISED):
56
25
  target = str(filename)
57
- if target in INITIALISED:
26
+ if target in initialised:
58
27
  return
59
28
  with _get_connection(filename) as conn:
60
29
  conn.execute("PRAGMA foreign_keys = ON;")
@@ -100,9 +69,9 @@ def initialise(filename=FILE_PATH):
100
69
  );
101
70
  """)
102
71
  conn.execute("PRAGMA user_version = 1;")
103
- INITIALISED.add(target)
72
+ initialised.add(target)
104
73
 
105
- def get_auth(filename=FILE_PATH) -> dict | None:
74
+ def get_auth(filename=c.FILE_PATH) -> dict | None:
106
75
  initialise(filename)
107
76
  with _get_connection(filename) as conn:
108
77
  row = conn.execute("SELECT username, hash, salt, created_at FROM auth WHERE id = 1").fetchone()
@@ -110,7 +79,7 @@ def get_auth(filename=FILE_PATH) -> dict | None:
110
79
  return None
111
80
  return {"username": row[0], "hash": row[1], "salt": row[2], "created_at": row[3]}
112
81
 
113
- def set_auth(username: str, pwd_hash: str, salt: str, filename=FILE_PATH) -> None:
82
+ def set_auth(username: str, pwd_hash: str, salt: str, filename=c.FILE_PATH) -> None:
114
83
  initialise(filename)
115
84
  now_utc = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
116
85
  with _get_connection(filename) as conn:
@@ -120,20 +89,20 @@ def set_auth(username: str, pwd_hash: str, salt: str, filename=FILE_PATH) -> Non
120
89
  VALUES (1, ?, ?, ?, ?)
121
90
  """, (username, pwd_hash, salt, now_utc))
122
91
 
123
- def clear_auth(filename=FILE_PATH) -> bool:
92
+ def clear_auth(filename=c.FILE_PATH) -> bool:
124
93
  initialise(filename)
125
94
  with _get_connection(filename) as conn:
126
95
  with conn:
127
96
  cursor = conn.execute("DELETE FROM auth WHERE id = 1")
128
97
  return cursor.rowcount > 0
129
98
 
130
- def append_history(cmd: str, timestamp: str, filename=FILE_PATH) -> None:
99
+ def append_history(cmd: str, timestamp: str, filename=c.FILE_PATH) -> None:
131
100
  initialise(filename)
132
101
  with _get_connection(filename) as conn:
133
102
  with conn:
134
103
  conn.execute("INSERT INTO cmd_history (command, timestamp) VALUES (?, ?)", (cmd, timestamp))
135
104
 
136
- def get_history(limit: int | None = None, filename=FILE_PATH) -> list[dict]:
105
+ def get_history(limit: int | None = None, filename=c.FILE_PATH) -> list[dict]:
137
106
  initialise(filename)
138
107
  with _get_connection(filename) as conn:
139
108
  if limit is not None and limit > 0:
@@ -145,14 +114,14 @@ def get_history(limit: int | None = None, filename=FILE_PATH) -> list[dict]:
145
114
  rows = conn.execute(query).fetchall()
146
115
  return [{"command": r[0], "timestamp": r[1]} for r in rows]
147
116
 
148
- def clear_history(filename=FILE_PATH) -> bool:
117
+ def clear_history(filename=c.FILE_PATH) -> bool:
149
118
  initialise(filename)
150
119
  with _get_connection(filename) as conn:
151
120
  with conn:
152
121
  conn.execute("DELETE FROM cmd_history")
153
122
  return True
154
123
 
155
- def get_config(key: str, default=None, filename=FILE_PATH):
124
+ def get_config(key: str, default=None, filename=c.FILE_PATH):
156
125
  initialise(filename)
157
126
  with _get_connection(filename) as conn:
158
127
  row = conn.execute("SELECT value FROM core_config WHERE key = ?", (key,)).fetchone()
@@ -163,7 +132,7 @@ def get_config(key: str, default=None, filename=FILE_PATH):
163
132
  except (ValueError, TypeError):
164
133
  return row[0]
165
134
 
166
- def set_config(key: str, value, filename=FILE_PATH) -> None:
135
+ def set_config(key: str, value, filename=c.FILE_PATH) -> None:
167
136
  initialise(filename)
168
137
  serialized = json.dumps(value)
169
138
  with _get_connection(filename) as conn:
@@ -173,14 +142,14 @@ def set_config(key: str, value, filename=FILE_PATH) -> None:
173
142
  VALUES (?, ?)
174
143
  """, (key, serialized))
175
144
 
176
- def delete_config(key: str, filename=FILE_PATH) -> bool:
145
+ def delete_config(key: str, filename=c.FILE_PATH) -> bool:
177
146
  initialise(filename)
178
147
  with _get_connection(filename) as conn:
179
148
  with conn:
180
149
  cursor = conn.execute("DELETE FROM core_config WHERE key = ?", (key,))
181
150
  return cursor.rowcount > 0
182
151
 
183
- def register_plugin(plugin_name: str, filename=FILE_PATH) -> None:
152
+ def register_plugin(plugin_name: str, filename=c.FILE_PATH) -> None:
184
153
  initialise(filename)
185
154
  now_utc = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
186
155
  with _get_connection(filename) as conn:
@@ -190,7 +159,7 @@ def register_plugin(plugin_name: str, filename=FILE_PATH) -> None:
190
159
  VALUES (?, ?, 1)
191
160
  """, (plugin_name, now_utc))
192
161
 
193
- def get_plugin_data(plugin_name: str, key: str, default=None, filename=FILE_PATH):
162
+ def get_plugin_data(plugin_name: str, key: str, default=None, filename=c.FILE_PATH):
194
163
  initialise(filename)
195
164
  with _get_connection(filename) as conn:
196
165
  row = conn.execute("""
@@ -203,7 +172,7 @@ def get_plugin_data(plugin_name: str, key: str, default=None, filename=FILE_PATH
203
172
  except (ValueError, TypeError):
204
173
  return row[0]
205
174
 
206
- def set_plugin_data(plugin_name: str, key: str, value, filename=FILE_PATH) -> None:
175
+ def set_plugin_data(plugin_name: str, key: str, value, filename=c.FILE_PATH) -> None:
207
176
  initialise(filename)
208
177
  register_plugin(plugin_name, filename=filename)
209
178
  serialized = json.dumps(value)
@@ -214,7 +183,7 @@ def set_plugin_data(plugin_name: str, key: str, value, filename=FILE_PATH) -> No
214
183
  VALUES (?, ?, ?)
215
184
  """, (plugin_name, key, serialized))
216
185
 
217
- def get_all_plugin_data(plugin_name: str, filename=FILE_PATH) -> dict:
186
+ def get_all_plugin_data(plugin_name: str, filename=c.FILE_PATH) -> dict:
218
187
  initialise(filename)
219
188
  with _get_connection(filename) as conn:
220
189
  rows = conn.execute("SELECT key, value FROM plugin_kv WHERE plugin_name = ?", (plugin_name,)).fetchall()
@@ -226,7 +195,7 @@ def get_all_plugin_data(plugin_name: str, filename=FILE_PATH) -> dict:
226
195
  result[k] = v
227
196
  return result
228
197
 
229
- def purge_plugin(plugin_name: str, filename=FILE_PATH) -> bool:
198
+ def purge_plugin(plugin_name: str, filename=c.FILE_PATH) -> bool:
230
199
  initialise(filename)
231
200
  with _get_connection(filename) as conn:
232
201
  with conn:
@@ -1,5 +1,6 @@
1
1
  # src\flyshell\core\directory.py
2
2
 
3
+ from flyshell import constants as c
3
4
  from flyshell.abstract.base_cmd import BaseCmd
4
5
  from flyshell.abstract.parent_cmd import ParentCmd
5
6
  from flyshell.builtin import fs, system, utils
@@ -60,7 +61,7 @@ def log(cmd):
60
61
 
61
62
  def _execute_single(raw_cmd: str) -> int:
62
63
  try:
63
- posix_mode = False if data.HOST_OS == "Windows" else True
64
+ posix_mode = False if c.HOST_OS == "Windows" else True
64
65
  cmd = shlex.split(raw_cmd, posix=posix_mode)
65
66
  except ValueError as e:
66
67
  print("\nCommand Error: Invalid syntax.")
@@ -77,8 +78,8 @@ def _execute_single(raw_cmd: str) -> int:
77
78
  return command.execute(args)
78
79
  command.help()
79
80
  return 0
80
- elif cmd_name in data.PLUGINS:
81
- plugin = data.PLUGINS[cmd_name]
81
+ elif cmd_name in c.PLUGINS:
82
+ plugin = c.PLUGINS[cmd_name]
82
83
  if hasattr(plugin, "help"):
83
84
  plugin.help()
84
85
  print()
@@ -87,8 +88,8 @@ def _execute_single(raw_cmd: str) -> int:
87
88
  print(f"\nPlugin Error: Plugin '{cmd_name}' does not provide help details.\n")
88
89
  return 1
89
90
  if "-d" in args or "--docs" in args:
90
- if cmd_name in data.PLUGINS:
91
- plugin = data.PLUGINS[cmd_name]
91
+ if cmd_name in c.PLUGINS:
92
+ plugin = c.PLUGINS[cmd_name]
92
93
  if hasattr(plugin, "show_docs") and plugin.show_docs():
93
94
  return 0
94
95
  print(f"\nPlugin Error: Plugin '{cmd_name}' has not provided documentation.\n")
@@ -106,7 +107,7 @@ def _execute_single(raw_cmd: str) -> int:
106
107
  print(f"\nCommand Error: Failed during execution of '{cmd_name}'.")
107
108
  print(f"Details: {e}\n")
108
109
  return 1
109
- data.SESSION_CMD_COUNT += 1
110
+ c.SESSION_CMD_COUNT += 1
110
111
  if isinstance(result, tuple):
111
112
  code, message = result
112
113
  if message:
@@ -119,9 +120,9 @@ def _execute_single(raw_cmd: str) -> int:
119
120
  print(result)
120
121
  return 0
121
122
  return 0
122
- elif cmd_name in data.PLUGINS:
123
+ elif cmd_name in c.PLUGINS:
123
124
  log(raw_cmd)
124
- plugin = data.PLUGINS[cmd_name]
125
+ plugin = c.PLUGINS[cmd_name]
125
126
  try:
126
127
  result = plugin.execute(args)
127
128
  return result if isinstance(result, int) else 0
@@ -156,7 +157,7 @@ def _parse_commands(raw_cmd: str) -> list:
156
157
  escape = False
157
158
  i += 1
158
159
  continue
159
- is_windows = (data.HOST_OS == "Windows")
160
+ is_windows = (c.HOST_OS == "Windows")
160
161
  if char == "\\":
161
162
  if not is_windows:
162
163
  escape = True
@@ -1,5 +1,6 @@
1
1
  # src\flyshell\core\loader.py
2
2
 
3
+ from flyshell import constants as c
3
4
  from flyshell.abstract.base_plugin import BasePlugin
4
5
  from flyshell.core import data
5
6
  from flyshell.core.directory import COMMAND_REGISTRY
@@ -10,11 +11,9 @@ import inspect
10
11
  import os
11
12
  import sys
12
13
 
13
- RESERVED_NAMES = set(COMMAND_REGISTRY.keys())
14
-
15
14
  def scan_plugins(plugin_folder=None):
16
- folder = Path(plugin_folder) if plugin_folder else data.USER_PLUGIN_DIR
17
- data.PLUGINS.clear()
15
+ folder = Path(plugin_folder) if plugin_folder else c.USER_PLUGIN_DIR
16
+ c.PLUGINS.clear()
18
17
  if not folder.exists():
19
18
  folder.mkdir(parents=True, exist_ok=True)
20
19
  print("\nNo plugins found.")
@@ -53,7 +52,7 @@ def scan_plugins(plugin_folder=None):
53
52
  first_print = True
54
53
  for name, entries in candidates.items():
55
54
  failed = False
56
- if name in RESERVED_NAMES:
55
+ if name in c.RESERVED_NAMES:
57
56
  print(f"\n⚠️ Collision Error - Plugin '{name}' conflicts with a built-in Flyshell command and will not be loaded.")
58
57
  failed = True
59
58
  if len(entries) > 1:
@@ -64,7 +63,7 @@ def scan_plugins(plugin_folder=None):
64
63
  if failed:
65
64
  continue
66
65
  instance, _ = entries[0]
67
- data.PLUGINS[name] = instance
66
+ c.PLUGINS[name] = instance
68
67
  if first_print:
69
68
  print("\nInstalled Plugins:")
70
69
  first_print = False
@@ -81,9 +80,9 @@ def build_context(plugin_name, file_path=None):
81
80
  "plugin_name": plugin_name,
82
81
  "file_path": str(file_path) if file_path else None,
83
82
  "metadata": {
84
- "host_os": data.HOST_OS,
85
- "version": data.VERSION,
86
- "build": data.BUILD
83
+ "host_os": c.HOST_OS,
84
+ "version": c.VERSION,
85
+ "build": c.BUILD
87
86
  },
88
87
  "storage": plugin_storage
89
88
  }
@@ -1,40 +1,20 @@
1
1
  # src\flyshell\main.py
2
2
 
3
+ from flyshell import constants as c
4
+
3
5
  from pathlib import Path
4
6
  import os
5
7
  import sys
6
8
 
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))
9
+ if str(c.SRC_DIR) not in sys.path:
10
+ sys.path.insert(0, str(c.SRC_DIR))
13
11
 
14
12
  def boot_check():
15
13
  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
14
  present = []
36
15
  missing = []
37
- for path in REQUIRED_COMPONENTS:
16
+ for obj in c.REQUIRED_COMPONENTS:
17
+ path = Path(c.PACKAGE_ROOT / obj)
38
18
  if path.exists():
39
19
  present.append(str(path))
40
20
  else:
@@ -47,7 +27,7 @@ def boot_check():
47
27
  print(f"❌ {i} - FILE NOT FOUND")
48
28
  if missing:
49
29
  return False
50
- tests_dir = PROJECT_ROOT / "tests"
30
+ tests_dir = c.PROJECT_ROOT / "tests"
51
31
  if tests_dir.exists():
52
32
  print("\nFlyshell will now check internal additional packages and features. These are not required.")
53
33
  for path in tests_dir.glob("test_*.py"):
@@ -67,17 +47,17 @@ def boot_check():
67
47
 
68
48
  def main():
69
49
  os.system("")
70
- print("\033[H\033[2J", end="")
50
+ print(c.CLEAR_SCREEN, end="")
71
51
  print("\nLaunch process initiated. Flyshell is checking it is able to launch...")
72
52
  try:
73
53
  if boot_check():
74
54
  from flyshell.core import auth, console, data, loader
75
- if data.HOST_OS in ["Windows", "Darwin", "Linux"]:
76
- if sys.version_info < (3, 10):
55
+ if c.HOST_OS in ["Windows", "Darwin", "Linux"]:
56
+ if sys.version_info < c.SUPPORTED_PYTHON:
77
57
  print("\nCRITICAL ERROR: Your Python version is too old to be supported by Flyshell.")
78
58
  print("Flyshell cannot launch because it requires Python 3.10 or newer.")
79
59
  sys.exit(1)
80
- print(f"\nYour OS '{data.HOST_OS}' is compatible with Flyshell.")
60
+ print(f"\nYour OS '{c.HOST_OS}' is compatible with Flyshell.")
81
61
  print("Flyshell will now boot.")
82
62
  loader.scan_plugins()
83
63
  if auth.login_flow():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flyshell
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: A modular, zero-dependency command-line environment in Python.
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
@@ -2,6 +2,7 @@ LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
4
  src/flyshell/__init__.py
5
+ src/flyshell/constants.py
5
6
  src/flyshell/main.py
6
7
  src/flyshell.egg-info/PKG-INFO
7
8
  src/flyshell.egg-info/SOURCES.txt
@@ -1,7 +1,8 @@
1
1
  # \tests\test_loader.py
2
2
 
3
+ from flyshell import constants as c
3
4
  from flyshell.abstract.base_plugin import BasePlugin
4
- from flyshell.core import data, loader
5
+ from flyshell.core import loader
5
6
 
6
7
  def test_loader_rejects_command_name_collision(tmp_path, monkeypatch):
7
8
  bad_plugin = tmp_path / "sys.py"
@@ -10,9 +11,9 @@ def test_loader_rejects_command_name_collision(tmp_path, monkeypatch):
10
11
  "class SysPlugin(BasePlugin):\n"
11
12
  " def execute(self, args): pass\n"
12
13
  )
13
- monkeypatch.setattr(data, "USER_PLUGIN_DIR", tmp_path)
14
+ monkeypatch.setattr(c, "USER_PLUGIN_DIR", tmp_path)
14
15
  loader.scan_plugins(plugin_folder=tmp_path)
15
- assert "sys" not in data.PLUGINS
16
+ assert "sys" not in c.PLUGINS
16
17
 
17
18
  def test_loader_discovers_and_instantiates_valid_plugin(tmp_path, monkeypatch):
18
19
  valid_plugin = tmp_path / "sample.py"
@@ -24,16 +25,16 @@ def test_loader_discovers_and_instantiates_valid_plugin(tmp_path, monkeypatch):
24
25
  " def execute(self, args):\n"
25
26
  " return (0, 'done')\n"
26
27
  )
27
- monkeypatch.setattr(data, "USER_PLUGIN_DIR", tmp_path)
28
+ monkeypatch.setattr(c, "USER_PLUGIN_DIR", tmp_path)
28
29
  loader.scan_plugins(plugin_folder=tmp_path)
29
- assert "sample" in data.PLUGINS
30
- plugin_instance = data.PLUGINS["sample"]
30
+ assert "sample" in c.PLUGINS
31
+ plugin_instance = c.PLUGINS["sample"]
31
32
  assert isinstance(plugin_instance, BasePlugin)
32
33
  assert plugin_instance.name == "Sample Tool"
33
34
 
34
35
  def test_loader_handles_broken_plugin_gracefully(tmp_path, monkeypatch):
35
36
  broken_plugin = tmp_path / "broken.py"
36
37
  broken_plugin.write_text("import non_existent_library_12345\n")
37
- monkeypatch.setattr(data, "USER_PLUGIN_DIR", tmp_path)
38
+ monkeypatch.setattr(c, "USER_PLUGIN_DIR", tmp_path)
38
39
  loader.scan_plugins(plugin_folder=tmp_path)
39
- assert "broken" not in data.PLUGINS
40
+ assert "broken" not in c.PLUGINS
File without changes
File without changes
File without changes
File without changes
File without changes