aegis-framework 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/aegis/__init__.py +19 -0
- package/aegis/__pycache__/__init__.cpython-312.pyc +0 -0
- package/aegis/builder/__init__.py +5 -0
- package/aegis/builder/__pycache__/__init__.cpython-312.pyc +0 -0
- package/aegis/builder/__pycache__/builder.cpython-312.pyc +0 -0
- package/aegis/builder/builder.py +301 -0
- package/aegis/cli/__init__.py +5 -0
- package/aegis/cli/__pycache__/__init__.cpython-312.pyc +0 -0
- package/aegis/cli/__pycache__/cli.cpython-312.pyc +0 -0
- package/aegis/cli/cli.py +607 -0
- package/aegis/core/__init__.py +16 -0
- package/aegis/core/__pycache__/__init__.cpython-312.pyc +0 -0
- package/aegis/core/__pycache__/aegis.cpython-312.pyc +0 -0
- package/aegis/core/__pycache__/bridge.cpython-312.pyc +0 -0
- package/aegis/core/__pycache__/window.cpython-312.pyc +0 -0
- package/aegis/core/aegis.py +97 -0
- package/aegis/core/bridge.py +270 -0
- package/aegis/core/preload.py +160 -0
- package/aegis/core/window.py +603 -0
- package/aegis/runtime/__init__.py +1 -0
- package/aegis/runtime/aegis-api.js +519 -0
- package/aegis-cli.py +18 -0
- package/bin/aegis +81 -0
- package/package.json +51 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aegis App - Main Application Class
|
|
3
|
+
High-level API for creating Aegis applications
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
from aegis.core.window import AegisWindow
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AegisApp:
|
|
12
|
+
"""
|
|
13
|
+
Main entry point for Aegis applications
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
app = AegisApp()
|
|
17
|
+
app.run()
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, config_path=None):
|
|
21
|
+
"""
|
|
22
|
+
Initialize Aegis application
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
config_path: Path to aegis.config.json (optional)
|
|
26
|
+
"""
|
|
27
|
+
self.config = self._load_config(config_path)
|
|
28
|
+
self.window = None
|
|
29
|
+
self.project_dir = os.getcwd()
|
|
30
|
+
|
|
31
|
+
def _load_config(self, config_path=None):
|
|
32
|
+
"""Load configuration from aegis.config.json"""
|
|
33
|
+
if config_path is None:
|
|
34
|
+
config_path = 'aegis.config.json'
|
|
35
|
+
|
|
36
|
+
if os.path.exists(config_path):
|
|
37
|
+
with open(config_path, 'r') as f:
|
|
38
|
+
return json.load(f)
|
|
39
|
+
|
|
40
|
+
# Default configuration
|
|
41
|
+
return {
|
|
42
|
+
'name': 'Aegis App',
|
|
43
|
+
'title': 'Aegis App',
|
|
44
|
+
'version': '1.0.0',
|
|
45
|
+
'main': 'index.html',
|
|
46
|
+
'preload': 'preload.js',
|
|
47
|
+
'width': 1200,
|
|
48
|
+
'height': 800,
|
|
49
|
+
'resizable': True,
|
|
50
|
+
'frame': True,
|
|
51
|
+
'devTools': True,
|
|
52
|
+
'contextMenu': True
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def create_window(self, **options):
|
|
56
|
+
"""
|
|
57
|
+
Create a new window with optional overrides
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
**options: Override config options for this window
|
|
61
|
+
"""
|
|
62
|
+
window_config = {**self.config, **options}
|
|
63
|
+
self.window = AegisWindow(window_config)
|
|
64
|
+
|
|
65
|
+
# Inject Aegis API
|
|
66
|
+
preload_path = os.path.join(self.project_dir, self.config.get('preload', 'preload.js'))
|
|
67
|
+
self.window.inject_aegis_api(preload_path)
|
|
68
|
+
|
|
69
|
+
return self.window
|
|
70
|
+
|
|
71
|
+
def run(self):
|
|
72
|
+
"""Start the application"""
|
|
73
|
+
if self.window is None:
|
|
74
|
+
self.create_window()
|
|
75
|
+
|
|
76
|
+
# Load main HTML file
|
|
77
|
+
main_file = os.path.join(self.project_dir, self.config.get('main', 'index.html'))
|
|
78
|
+
self.window.load_file(main_file)
|
|
79
|
+
|
|
80
|
+
# Start the main loop
|
|
81
|
+
self.window.run()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def run_app(config_path=None):
|
|
85
|
+
"""
|
|
86
|
+
Convenience function to run an Aegis app
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
config_path: Optional path to config file
|
|
90
|
+
"""
|
|
91
|
+
app = AegisApp(config_path)
|
|
92
|
+
app.run()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# Allow running as module: python -m aegis.core.aegis
|
|
96
|
+
if __name__ == '__main__':
|
|
97
|
+
run_app()
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aegis Bridge - Python ↔ JavaScript Communication
|
|
3
|
+
Handles IPC messaging between frontend and backend
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import subprocess
|
|
9
|
+
import threading
|
|
10
|
+
from typing import Callable, Dict, Any, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AegisBridge:
|
|
14
|
+
"""
|
|
15
|
+
Manages communication between Python backend and JavaScript frontend
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self._handlers: Dict[str, Callable] = {}
|
|
20
|
+
self._allowed_actions: set = set()
|
|
21
|
+
self._register_default_handlers()
|
|
22
|
+
|
|
23
|
+
def _register_default_handlers(self):
|
|
24
|
+
"""Register built-in action handlers"""
|
|
25
|
+
self.register('read', self._handle_read)
|
|
26
|
+
self.register('write', self._handle_write)
|
|
27
|
+
self.register('run', self._handle_run)
|
|
28
|
+
self.register('exists', self._handle_exists)
|
|
29
|
+
self.register('mkdir', self._handle_mkdir)
|
|
30
|
+
self.register('remove', self._handle_remove)
|
|
31
|
+
self.register('copy', self._handle_copy)
|
|
32
|
+
self.register('move', self._handle_move)
|
|
33
|
+
self.register('env', self._handle_env)
|
|
34
|
+
|
|
35
|
+
def register(self, action: str, handler: Callable):
|
|
36
|
+
"""
|
|
37
|
+
Register a custom action handler
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
action: Action name (e.g., 'myAction')
|
|
41
|
+
handler: Function that takes payload dict and returns result dict
|
|
42
|
+
"""
|
|
43
|
+
self._handlers[action] = handler
|
|
44
|
+
|
|
45
|
+
def allow(self, *actions: str):
|
|
46
|
+
"""
|
|
47
|
+
Set which actions are allowed (preload security)
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
*actions: Action names to allow
|
|
51
|
+
"""
|
|
52
|
+
self._allowed_actions.update(actions)
|
|
53
|
+
|
|
54
|
+
def allow_all(self):
|
|
55
|
+
"""Allow all registered actions"""
|
|
56
|
+
self._allowed_actions = set(self._handlers.keys())
|
|
57
|
+
|
|
58
|
+
def is_allowed(self, action: str) -> bool:
|
|
59
|
+
"""Check if an action is allowed"""
|
|
60
|
+
# If no restrictions set, allow all
|
|
61
|
+
if not self._allowed_actions:
|
|
62
|
+
return True
|
|
63
|
+
return action in self._allowed_actions
|
|
64
|
+
|
|
65
|
+
def process(self, action: str, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
66
|
+
"""
|
|
67
|
+
Process an action request
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
action: Action name
|
|
71
|
+
payload: Action payload
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Result dictionary
|
|
75
|
+
"""
|
|
76
|
+
if not self.is_allowed(action):
|
|
77
|
+
raise PermissionError(f"Action '{action}' is not allowed by preload")
|
|
78
|
+
|
|
79
|
+
handler = self._handlers.get(action)
|
|
80
|
+
if not handler:
|
|
81
|
+
raise ValueError(f"Unknown action: {action}")
|
|
82
|
+
|
|
83
|
+
return handler(payload)
|
|
84
|
+
|
|
85
|
+
# ==================== Built-in Handlers ====================
|
|
86
|
+
|
|
87
|
+
def _handle_read(self, payload: Dict) -> Dict:
|
|
88
|
+
"""Read file contents"""
|
|
89
|
+
path = payload.get('path', '.')
|
|
90
|
+
file = payload.get('file')
|
|
91
|
+
encoding = payload.get('encoding', 'utf-8')
|
|
92
|
+
binary = payload.get('binary', False)
|
|
93
|
+
|
|
94
|
+
if file:
|
|
95
|
+
full_path = os.path.join(path, file)
|
|
96
|
+
else:
|
|
97
|
+
full_path = path
|
|
98
|
+
|
|
99
|
+
if os.path.isdir(full_path):
|
|
100
|
+
# List directory
|
|
101
|
+
entries = []
|
|
102
|
+
for entry in os.listdir(full_path):
|
|
103
|
+
entry_path = os.path.join(full_path, entry)
|
|
104
|
+
stat = os.stat(entry_path)
|
|
105
|
+
entries.append({
|
|
106
|
+
'name': entry,
|
|
107
|
+
'isDirectory': os.path.isdir(entry_path),
|
|
108
|
+
'isFile': os.path.isfile(entry_path),
|
|
109
|
+
'size': stat.st_size,
|
|
110
|
+
'modified': stat.st_mtime
|
|
111
|
+
})
|
|
112
|
+
return {'entries': entries, 'path': full_path}
|
|
113
|
+
else:
|
|
114
|
+
# Read file
|
|
115
|
+
mode = 'rb' if binary else 'r'
|
|
116
|
+
with open(full_path, mode, encoding=None if binary else encoding) as f:
|
|
117
|
+
content = f.read()
|
|
118
|
+
if binary:
|
|
119
|
+
import base64
|
|
120
|
+
content = base64.b64encode(content).decode('ascii')
|
|
121
|
+
return {'content': content, 'path': full_path}
|
|
122
|
+
|
|
123
|
+
def _handle_write(self, payload: Dict) -> Dict:
|
|
124
|
+
"""Write file contents"""
|
|
125
|
+
path = payload.get('path', '.')
|
|
126
|
+
file = payload.get('file')
|
|
127
|
+
content = payload.get('content', '')
|
|
128
|
+
encoding = payload.get('encoding', 'utf-8')
|
|
129
|
+
append = payload.get('append', False)
|
|
130
|
+
binary = payload.get('binary', False)
|
|
131
|
+
|
|
132
|
+
if file:
|
|
133
|
+
full_path = os.path.join(path, file)
|
|
134
|
+
else:
|
|
135
|
+
full_path = path
|
|
136
|
+
|
|
137
|
+
# Create parent directories
|
|
138
|
+
parent = os.path.dirname(full_path)
|
|
139
|
+
if parent:
|
|
140
|
+
os.makedirs(parent, exist_ok=True)
|
|
141
|
+
|
|
142
|
+
mode = 'ab' if append and binary else 'a' if append else 'wb' if binary else 'w'
|
|
143
|
+
|
|
144
|
+
with open(full_path, mode, encoding=None if binary else encoding) as f:
|
|
145
|
+
if binary:
|
|
146
|
+
import base64
|
|
147
|
+
f.write(base64.b64decode(content))
|
|
148
|
+
else:
|
|
149
|
+
f.write(content)
|
|
150
|
+
|
|
151
|
+
return {'success': True, 'path': full_path}
|
|
152
|
+
|
|
153
|
+
def _handle_run(self, payload: Dict) -> Dict:
|
|
154
|
+
"""Execute commands"""
|
|
155
|
+
if 'py' in payload:
|
|
156
|
+
# Execute Python code
|
|
157
|
+
code = payload['py']
|
|
158
|
+
local_vars = {}
|
|
159
|
+
try:
|
|
160
|
+
# Try eval first (for expressions)
|
|
161
|
+
result = eval(code)
|
|
162
|
+
return {'output': str(result), 'exitCode': 0}
|
|
163
|
+
except SyntaxError:
|
|
164
|
+
# Fall back to exec (for statements)
|
|
165
|
+
exec(code, {}, local_vars)
|
|
166
|
+
return {'output': str(local_vars), 'exitCode': 0}
|
|
167
|
+
except Exception as e:
|
|
168
|
+
return {'error': str(e), 'exitCode': 1}
|
|
169
|
+
|
|
170
|
+
elif 'sh' in payload:
|
|
171
|
+
# Execute shell command
|
|
172
|
+
cmd = payload['sh']
|
|
173
|
+
cwd = payload.get('cwd', os.getcwd())
|
|
174
|
+
timeout = payload.get('timeout', None)
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
result = subprocess.run(
|
|
178
|
+
cmd,
|
|
179
|
+
shell=True,
|
|
180
|
+
capture_output=True,
|
|
181
|
+
text=True,
|
|
182
|
+
cwd=cwd,
|
|
183
|
+
timeout=timeout
|
|
184
|
+
)
|
|
185
|
+
return {
|
|
186
|
+
'output': result.stdout,
|
|
187
|
+
'error': result.stderr,
|
|
188
|
+
'exitCode': result.returncode
|
|
189
|
+
}
|
|
190
|
+
except subprocess.TimeoutExpired:
|
|
191
|
+
return {'error': 'Command timed out', 'exitCode': -1}
|
|
192
|
+
except Exception as e:
|
|
193
|
+
return {'error': str(e), 'exitCode': -1}
|
|
194
|
+
|
|
195
|
+
return {'error': 'No py or sh command specified', 'exitCode': -1}
|
|
196
|
+
|
|
197
|
+
def _handle_exists(self, payload: Dict) -> Dict:
|
|
198
|
+
"""Check if path exists"""
|
|
199
|
+
path = payload.get('path')
|
|
200
|
+
return {
|
|
201
|
+
'exists': os.path.exists(path),
|
|
202
|
+
'isFile': os.path.isfile(path),
|
|
203
|
+
'isDirectory': os.path.isdir(path)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
def _handle_mkdir(self, payload: Dict) -> Dict:
|
|
207
|
+
"""Create directory"""
|
|
208
|
+
path = payload.get('path')
|
|
209
|
+
recursive = payload.get('recursive', True)
|
|
210
|
+
|
|
211
|
+
if recursive:
|
|
212
|
+
os.makedirs(path, exist_ok=True)
|
|
213
|
+
else:
|
|
214
|
+
os.mkdir(path)
|
|
215
|
+
|
|
216
|
+
return {'success': True, 'path': path}
|
|
217
|
+
|
|
218
|
+
def _handle_remove(self, payload: Dict) -> Dict:
|
|
219
|
+
"""Remove file or directory"""
|
|
220
|
+
import shutil
|
|
221
|
+
path = payload.get('path')
|
|
222
|
+
recursive = payload.get('recursive', False)
|
|
223
|
+
|
|
224
|
+
if os.path.isdir(path):
|
|
225
|
+
if recursive:
|
|
226
|
+
shutil.rmtree(path)
|
|
227
|
+
else:
|
|
228
|
+
os.rmdir(path)
|
|
229
|
+
else:
|
|
230
|
+
os.remove(path)
|
|
231
|
+
|
|
232
|
+
return {'success': True}
|
|
233
|
+
|
|
234
|
+
def _handle_copy(self, payload: Dict) -> Dict:
|
|
235
|
+
"""Copy file or directory"""
|
|
236
|
+
import shutil
|
|
237
|
+
src = payload.get('src')
|
|
238
|
+
dest = payload.get('dest')
|
|
239
|
+
|
|
240
|
+
if os.path.isdir(src):
|
|
241
|
+
shutil.copytree(src, dest)
|
|
242
|
+
else:
|
|
243
|
+
shutil.copy2(src, dest)
|
|
244
|
+
|
|
245
|
+
return {'success': True, 'dest': dest}
|
|
246
|
+
|
|
247
|
+
def _handle_move(self, payload: Dict) -> Dict:
|
|
248
|
+
"""Move/rename file or directory"""
|
|
249
|
+
import shutil
|
|
250
|
+
src = payload.get('src')
|
|
251
|
+
dest = payload.get('dest')
|
|
252
|
+
|
|
253
|
+
shutil.move(src, dest)
|
|
254
|
+
return {'success': True, 'dest': dest}
|
|
255
|
+
|
|
256
|
+
def _handle_env(self, payload: Dict) -> Dict:
|
|
257
|
+
"""Get/set environment variables"""
|
|
258
|
+
name = payload.get('name')
|
|
259
|
+
value = payload.get('value')
|
|
260
|
+
|
|
261
|
+
if value is not None:
|
|
262
|
+
# Set environment variable
|
|
263
|
+
os.environ[name] = value
|
|
264
|
+
return {'success': True}
|
|
265
|
+
elif name:
|
|
266
|
+
# Get single variable
|
|
267
|
+
return {'value': os.environ.get(name)}
|
|
268
|
+
else:
|
|
269
|
+
# Get all variables
|
|
270
|
+
return {'env': dict(os.environ)}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aegis Preload Security System
|
|
3
|
+
Parses preload.js to determine which APIs should be exposed
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from typing import Set, Dict, Any, List
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PreloadManager:
|
|
13
|
+
"""
|
|
14
|
+
Manages the preload security configuration
|
|
15
|
+
|
|
16
|
+
The preload.js file defines which Aegis APIs are exposed to the frontend.
|
|
17
|
+
This provides a security layer to prevent malicious code from accessing
|
|
18
|
+
sensitive system operations.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.allowed_apis: Set[str] = set()
|
|
23
|
+
self.custom_handlers: Dict[str, str] = {}
|
|
24
|
+
self.config: Dict[str, Any] = {}
|
|
25
|
+
|
|
26
|
+
def load(self, preload_path: str) -> bool:
|
|
27
|
+
"""
|
|
28
|
+
Load and parse a preload.js file
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
preload_path: Path to preload.js
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
True if loaded successfully
|
|
35
|
+
"""
|
|
36
|
+
if not os.path.exists(preload_path):
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
with open(preload_path, 'r') as f:
|
|
40
|
+
content = f.read()
|
|
41
|
+
|
|
42
|
+
self._parse_preload(content)
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
def _parse_preload(self, content: str):
|
|
46
|
+
"""Parse preload.js content to extract configuration"""
|
|
47
|
+
|
|
48
|
+
# Look for Aegis.expose() calls
|
|
49
|
+
# Aegis.expose(['read', 'write', 'run'])
|
|
50
|
+
expose_pattern = r'Aegis\.expose\s*\(\s*\[(.*?)\]\s*\)'
|
|
51
|
+
matches = re.findall(expose_pattern, content, re.DOTALL)
|
|
52
|
+
|
|
53
|
+
for match in matches:
|
|
54
|
+
# Extract API names from the array
|
|
55
|
+
apis = re.findall(r'["\'](\w+(?:\.\w+)*)["\']', match)
|
|
56
|
+
self.allowed_apis.update(apis)
|
|
57
|
+
|
|
58
|
+
# Look for Aegis.exposeAll() - expose everything
|
|
59
|
+
if 'Aegis.exposeAll()' in content:
|
|
60
|
+
self.allowed_apis = {'*'}
|
|
61
|
+
|
|
62
|
+
# Look for Aegis.config() calls
|
|
63
|
+
config_pattern = r'Aegis\.config\s*\(\s*(\{.*?\})\s*\)'
|
|
64
|
+
config_matches = re.findall(config_pattern, content, re.DOTALL)
|
|
65
|
+
|
|
66
|
+
for match in config_matches:
|
|
67
|
+
try:
|
|
68
|
+
# Try to parse as JSON (basic JS object syntax)
|
|
69
|
+
# Replace single quotes with double quotes for JSON
|
|
70
|
+
json_str = match.replace("'", '"')
|
|
71
|
+
self.config.update(json.loads(json_str))
|
|
72
|
+
except json.JSONDecodeError:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
# Look for custom handler definitions
|
|
76
|
+
# Aegis.handle('myAction', async (data) => { ... })
|
|
77
|
+
handler_pattern = r'Aegis\.handle\s*\(\s*["\'](\w+)["\']\s*,'
|
|
78
|
+
handler_matches = re.findall(handler_pattern, content)
|
|
79
|
+
|
|
80
|
+
for handler_name in handler_matches:
|
|
81
|
+
self.custom_handlers[handler_name] = content
|
|
82
|
+
|
|
83
|
+
def is_api_allowed(self, api_name: str) -> bool:
|
|
84
|
+
"""
|
|
85
|
+
Check if an API is allowed
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
api_name: Full API name (e.g., 'read', 'dialog.open')
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
True if the API is allowed
|
|
92
|
+
"""
|
|
93
|
+
# If exposeAll was used, everything is allowed
|
|
94
|
+
if '*' in self.allowed_apis:
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
# Check exact match
|
|
98
|
+
if api_name in self.allowed_apis:
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
# Check namespace match (e.g., 'dialog' allows 'dialog.open')
|
|
102
|
+
namespace = api_name.split('.')[0]
|
|
103
|
+
if namespace in self.allowed_apis:
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
# If no APIs are explicitly allowed, allow all by default
|
|
107
|
+
# (for backwards compatibility and ease of use)
|
|
108
|
+
if not self.allowed_apis:
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
def get_allowed_list(self) -> List[str]:
|
|
114
|
+
"""Get list of allowed APIs"""
|
|
115
|
+
return list(self.allowed_apis)
|
|
116
|
+
|
|
117
|
+
def generate_js_config(self) -> str:
|
|
118
|
+
"""Generate JavaScript configuration for the frontend"""
|
|
119
|
+
if '*' in self.allowed_apis:
|
|
120
|
+
allowed_js = '["*"]'
|
|
121
|
+
else:
|
|
122
|
+
allowed_js = json.dumps(list(self.allowed_apis))
|
|
123
|
+
|
|
124
|
+
return f"""
|
|
125
|
+
// Aegis Preload Configuration (auto-generated)
|
|
126
|
+
window.__aegisAllowedAPIs = {allowed_js};
|
|
127
|
+
window.__aegisConfig = {json.dumps(self.config)};
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def create_default_preload() -> str:
|
|
132
|
+
"""Generate default preload.js content"""
|
|
133
|
+
return '''/**
|
|
134
|
+
* Aegis Preload Configuration
|
|
135
|
+
*
|
|
136
|
+
* This file controls which Aegis APIs are exposed to your frontend.
|
|
137
|
+
* For security, only expose the APIs you actually need.
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
// Expose specific APIs
|
|
141
|
+
Aegis.expose([
|
|
142
|
+
'read', // File reading
|
|
143
|
+
'write', // File writing
|
|
144
|
+
'run', // Command execution
|
|
145
|
+
'dialog', // Native dialogs (open, save, message)
|
|
146
|
+
'app', // App control (quit, minimize, maximize)
|
|
147
|
+
'exists', // Check file/directory existence
|
|
148
|
+
'mkdir', // Create directories
|
|
149
|
+
'env' // Environment variables
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
// Or expose everything (not recommended for production):
|
|
153
|
+
// Aegis.exposeAll();
|
|
154
|
+
|
|
155
|
+
// Optional: Configure Aegis behavior
|
|
156
|
+
Aegis.config({
|
|
157
|
+
allowRemoteContent: false,
|
|
158
|
+
enableDevTools: true
|
|
159
|
+
});
|
|
160
|
+
'''
|