flyshell 1.0.11__tar.gz → 1.0.13__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.11/src/flyshell.egg-info → flyshell-1.0.13}/PKG-INFO +1 -1
  2. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/diskops.py +213 -15
  3. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/utils.py +16 -0
  4. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/constants.py +2 -2
  5. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/directory.py +4 -0
  6. {flyshell-1.0.11 → flyshell-1.0.13/src/flyshell.egg-info}/PKG-INFO +1 -1
  7. {flyshell-1.0.11 → flyshell-1.0.13}/LICENSE +0 -0
  8. {flyshell-1.0.11 → flyshell-1.0.13}/README.md +0 -0
  9. {flyshell-1.0.11 → flyshell-1.0.13}/pyproject.toml +0 -0
  10. {flyshell-1.0.11 → flyshell-1.0.13}/setup.cfg +0 -0
  11. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/__init__.py +0 -0
  12. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/abstract/__init__.py +0 -0
  13. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/abstract/base_cmd.py +0 -0
  14. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/abstract/base_plugin.py +0 -0
  15. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/abstract/parent_cmd.py +0 -0
  16. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/__init__.py +0 -0
  17. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/fs.py +0 -0
  18. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/system.py +0 -0
  19. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/__init__.py +0 -0
  20. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/auth.py +0 -0
  21. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/console.py +0 -0
  22. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/data.py +0 -0
  23. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/loader.py +0 -0
  24. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/main.py +0 -0
  25. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/SOURCES.txt +0 -0
  26. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/dependency_links.txt +0 -0
  27. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/entry_points.txt +0 -0
  28. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/requires.txt +0 -0
  29. {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/top_level.txt +0 -0
  30. {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_auth.py +0 -0
  31. {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_data.py +0 -0
  32. {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_directory.py +0 -0
  33. {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_loader.py +0 -0
  34. {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_security.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flyshell
3
- Version: 1.0.11
3
+ Version: 1.0.13
4
4
  Summary: A modular, zero-dependency command-line environment in Python.
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
@@ -73,6 +73,69 @@ class Cat(BaseCmd):
73
73
  return (1, f"\nSystem Error: Failed to read file '{raw_target}' ({e})\n")
74
74
 
75
75
 
76
+ class Copy(BaseCmd):
77
+ name = "cp"
78
+ description = "Copy files or directories to a new destination"
79
+ min_args = 2
80
+ options = "Source, then destination"
81
+ aliases = ["copy"]
82
+
83
+ def execute(self, args):
84
+ force = ("-f" in args) or ("--force" in args)
85
+ flags = {"-f", "--force"}
86
+ path_tokens = [arg for arg in args if arg not in flags]
87
+ if len(path_tokens) < 2:
88
+ return (
89
+ 1,
90
+ "\nCommand Error: 'cp' requires both a source and destination path.\n",
91
+ )
92
+ raw_src = path_tokens[0].strip("\"'")
93
+ raw_dst = path_tokens[1].strip("\"'")
94
+ src_path = Path(raw_src).expanduser().resolve()
95
+ dst_path = Path(raw_dst).expanduser().resolve()
96
+ if not src_path.exists():
97
+ return (1, f"\nSystem Error: Source '{raw_src}' not found.\n")
98
+ if dst_path.exists() and dst_path.is_dir():
99
+ final_dst = dst_path / src_path.name
100
+ else:
101
+ final_dst = dst_path
102
+ if final_dst.exists() and not force:
103
+ try:
104
+ confirm = (
105
+ input(
106
+ f"\nDestination '{final_dst.name}' already exists. Overwrite? [y/n]: "
107
+ )
108
+ .strip()
109
+ .lower()
110
+ )
111
+ except KeyboardInterrupt:
112
+ return (1, "\nCopy cancelled by user.\n")
113
+ if confirm != "y":
114
+ return (0, "\nOperation cancelled. No files were copied.\n")
115
+ try:
116
+ if src_path.is_dir():
117
+ shutil.copytree(src_path, final_dst, dirs_exist_ok=force)
118
+ return (
119
+ 0,
120
+ f"\nSUCCESS! Copied directory '{src_path.name}' -> '{final_dst}'\n",
121
+ )
122
+ else:
123
+ shutil.copy2(src_path, final_dst)
124
+ return (
125
+ 0,
126
+ f"\nSUCCESS! Copied file '{src_path.name}' -> '{final_dst}'\n",
127
+ )
128
+ except PermissionError:
129
+ return (
130
+ 1,
131
+ f"\nSystem Error: Permission denied. Cannot copy to '{final_dst}'\n",
132
+ )
133
+ except shutil.SameFileError:
134
+ return (1, "\nSystem Error: Source and destination are the same.\n")
135
+ except Exception as e:
136
+ return (1, f"\nSystem Error: Failed to copy '{raw_src}' ({e})\n")
137
+
138
+
76
139
  class Dir(BaseCmd):
77
140
  name = "dir"
78
141
  description = "List all files in the current working directory"
@@ -126,6 +189,125 @@ class MkDir(BaseCmd):
126
189
  return (1, f"\nSystem Error: Unable to create directory '{target}' ({e})\n")
127
190
 
128
191
 
192
+ class Move(BaseCmd):
193
+ name = "mv"
194
+ description = "Move or relocate files and directories"
195
+ min_args = 2
196
+ options = "Source, then destination"
197
+ aliases = ["move"]
198
+
199
+ def execute(self, args):
200
+ force = ("-f" in args) or ("--force" in args)
201
+ flags = {"-f", "--force"}
202
+ path_tokens = [arg for arg in args if arg not in flags]
203
+ if len(path_tokens) < 2:
204
+ return (
205
+ 1,
206
+ "\nCommand Error: 'mv' requires both a source and a destination path.\n",
207
+ )
208
+ raw_src = path_tokens[0].strip("\"'")
209
+ raw_dst = path_tokens[1].strip("\"'")
210
+ src_path = Path(raw_src).expanduser().resolve()
211
+ dst_path = Path(raw_dst).expanduser().resolve()
212
+ if not src_path.exists():
213
+ return (1, f"\nSystem Error: Source '{raw_src}' not found.\n")
214
+ if src_path == src_path.parent or str(src_path) == src_path.anchor:
215
+ return (
216
+ 1,
217
+ "\nSecurity Error: Flyshell has blocked moving the root filesystem.\n",
218
+ )
219
+ flyshell_dirs = [
220
+ c.APP_DIR.resolve(),
221
+ c.PACKAGE_ROOT.resolve(),
222
+ c.PROJECT_ROOT.resolve(),
223
+ ]
224
+ for protected in flyshell_dirs:
225
+ if src_path == protected or src_path.is_relative_to(protected):
226
+ return (
227
+ 1,
228
+ "\nSecurity Error: Flyshell has blocked this action.\n"
229
+ f"Source '{raw_src}' contains or belongs to Flyshell system files.\n",
230
+ )
231
+ if dst_path.exists() and dst_path.is_dir():
232
+ final_dst = dst_path / src_path.name
233
+ else:
234
+ final_dst = dst_path
235
+ if final_dst.exists() and not force:
236
+ try:
237
+ confirm = (
238
+ input(
239
+ f"\nDestination '{final_dst.name}' already exists. Overwrite? [y/n]: "
240
+ )
241
+ .strip()
242
+ .lower()
243
+ )
244
+ except KeyboardInterrupt:
245
+ return (1, "\nMove cancelled by user.\n")
246
+ if confirm != "y":
247
+ return (0, "\nOperation cancelled. No files were moved.\n")
248
+ try:
249
+ shutil.move(str(src_path), str(final_dst))
250
+ return (0, f"\nSUCCESS! Moved '{src_path.name}' -> '{final_dst}'\n")
251
+ except PermissionError:
252
+ return (
253
+ 1,
254
+ f"\nSystem Error: Permission denied. Cannot move to '{final_dst}'\n",
255
+ )
256
+ except shutil.SameFileError:
257
+ return (1, "\nSystem Error: Source and destination are the same.\n")
258
+ except Exception as e:
259
+ return (1, f"\nSystem Error: Failed to move '{raw_src}' ({e})\n")
260
+
261
+
262
+ class Rename(BaseCmd):
263
+ name = "rename"
264
+ description = "Rename a file or directory"
265
+ min_args = 2
266
+ options = "Old name, then new name"
267
+ aliases = []
268
+
269
+ def execute(self, args):
270
+ raw_src = args[0].strip("\"'")
271
+ raw_new_name = args[1].strip("\"'")
272
+ if "/" in raw_new_name or "\\" in raw_new_name:
273
+ return (
274
+ 1,
275
+ "\nCommand Error: The new name must not contain path separators ('/' or '\\')\n"
276
+ "Use 'mv' to move items between different folders.\n",
277
+ )
278
+ src_path = Path(raw_src).expanduser().resolve()
279
+ if not src_path.exists():
280
+ return (1, f"\nSystem Error: Target '{raw_src}' not found.\n")
281
+ flyshell_dirs = [
282
+ c.APP_DIR.resolve(),
283
+ c.PACKAGE_ROOT.resolve(),
284
+ c.PROJECT_ROOT.resolve(),
285
+ ]
286
+ for protected in flyshell_dirs:
287
+ if src_path == protected or src_path.is_relative_to(protected):
288
+ return (
289
+ 1,
290
+ "\nSecurity Error: Flyshell has blocked this action.\n"
291
+ f"Target '{raw_src}' contains or belongs to Flyshell system files.\n",
292
+ )
293
+ new_path = src_path.parent / raw_new_name
294
+ if new_path.exists():
295
+ return (
296
+ 1,
297
+ f"\nSystem Error: An item named '{raw_new_name}' already exists in this folder.\n",
298
+ )
299
+ try:
300
+ src_path.rename(new_path)
301
+ return (0, f"\nSUCCESS! Renamed '{src_path.name}' -> '{new_path.name}'\n")
302
+ except PermissionError:
303
+ return (
304
+ 1,
305
+ f"\nSystem Error: Permission denied. Cannot rename '{raw_src}'\n",
306
+ )
307
+ except Exception as e:
308
+ return (1, f"\nSystem Error: Failed to rename '{raw_src}' ({e})\n")
309
+
310
+
129
311
  class Rm(BaseCmd):
130
312
  name = "rm"
131
313
  description = "Remove files from the filesystem"
@@ -156,18 +338,22 @@ class Rm(BaseCmd):
156
338
  ]
157
339
  if c.HOST_OS == "Windows":
158
340
  system_drive = os.environ.get("SystemDrive", "C:")
159
- critical_system_dirs.extend([
160
- Path(f"{system_drive}\\Windows").resolve(),
161
- Path(f"{system_drive}\\Program Files").resolve(),
162
- Path(f"{system_drive}\\Program Files (x86)").resolve(),
163
- ])
341
+ critical_system_dirs.extend(
342
+ [
343
+ Path(f"{system_drive}\\Windows").resolve(),
344
+ Path(f"{system_drive}\\Program Files").resolve(),
345
+ Path(f"{system_drive}\\Program Files (x86)").resolve(),
346
+ ]
347
+ )
164
348
  else:
165
- critical_system_dirs.extend([
166
- Path("/bin").resolve(),
167
- Path("/etc").resolve(),
168
- Path("/usr").resolve(),
169
- Path("/var").resolve(),
170
- ])
349
+ critical_system_dirs.extend(
350
+ [
351
+ Path("/bin").resolve(),
352
+ Path("/etc").resolve(),
353
+ Path("/usr").resolve(),
354
+ Path("/var").resolve(),
355
+ ]
356
+ )
171
357
  if target_path in critical_system_dirs:
172
358
  return (
173
359
  1,
@@ -212,16 +398,28 @@ class Rm(BaseCmd):
212
398
  try:
213
399
  if is_directory:
214
400
  shutil.rmtree(target_path)
215
- return (0, f"\nSUCCESS! Deleted directory '{raw_target}' and all its contents.\n")
401
+ return (
402
+ 0,
403
+ f"\nSUCCESS! Deleted directory '{raw_target}' and all its contents.\n",
404
+ )
216
405
  else:
217
406
  os.remove(target_path)
218
407
  return (0, f"\nSUCCESS! Deleted file '{raw_target}'\n")
219
408
  except PermissionError:
220
- return (1, f"\nSystem Error: Permission denied. Cannot remove '{raw_target}'\n")
409
+ return (
410
+ 1,
411
+ f"\nSystem Error: Permission denied. Cannot remove '{raw_target}'\n",
412
+ )
221
413
  except OSError as e:
222
- return (1, f"\nSystem Error: Unable to remove '{raw_target}' ({e.strerror})\n")
414
+ return (
415
+ 1,
416
+ f"\nSystem Error: Unable to remove '{raw_target}' ({e.strerror})\n",
417
+ )
223
418
  except Exception as e:
224
- return (1, f"\nSystem Error: Unexpected error removing '{raw_target}' ({e})\n")
419
+ return (
420
+ 1,
421
+ f"\nSystem Error: Unexpected error removing '{raw_target}' ({e})\n",
422
+ )
225
423
 
226
424
 
227
425
  class RmDir(BaseCmd):
@@ -22,6 +22,22 @@ class Clear(BaseCmd):
22
22
  return (0, c.CLEAR_SCREEN)
23
23
 
24
24
 
25
+ class Echo(BaseCmd):
26
+ name = "echo"
27
+ description = "Print arguments to terminal"
28
+ min_args = 1
29
+ options = "Message"
30
+ aliases = []
31
+
32
+ def execute(self, args):
33
+ text = " ".join(args)
34
+ try:
35
+ text = text.encode("utf-8").decode("unicode_escape")
36
+ except Exception:
37
+ pass
38
+ return (0, f"\n{text}\n")
39
+
40
+
25
41
  class History(BaseCmd):
26
42
  name = "history"
27
43
  description = "View command history"
@@ -7,8 +7,8 @@ import os
7
7
  import platform
8
8
  import time
9
9
 
10
- BUILD = 110
11
- VERSION = "1.0.11"
10
+ BUILD = 112
11
+ VERSION = "1.0.13"
12
12
 
13
13
 
14
14
  def _resolve_app_dir() -> Path:
@@ -53,14 +53,18 @@ INSTALLED_COMMANDS: list[BaseCmd] = [
53
53
  system.Cd(),
54
54
  Cmds(),
55
55
  utils.Clear(),
56
+ diskops.Copy(),
56
57
  diskops.Dir(),
58
+ utils.Echo(),
57
59
  fs.Fs(),
58
60
  utils.History(),
59
61
  utils.Kill(),
60
62
  auth.Lock(),
61
63
  diskops.MkDir(),
64
+ diskops.Move(),
62
65
  system.OpenFile(),
63
66
  utils.Reboot(),
67
+ diskops.Rename(),
64
68
  diskops.Rm(),
65
69
  diskops.RmDir(),
66
70
  utils.Sleep(),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flyshell
3
- Version: 1.0.11
3
+ Version: 1.0.13
4
4
  Summary: A modular, zero-dependency command-line environment in Python.
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes