flyshell 1.0.11__tar.gz → 1.0.12__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.12}/PKG-INFO +1 -1
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/builtin/diskops.py +164 -15
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/constants.py +2 -2
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/core/directory.py +2 -0
- {flyshell-1.0.11 → flyshell-1.0.12/src/flyshell.egg-info}/PKG-INFO +1 -1
- {flyshell-1.0.11 → flyshell-1.0.12}/LICENSE +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/README.md +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/pyproject.toml +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/setup.cfg +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/__init__.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/abstract/__init__.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/abstract/base_cmd.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/abstract/base_plugin.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/abstract/parent_cmd.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/builtin/__init__.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/builtin/fs.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/builtin/system.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/builtin/utils.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/core/__init__.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/core/auth.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/core/console.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/core/data.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/core/loader.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell/main.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell.egg-info/SOURCES.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell.egg-info/dependency_links.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell.egg-info/entry_points.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell.egg-info/requires.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/src/flyshell.egg-info/top_level.txt +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/tests/test_auth.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/tests/test_data.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/tests/test_directory.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/tests/test_loader.py +0 -0
- {flyshell-1.0.11 → flyshell-1.0.12}/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,76 @@ 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
|
+
|
|
129
262
|
class Rm(BaseCmd):
|
|
130
263
|
name = "rm"
|
|
131
264
|
description = "Remove files from the filesystem"
|
|
@@ -156,18 +289,22 @@ class Rm(BaseCmd):
|
|
|
156
289
|
]
|
|
157
290
|
if c.HOST_OS == "Windows":
|
|
158
291
|
system_drive = os.environ.get("SystemDrive", "C:")
|
|
159
|
-
critical_system_dirs.extend(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
292
|
+
critical_system_dirs.extend(
|
|
293
|
+
[
|
|
294
|
+
Path(f"{system_drive}\\Windows").resolve(),
|
|
295
|
+
Path(f"{system_drive}\\Program Files").resolve(),
|
|
296
|
+
Path(f"{system_drive}\\Program Files (x86)").resolve(),
|
|
297
|
+
]
|
|
298
|
+
)
|
|
164
299
|
else:
|
|
165
|
-
critical_system_dirs.extend(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
300
|
+
critical_system_dirs.extend(
|
|
301
|
+
[
|
|
302
|
+
Path("/bin").resolve(),
|
|
303
|
+
Path("/etc").resolve(),
|
|
304
|
+
Path("/usr").resolve(),
|
|
305
|
+
Path("/var").resolve(),
|
|
306
|
+
]
|
|
307
|
+
)
|
|
171
308
|
if target_path in critical_system_dirs:
|
|
172
309
|
return (
|
|
173
310
|
1,
|
|
@@ -212,16 +349,28 @@ class Rm(BaseCmd):
|
|
|
212
349
|
try:
|
|
213
350
|
if is_directory:
|
|
214
351
|
shutil.rmtree(target_path)
|
|
215
|
-
return (
|
|
352
|
+
return (
|
|
353
|
+
0,
|
|
354
|
+
f"\nSUCCESS! Deleted directory '{raw_target}' and all its contents.\n",
|
|
355
|
+
)
|
|
216
356
|
else:
|
|
217
357
|
os.remove(target_path)
|
|
218
358
|
return (0, f"\nSUCCESS! Deleted file '{raw_target}'\n")
|
|
219
359
|
except PermissionError:
|
|
220
|
-
return (
|
|
360
|
+
return (
|
|
361
|
+
1,
|
|
362
|
+
f"\nSystem Error: Permission denied. Cannot remove '{raw_target}'\n",
|
|
363
|
+
)
|
|
221
364
|
except OSError as e:
|
|
222
|
-
return (
|
|
365
|
+
return (
|
|
366
|
+
1,
|
|
367
|
+
f"\nSystem Error: Unable to remove '{raw_target}' ({e.strerror})\n",
|
|
368
|
+
)
|
|
223
369
|
except Exception as e:
|
|
224
|
-
return (
|
|
370
|
+
return (
|
|
371
|
+
1,
|
|
372
|
+
f"\nSystem Error: Unexpected error removing '{raw_target}' ({e})\n",
|
|
373
|
+
)
|
|
225
374
|
|
|
226
375
|
|
|
227
376
|
class RmDir(BaseCmd):
|
|
@@ -53,12 +53,14 @@ INSTALLED_COMMANDS: list[BaseCmd] = [
|
|
|
53
53
|
system.Cd(),
|
|
54
54
|
Cmds(),
|
|
55
55
|
utils.Clear(),
|
|
56
|
+
diskops.Copy(),
|
|
56
57
|
diskops.Dir(),
|
|
57
58
|
fs.Fs(),
|
|
58
59
|
utils.History(),
|
|
59
60
|
utils.Kill(),
|
|
60
61
|
auth.Lock(),
|
|
61
62
|
diskops.MkDir(),
|
|
63
|
+
diskops.Move(),
|
|
62
64
|
system.OpenFile(),
|
|
63
65
|
utils.Reboot(),
|
|
64
66
|
diskops.Rm(),
|
|
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
|
|
File without changes
|