aegis-framework 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -181,7 +181,51 @@ console.log(result.output);
181
181
  // Python code
182
182
  const py = await Aegis.run({ py: '2 + 2' });
183
183
  console.log(py.output); // "4"
184
+
185
+ // Async command with streaming output (UI won't freeze!)
186
+ await Aegis.runAsync(
187
+ { sh: 'apt update' },
188
+ (progress) => console.log(progress.line)
189
+ );
190
+ ```
191
+
192
+ ### Downloads (with aria2c turbo mode!)
193
+
194
+ Aegis uses **aria2c** for blazing fast downloads with multiple connections:
195
+
196
+ ```javascript
197
+ // Basic download with progress
198
+ await Aegis.download(
199
+ {
200
+ url: 'https://example.com/large-file.zip',
201
+ dest: '/home/user/downloads/file.zip'
202
+ },
203
+ (progress) => {
204
+ console.log(`${progress.percent.toFixed(1)}%`);
205
+ console.log(`Speed: ${progress.speed}`); // "5.2 MiB/s"
206
+ console.log(`Engine: ${progress.engine}`); // "aria2c" or "urllib"
207
+ }
208
+ );
209
+
210
+ // Turbo mode with 16 connections (for fast internet)
211
+ await Aegis.download(
212
+ {
213
+ url: 'https://example.com/game.iso',
214
+ dest: '/home/user/games/game.iso',
215
+ connections: 16 // Up to 16 simultaneous connections!
216
+ },
217
+ (progress) => {
218
+ progressBar.style.width = progress.percent + '%';
219
+ speedLabel.textContent = progress.speed;
220
+ }
221
+ );
222
+ ```
223
+
224
+ **Note:** For maximum speed, install aria2c:
225
+ ```bash
226
+ sudo apt install aria2
184
227
  ```
228
+ If aria2c is not installed, Aegis automatically falls back to standard downloads.
185
229
 
186
230
  ## 🔒 Security (preload.js)
187
231
 
@@ -677,45 +677,140 @@ class AegisWindow(Gtk.Window):
677
677
  GLib.idle_add(self._send_error, callback_id, str(e))
678
678
 
679
679
  def _handle_download(self, payload, callback_id):
680
- """Download file with progress updates"""
680
+ """Download file with progress updates - uses aria2c if available for faster downloads"""
681
+ url = payload.get('url')
682
+ dest = payload.get('dest')
683
+ connections = payload.get('connections', 8) # Max simultaneous connections
684
+
681
685
  try:
682
- url = payload.get('url')
683
- dest = payload.get('dest')
686
+ # Ensure destination directory exists
687
+ os.makedirs(os.path.dirname(dest) or '.', exist_ok=True)
688
+
689
+ # Check if aria2c is available for faster downloads
690
+ if shutil.which('aria2c'):
691
+ self._download_with_aria2(url, dest, connections, callback_id)
692
+ else:
693
+ self._download_with_urllib(url, dest, callback_id)
694
+
695
+ except Exception as e:
696
+ GLib.idle_add(self._send_error, callback_id, str(e))
697
+
698
+ def _download_with_aria2(self, url, dest, connections, callback_id):
699
+ """Download using aria2c for multi-connection speed boost"""
700
+ import subprocess
701
+ import re
702
+
703
+ try:
704
+ dest_dir = os.path.dirname(dest) or '.'
705
+ dest_file = os.path.basename(dest)
706
+
707
+ cmd = [
708
+ 'aria2c',
709
+ '-x', str(connections), # Max connections per server
710
+ '-s', str(connections), # Split file into N parts
711
+ '-k', '1M', # Min split size
712
+ '--console-log-level=warn',
713
+ '--summary-interval=1',
714
+ '--download-result=hide',
715
+ '-d', dest_dir,
716
+ '-o', dest_file,
717
+ '--allow-overwrite=true',
718
+ url
719
+ ]
720
+
721
+ process = subprocess.Popen(
722
+ cmd,
723
+ stdout=subprocess.PIPE,
724
+ stderr=subprocess.STDOUT,
725
+ text=True,
726
+ bufsize=1
727
+ )
684
728
 
685
- # Create request
729
+ total_size = 0
730
+ downloaded = 0
731
+
732
+ for line in process.stdout:
733
+ # Parse aria2c progress: [#abc123 1.2MiB/50MiB(2%) CN:8 DL:5.2MiB]
734
+ progress_match = re.search(r'\[#\w+\s+([\d.]+)(\w+)/([\d.]+)(\w+)\s*\((\d+)%\).*DL:([\d.]+)(\w+)', line)
735
+ if progress_match:
736
+ dl_num, dl_unit, total_num, total_unit, percent, speed_num, speed_unit = progress_match.groups()
737
+
738
+ # Convert to bytes
739
+ def to_bytes(num, unit):
740
+ multipliers = {'B': 1, 'KiB': 1024, 'MiB': 1024**2, 'GiB': 1024**3}
741
+ return float(num) * multipliers.get(unit, 1)
742
+
743
+ downloaded = int(to_bytes(dl_num, dl_unit))
744
+ total_size = int(to_bytes(total_num, total_unit))
745
+ speed = f"{speed_num} {speed_unit}/s"
746
+
747
+ progress = {
748
+ 'downloaded': downloaded,
749
+ 'total': total_size,
750
+ 'percent': float(percent),
751
+ 'speed': speed,
752
+ 'connections': connections,
753
+ 'engine': 'aria2c'
754
+ }
755
+ GLib.idle_add(self._send_progress, callback_id, progress)
756
+
757
+ process.wait()
758
+
759
+ if process.returncode == 0:
760
+ # Get final file size
761
+ final_size = os.path.getsize(dest) if os.path.exists(dest) else downloaded
762
+ result = {
763
+ 'success': True,
764
+ 'path': dest,
765
+ 'size': final_size,
766
+ 'engine': 'aria2c'
767
+ }
768
+ GLib.idle_add(self._send_response, callback_id, result)
769
+ else:
770
+ # aria2c failed (e.g., snap sandbox restrictions) - fallback to urllib
771
+ print(f"[Aegis] aria2c failed (code {process.returncode}), falling back to urllib")
772
+ self._download_with_urllib(url, dest, callback_id)
773
+
774
+ except Exception as e:
775
+ # On any error, fallback to urllib
776
+ print(f"[Aegis] aria2c error: {e}, falling back to urllib")
777
+ self._download_with_urllib(url, dest, callback_id)
778
+
779
+ def _download_with_urllib(self, url, dest, callback_id):
780
+ """Fallback download using urllib (single connection)"""
781
+ try:
686
782
  req = urllib.request.Request(url, headers={
687
783
  'User-Agent': 'Aegis/0.1.0'
688
784
  })
689
785
 
690
- response = urllib.request.urlopen(req, timeout=30)
786
+ response = urllib.request.urlopen(req, timeout=60)
691
787
  total_size = int(response.headers.get('Content-Length', 0))
692
788
  downloaded = 0
693
789
 
694
- # Ensure destination directory exists
695
- os.makedirs(os.path.dirname(dest) or '.', exist_ok=True)
696
-
697
790
  with open(dest, 'wb') as f:
698
791
  while True:
699
- chunk = response.read(8192)
792
+ chunk = response.read(65536) # 64KB chunks
700
793
  if not chunk:
701
794
  break
702
795
 
703
796
  f.write(chunk)
704
797
  downloaded += len(chunk)
705
798
 
706
- # Send progress update
707
799
  progress = {
708
800
  'downloaded': downloaded,
709
801
  'total': total_size,
710
- 'percent': (downloaded / total_size * 100) if total_size else 0
802
+ 'percent': (downloaded / total_size * 100) if total_size else 0,
803
+ 'speed': None,
804
+ 'connections': 1,
805
+ 'engine': 'urllib'
711
806
  }
712
807
  GLib.idle_add(self._send_progress, callback_id, progress)
713
808
 
714
- # Send completion
715
809
  result = {
716
810
  'success': True,
717
811
  'path': dest,
718
- 'size': downloaded
812
+ 'size': downloaded,
813
+ 'engine': 'urllib'
719
814
  }
720
815
  GLib.idle_add(self._send_response, callback_id, result)
721
816
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aegis-framework",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Lightweight AppImage framework using WebKit2GTK and Python - An alternative to Electron that creates ~200KB apps instead of 150MB!",
5
5
  "keywords": [
6
6
  "appimage",