abstractassistant 0.2.5__tar.gz → 0.2.6__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 (61) hide show
  1. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/PKG-INFO +26 -4
  2. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/README.md +24 -1
  3. abstractassistant-0.2.6/abstractassistant/create_app_bundle.py +48 -0
  4. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant.egg-info/PKG-INFO +26 -4
  5. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant.egg-info/SOURCES.txt +2 -0
  6. abstractassistant-0.2.6/abstractassistant.egg-info/entry_points.txt +3 -0
  7. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant.egg-info/top_level.txt +1 -0
  8. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/pyproject.toml +6 -3
  9. abstractassistant-0.2.6/setup_macos_app.py +269 -0
  10. abstractassistant-0.2.5/abstractassistant.egg-info/entry_points.txt +0 -2
  11. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/LICENSE +0 -0
  12. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/__init__.py +0 -0
  13. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/app.py +0 -0
  14. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/cli.py +0 -0
  15. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/config.py +0 -0
  16. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/core/__init__.py +0 -0
  17. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/core/llm_manager.py +0 -0
  18. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/core/tts_manager.py +0 -0
  19. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/__init__.py +0 -0
  20. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/chat_bubble.py +0 -0
  21. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/history_dialog.py +0 -0
  22. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/provider_manager.py +0 -0
  23. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/qt_bubble.py +0 -0
  24. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/toast_manager.py +0 -0
  25. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/toast_window.py +0 -0
  26. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/tts_state_manager.py +0 -0
  27. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/ui/ui_styles.py +0 -0
  28. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/utils/__init__.py +0 -0
  29. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/utils/icon_generator.py +0 -0
  30. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/utils/markdown_renderer.py +0 -0
  31. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant/web_server.py +0 -0
  32. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant.egg-info/dependency_links.txt +0 -0
  33. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/abstractassistant.egg-info/requires.txt +0 -0
  34. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/setup.cfg +0 -0
  35. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_abstractcore.py +0 -0
  36. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_app.py +0 -0
  37. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_bubble.py +0 -0
  38. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_bubble_manual.py +0 -0
  39. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_complete_voice_integration.py +0 -0
  40. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_complete_working.py +0 -0
  41. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_corrected_logic.py +0 -0
  42. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_double_click_fix.py +0 -0
  43. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_final_app_demo.py +0 -0
  44. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_final_verification.py +0 -0
  45. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_final_voice_mode.py +0 -0
  46. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_fixed_app.py +0 -0
  47. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_fixed_integration.py +0 -0
  48. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_full_voice_mode.py +0 -0
  49. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_integration.py +0 -0
  50. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_qt_bubble.py +0 -0
  51. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_qt_threading.py +0 -0
  52. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_real_application.py +0 -0
  53. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_reason_values.py +0 -0
  54. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_safe_startup.py +0 -0
  55. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_safe_voice_controls.py +0 -0
  56. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_simple.py +0 -0
  57. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_system_tray.py +0 -0
  58. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_timestamp_clicks.py +0 -0
  59. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_voice_double_click.py +0 -0
  60. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_voice_features.py +0 -0
  61. {abstractassistant-0.2.5 → abstractassistant-0.2.6}/tests/test_voice_timing.py +0 -0
@@ -1,16 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractassistant
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: A sleek (macOS) system tray application providing instant access to LLMs
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/lpalbou/abstractassistant
8
8
  Project-URL: Repository, https://github.com/lpalbou/abstractassistant
9
9
  Project-URL: Issues, https://github.com/lpalbou/abstractassistant/issues
10
10
  Keywords: ai,llm,macos,system-tray,assistant
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: End Users/Desktop
13
- Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Operating System :: MacOS
15
14
  Classifier: Programming Language :: Python :: 3
16
15
  Classifier: Programming Language :: Python :: 3.9
@@ -67,17 +66,40 @@ A sleek macOS system tray application providing instant access to Large Language
67
66
  ## 🚀 Quick Start
68
67
 
69
68
  ### 1. Installation
69
+
70
+ #### 🍎 macOS Users (Recommended)
71
+ ```bash
72
+ # Enhanced installation with Dock integration
73
+ python3 install.py
74
+ ```
75
+
76
+ This will:
77
+ - Install AbstractAssistant from PyPI
78
+ - Create a macOS app bundle in `/Applications`
79
+ - Add AbstractAssistant to your Dock for easy access
80
+
81
+ #### 🔧 Standard Installation
70
82
  ```bash
71
- # Install from PyPI (recommended)
83
+ # Install from PyPI
72
84
  pip install abstractassistant
73
85
  ```
