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.
- {flyshell-1.0.11/src/flyshell.egg-info → flyshell-1.0.13}/PKG-INFO +1 -1
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/diskops.py +213 -15
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/utils.py +16 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/constants.py +2 -2
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/directory.py +4 -0
- {flyshell-1.0.11 → flyshell-1.0.13/src/flyshell.egg-info}/PKG-INFO +1 -1
- {flyshell-1.0.11 → flyshell-1.0.13}/LICENSE +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/README.md +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/pyproject.toml +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/setup.cfg +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/__init__.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/abstract/__init__.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/abstract/base_cmd.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/abstract/base_plugin.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/abstract/parent_cmd.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/__init__.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/fs.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/builtin/system.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/__init__.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/auth.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/console.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/data.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/core/loader.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell/main.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/SOURCES.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/dependency_links.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/entry_points.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/requires.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/src/flyshell.egg-info/top_level.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_auth.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_data.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_directory.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_loader.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.13}/tests/test_security.py +0 -0
|
@@ -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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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 (
|
|
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 (
|
|
409
|
+
return (
|
|
410
|
+
1,
|
|
411
|
+
f"\nSystem Error: Permission denied. Cannot remove '{raw_target}'\n",
|
|
412
|
+
)
|
|
221
413
|
except OSError as e:
|
|
222
|
-
return (
|
|
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 (
|
|
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"
|
|
@@ -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(),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|