74
86
 
75
87
  For detailed installation instructions including prerequisites and voice setup, see **[📖 Installation Guide](docs/installation.md)**.
76
88
 
77
89
  ### 2. First Launch
90
+
91
+ #### 🍎 macOS App Bundle Users
92
+ - **Dock**: Click the AbstractAssistant icon in your Dock
93
+ - **Spotlight**: Search for "AbstractAssistant" and press Enter
94
+ - **Finder**: Open `/Applications/AbstractAssistant.app`
95
+
96
+ #### 🔧 Terminal Users
78
97
  ```bash
79
98
  # Launch the assistant
80
99
  assistant
100
+
101
+ # Or create macOS app bundle after installation
102
+ create-app-bundle
81
103
  ```
82
104
 
83
105
  ### 3. Start Using
@@ -24,17 +24,40 @@ A sleek macOS system tray application providing instant access to Large Language
24
24
  ## 🚀 Quick Start
25
25
 
26
26
  ### 1. Installation
27
+
28
+ #### 🍎 macOS Users (Recommended)
29
+ ```bash
30
+ # Enhanced installation with Dock integration
31
+ python3 install.py
32
+ ```
33
+
34
+ This will:
35
+ - Install AbstractAssistant from PyPI
36
+ - Create a macOS app bundle in `/Applications`
37
+ - Add AbstractAssistant to your Dock for easy access
38
+
39
+ #### 🔧 Standard Installation
27
40
  ```bash
28
- # Install from PyPI (recommended)
41
+ # Install from PyPI
29
42
  pip install abstractassistant
30
43
  ```
31
44
 
32
45
  For detailed installation instructions including prerequisites and voice setup, see **[📖 Installation Guide](docs/installation.md)**.
33
46
 
34
47
  ### 2. First Launch
48
+
49
+ #### 🍎 macOS App Bundle Users
50
+ - **Dock**: Click the AbstractAssistant icon in your Dock
51
+ - **Spotlight**: Search for "AbstractAssistant" and press Enter
52
+ - **Finder**: Open `/Applications/AbstractAssistant.app`
53
+
54
+ #### 🔧 Terminal Users
35
55
  ```bash
36
56
  # Launch the assistant
37
57
  assistant
58
+
59
+ # Or create macOS app bundle after installation
60
+ create-app-bundle
38
61
  ```
39
62
 
40
63
  ### 3. Start Using
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Create macOS App Bundle for AbstractAssistant.
4
+
5
+ This script can be run after installation to create a macOS app bundle
6
+ that allows launching AbstractAssistant from the Dock.
7
+ """
8
+
9
+ import sys
10
+ from pathlib import Path
11
+
12
+
13
+ def main():
14
+ """Create macOS app bundle for AbstractAssistant."""
15
+ try:
16
+ # Import the app bundle generator
17
+ from abstractassistant.setup_macos_app import MacOSAppBundleGenerator
18
+
19
+ # Find the package directory
20
+ import abstractassistant
21
+ package_dir = Path(abstractassistant.__file__).parent
22
+
23
+ # Create the generator and build the app bundle
24
+ generator = MacOSAppBundleGenerator(package_dir)
25
+
26
+ print("🍎 Creating macOS app bundle for AbstractAssistant...")
27
+ success = generator.generate_app_bundle()
28
+
29
+ if success:
30
+ print("\n🎉 Success!")
31
+ print(" AbstractAssistant is now available in your Applications folder")
32
+ print(" You can launch it from the Dock or Spotlight!")
33
+ return 0
34
+ else:
35
+ print("\n❌ Failed to create app bundle")
36
+ return 1
37
+
38
+ except ImportError as e:
39
+ print(f"❌ Error: {e}")
40
+ print(" Make sure AbstractAssistant is properly installed")
41
+ return 1
42
+ except Exception as e:
43
+ print(f"❌ Unexpected error: {e}")
44
+ return 1
45
+
46
+
47
+ if __name__ == "__main__":
48
+ sys.exit(main())
@@ -1,16 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractassistant
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: A sleek (macOS) system tray application providing instant access to LLMs
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/lpalbou/abstractassistant
8
8
  Project-URL: Repository, https://github.com/lpalbou/abstractassistant
9
9
  Project-URL: Issues, https://github.com/lpalbou/abstractassistant/issues
10
10
  Keywords: ai,llm,macos,system-tray,assistant
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: End Users/Desktop
13
- Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Operating System :: MacOS
15
14
  Classifier: Programming Language :: Python :: 3
16
15
  Classifier: Programming Language :: Python :: 3.9
@@ -67,17 +66,40 @@ A sleek macOS system tray application providing instant access to Large Language
67
66
  ## 🚀 Quick Start
68
67
 
69
68
  ### 1. Installation
69
+
70
+ #### 🍎 macOS Users (Recommended)
71
+ ```bash
72
+ # Enhanced installation with Dock integration
73
+ python3 install.py
74
+ ```
75
+
76
+ This will:
77
+ - Install AbstractAssistant from PyPI
78
+ - Create a macOS app bundle in `/Applications`
79
+ - Add AbstractAssistant to your Dock for easy access
80
+
81
+ #### 🔧 Standard Installation
70
82
  ```bash
71
- # Install from PyPI (recommended)
83
+ # Install from PyPI
72
84
  pip install abstractassistant
73
85
  ```
74
86
 
75
87
  For detailed installation instructions including prerequisites and voice setup, see **[📖 Installation Guide](docs/installation.md)**.
76
88
 
77
89
  ### 2. First Launch
90
+
91
+ #### 🍎 macOS App Bundle Users
92
+ - **Dock**: Click the AbstractAssistant icon in your Dock
93
+ - **Spotlight**: Search for "AbstractAssistant" and press Enter
94
+ - **Finder**: Open `/Applications/AbstractAssistant.app`
95
+
96
+ #### 🔧 Terminal Users
78
97
  ```bash
79
98
  # Launch the assistant
80
99
  assistant
100
+
101
+ # Or create macOS app bundle after installation
102
+ create-app-bundle
81
103
  ```
82
104
 
83
105
  ### 3. Start Using
@@ -1,10 +1,12 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
+ setup_macos_app.py
4
5
  abstractassistant/__init__.py
5
6
  abstractassistant/app.py
6
7
  abstractassistant/cli.py
7
8
  abstractassistant/config.py
9
+ abstractassistant/create_app_bundle.py
8
10
  abstractassistant/web_server.py
9
11
  abstractassistant.egg-info/PKG-INFO
10
12
  abstractassistant.egg-info/SOURCES.txt
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ assistant = abstractassistant.cli:main
3
+ create-app-bundle = abstractassistant.create_app_bundle:main
@@ -1 +1,2 @@
1
1
  abstractassistant
2
+ setup_macos_app
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "abstractassistant"
7
- version = "0.2.5"
7
+ version = "0.2.6"
8
8
  description = "A sleek (macOS) system tray application providing instant access to LLMs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
- license = {text = "MIT"}
11
+ license = "MIT"
12
12
  authors = [
13
13
  {name = "Laurent-Philippe Albou", email = "contact@abstractcore.ai"}
14
14
  ]
@@ -16,7 +16,6 @@ keywords = ["ai", "llm", "macos", "system-tray", "assistant"]
16
16
  classifiers = [
17
17
  "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: End Users/Desktop",
19
- "License :: OSI Approved :: MIT License",
20
19
  "Operating System :: MacOS",
21
20
  "Programming Language :: Python :: 3",
22
21
  "Programming Language :: Python :: 3.9",
@@ -52,6 +51,7 @@ dev = [
52
51
 
53
52
  [project.scripts]
54
53
  assistant = "abstractassistant.cli:main"
54
+ create-app-bundle = "abstractassistant.create_app_bundle:main"
55
55
 
56
56
  [project.urls]
57
57
  Homepage = "https://github.com/lpalbou/abstractassistant"
@@ -62,6 +62,9 @@ Issues = "https://github.com/lpalbou/abstractassistant/issues"
62
62
  where = ["."]
63
63
  include = ["abstractassistant*"]
64
64
 
65
+ [tool.setuptools]
66
+ py-modules = ["setup_macos_app"]
67
+
65
68
  [tool.black]
66
69
  line-length = 88
67
70
  target-version = ['py39']
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ macOS App Bundle Generator for AbstractAssistant.
4
+
5
+ This module creates a macOS .app bundle during installation,
6
+ allowing users to launch AbstractAssistant from the Dock.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import shutil
12
+ import subprocess
13
+ from pathlib import Path
14
+ from typing import Optional
15
+
16
+ try:
17
+ from PIL import Image
18
+ PIL_AVAILABLE = True
19
+ except ImportError:
20
+ PIL_AVAILABLE = False
21
+
22
+
23
+ class MacOSAppBundleGenerator:
24
+ """Generates macOS app bundles for AbstractAssistant."""
25
+
26
+ def __init__(self, package_dir: Path):
27
+ """Initialize the app bundle generator.
28
+
29
+ Args:
30
+ package_dir: Path to the abstractassistant package directory
31
+ """
32
+ self.package_dir = package_dir
33
+ self.app_name = "AbstractAssistant"
34
+ self.app_bundle_path = Path("/Applications") / f"{self.app_name}.app"
35
+
36
+ def is_macos(self) -> bool:
37
+ """Check if running on macOS."""
38
+ return sys.platform == "darwin"
39
+
40
+ def has_permissions(self) -> bool:
41
+ """Check if we have permissions to write to /Applications."""
42
+ try:
43
+ test_file = Path("/Applications") / ".test_write_permission"
44
+ test_file.touch()
45
+ test_file.unlink()
46
+ return True
47
+ except (PermissionError, OSError):
48
+ return False
49
+
50
+ def create_app_bundle_structure(self) -> bool:
51
+ """Create the basic app bundle directory structure."""
52
+ try:
53
+ # Create main directories
54
+ contents_dir = self.app_bundle_path / "Contents"
55
+ macos_dir = contents_dir / "MacOS"
56
+ resources_dir = contents_dir / "Resources"
57
+
58
+ for directory in [contents_dir, macos_dir, resources_dir]:
59
+ directory.mkdir(parents=True, exist_ok=True)
60
+
61
+ return True
62
+ except Exception as e:
63
+ print(f"Error creating app bundle structure: {e}")
64
+ return False
65
+
66
+ def generate_app_icon(self) -> bool:
67
+ """Generate the app icon using the existing icon generator."""
68
+ try:
69
+ # Import the icon generator
70
+ sys.path.insert(0, str(self.package_dir))
71
+ from abstractassistant.utils.icon_generator import IconGenerator
72
+
73
+ # Generate high-resolution icon
74
+ generator = IconGenerator(size=512)
75
+ icon = generator.create_app_icon('blue', animated=False)
76
+
77
+ # Save as PNG
78
+ icon_path = self.app_bundle_path / "Contents" / "Resources" / "icon.png"
79
+ icon.save(str(icon_path))
80
+
81
+ # Create ICNS file
82
+ return self._create_icns_file(icon_path)
83
+
84
+ except Exception as e:
85
+ print(f"Error generating app icon: {e}")
86
+ return False
87
+
88
+ def _create_icns_file(self, png_path: Path) -> bool:
89
+ """Create ICNS file from PNG using macOS iconutil."""
90
+ try:
91
+ # Create iconset directory
92
+ iconset_dir = png_path.parent / "temp_icons.iconset"
93
+ iconset_dir.mkdir(exist_ok=True)
94
+
95
+ # Load the PNG and create different sizes
96
+ icon = Image.open(png_path)
97
+ sizes = [
98
+ (16, 'icon_16x16.png'),
99
+ (32, 'icon_16x16@2x.png'),
100
+ (32, 'icon_32x32.png'),
101
+ (64, 'icon_32x32@2x.png'),
102
+ (128, 'icon_128x128.png'),
103
+ (256, 'icon_128x128@2x.png'),
104
+ (256, 'icon_256x256.png'),
105
+ (512, 'icon_256x256@2x.png'),
106
+ (512, 'icon_512x512.png'),
107
+ (1024, 'icon_512x512@2x.png')
108
+ ]
109
+
110
+ for size, filename in sizes:
111
+ resized = icon.resize((size, size), Image.Resampling.LANCZOS)
112
+ resized.save(iconset_dir / filename)
113
+
114
+ # Convert to ICNS
115
+ icns_path = png_path.parent / "icon.icns"
116
+ result = subprocess.run([
117
+ 'iconutil', '-c', 'icns', str(iconset_dir),
118
+ '-o', str(icns_path)
119
+ ], capture_output=True, text=True)
120
+
121
+ # Clean up
122
+ shutil.rmtree(iconset_dir)
123
+
124
+ return result.returncode == 0
125
+
126
+ except Exception as e:
127
+ print(f"Error creating ICNS file: {e}")
128
+ return False
129
+
130
+ def create_info_plist(self) -> bool:
131
+ """Create the Info.plist file."""
132
+ try:
133
+ plist_content = '''<?xml version="1.0" encoding="UTF-8"?>
134
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
135
+ <plist version="1.0">
136
+ <dict>
137
+ <key>CFBundleExecutable</key>
138
+ <string>AbstractAssistant</string>
139
+ <key>CFBundleIdentifier</key>
140
+ <string>ai.abstractcore.abstractassistant</string>
141
+ <key>CFBundleName</key>
142
+ <string>AbstractAssistant</string>
143
+ <key>CFBundleDisplayName</key>
144
+ <string>AbstractAssistant</string>
145
+ <key>CFBundleVersion</key>
146
+ <string>0.2.5</string>
147
+ <key>CFBundleShortVersionString</key>
148
+ <string>0.2.5</string>
149
+ <key>CFBundlePackageType</key>
150
+ <string>APPL</string>
151
+ <key>CFBundleSignature</key>
152
+ <string>????</string>
153
+ <key>CFBundleIconFile</key>
154
+ <string>icon.icns</string>
155
+ <key>LSMinimumSystemVersion</key>
156
+ <string>10.15</string>
157
+ <key>NSHighResolutionCapable</key>
158
+ <true/>
159
+ <key>NSRequiresAquaSystemAppearance</key>
160
+ <false/>
161
+ <key>LSUIElement</key>
162
+ <true/>
163
+ <key>NSAppleScriptEnabled</key>
164
+ <false/>
165
+ <key>CFBundleDocumentTypes</key>
166
+ <array/>
167
+ <key>NSPrincipalClass</key>
168
+ <string>NSApplication</string>
169
+ </dict>
170
+ </plist>'''
171
+
172
+ plist_path = self.app_bundle_path / "Contents" / "Info.plist"
173
+ plist_path.write_text(plist_content)
174
+ return True
175
+
176
+ except Exception as e:
177
+ print(f"Error creating Info.plist: {e}")
178
+ return False
179
+
180
+ def create_launch_script(self) -> bool:
181
+ """Create the executable launch script."""
182
+ try:
183
+ script_content = '''#!/bin/bash
184
+
185
+ # AbstractAssistant macOS App Launcher
186
+ # This script launches the AbstractAssistant application
187
+
188
+ # Find the Python executable and package
189
+ PYTHON_EXEC="$(which python3)"
190
+ if [ -z "$PYTHON_EXEC" ]; then
191
+ PYTHON_EXEC="$(which python)"
192
+ fi
193
+
194
+ if [ -z "$PYTHON_EXEC" ]; then
195
+ echo "Error: Python not found in PATH"
196
+ exit 1
197
+ fi
198
+
199
+ # Launch the assistant
200
+ exec "$PYTHON_EXEC" -m abstractassistant.cli "$@"'''
201
+
202
+ script_path = self.app_bundle_path / "Contents" / "MacOS" / "AbstractAssistant"
203
+ script_path.write_text(script_content)
204
+
205
+ # Make executable
206
+ os.chmod(script_path, 0o755)
207
+ return True
208
+
209
+ except Exception as e:
210
+ print(f"Error creating launch script: {e}")
211
+ return False
212
+
213
+ def generate_app_bundle(self) -> bool:
214
+ """Generate the complete macOS app bundle."""
215
+ if not self.is_macos():
216
+ print("macOS app bundle generation is only available on macOS")
217
+ return False
218
+
219
+ if not self.has_permissions():
220
+ print("Insufficient permissions to create app bundle in /Applications")
221
+ print("Please run with sudo or manually copy the app bundle")
222
+ return False
223
+
224
+ print("Creating macOS app bundle...")
225
+
226
+ # Remove existing bundle if it exists
227
+ if self.app_bundle_path.exists():
228
+ shutil.rmtree(self.app_bundle_path)
229
+
230
+ # Create bundle structure
231
+ if not self.create_app_bundle_structure():
232
+ return False
233
+
234
+ # Generate icon
235
+ if not self.generate_app_icon():
236
+ return False
237
+
238
+ # Create Info.plist
239
+ if not self.create_info_plist():
240
+ return False
241
+
242
+ # Create launch script
243
+ if not self.create_launch_script():
244
+ return False
245
+
246
+ print(f"✅ macOS app bundle created successfully!")
247
+ print(f" Location: {self.app_bundle_path}")
248
+ print(f" You can now launch AbstractAssistant from the Dock!")
249
+
250
+ return True
251
+
252
+
253
+ def create_macos_app_bundle():
254
+ """Main function to create macOS app bundle during installation."""
255
+ try:
256
+ # Find the package directory
257
+ package_dir = Path(__file__).parent
258
+
259
+ generator = MacOSAppBundleGenerator(package_dir)
260
+ return generator.generate_app_bundle()
261
+
262
+ except Exception as e:
263
+ print(f"Error creating macOS app bundle: {e}")
264
+ return False
265
+
266
+
267
+ if __name__ == "__main__":
268
+ success = create_macos_app_bundle()
269
+ sys.exit(0 if success else 1)
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- assistant = abstractassistant.cli:main