eagle-cooler 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,46 @@
1
+
2
+ from eagle_cooler import EagleWebApi
3
+
4
+ from eagle_cooler.model import FolderModel, ItemModel
5
+ from .core import EagleCoolerCore
6
+
7
+ class EagleContext:
8
+ def get_selected_folder_ids(self) -> list[str]:
9
+ """Get selected folder IDs from the context if available"""
10
+ return EagleCoolerCore.selected_folder_ids()
11
+
12
+ def get_selected_item_ids(self) -> list[str]:
13
+ """Get selected item IDs from the context if available"""
14
+ return EagleCoolerCore.selected_item_ids()
15
+
16
+ def get_selected_folders(self) -> list[FolderModel]:
17
+ folder_ids = self.get_selected_folder_ids()
18
+ if not folder_ids:
19
+ return []
20
+
21
+ all_folders = EagleWebApi.folder.list()
22
+ return [folder for folder in all_folders if folder['id'] in folder_ids]
23
+
24
+ def get_selected_items(self, throw : bool = False) -> list[ItemModel]:
25
+ item_ids = self.get_selected_item_ids()
26
+ if not item_ids:
27
+ return []
28
+
29
+ selected_items = []
30
+ for selected_item in item_ids:
31
+ try:
32
+ item_info = EagleWebApi.item.get_info(selected_item)
33
+ if item_info:
34
+ selected_items.append(item_info)
35
+ continue
36
+ except Exception as e:
37
+ if throw:
38
+ raise e
39
+
40
+ continue
41
+
42
+ return selected_items
43
+
44
+ eagleContext = EagleContext()
45
+
46
+ __all__ = ["eagleContext"]
eagle_cooler/core.py ADDED
@@ -0,0 +1,55 @@
1
+ import json
2
+ import os
3
+ import requests
4
+ from typing import Optional
5
+
6
+ class EagleCoolerCore:
7
+ __fullBridgeContext = None
8
+ __token = None
9
+ __applicationInfo = None
10
+
11
+ @classmethod
12
+ def __load(cls):
13
+ # Load context when class is defined
14
+ try:
15
+ context_env = os.environ.get("POWEREAGLE_CONTEXT")
16
+ if context_env:
17
+ cls.__fullBridgeContext = json.loads(context_env)
18
+ cls.__token = cls.__fullBridgeContext.get("apiToken")
19
+ return # Successfully loaded, exit early
20
+ except Exception as e:
21
+ print(f"Failed to load Power Eagle context: {e}")
22
+
23
+ # Fallback to Eagle API if Power Eagle context failed
24
+ try:
25
+ response = requests.get("http://localhost:41595/api/application/info", timeout=5)
26
+ if response.status_code == 200:
27
+ cls.__applicationInfo = response.json()
28
+ token = cls.__applicationInfo.get("data", {}).get("preferences", {}).get("developer", {}).get("apiToken")
29
+ if token:
30
+ cls.__token = token
31
+ except Exception as api_error:
32
+ print(f"Failed to get token from Eagle API: {api_error}")
33
+ cls.__token = None
34
+
35
+ @classmethod
36
+ def token(cls) -> Optional[str]:
37
+ """Get API token"""
38
+ return cls.__token
39
+
40
+ @classmethod
41
+ def selected_item_ids(cls) -> Optional[list]:
42
+ """Get selected items from the context if available"""
43
+ if cls.__fullBridgeContext:
44
+ return cls.__fullBridgeContext.get("selected").get("items")
45
+ return None
46
+
47
+ @classmethod
48
+ def selected_folder_ids(cls) -> Optional[list]:
49
+ """Get selected folders from the context if available"""
50
+ if cls.__fullBridgeContext:
51
+ return cls.__fullBridgeContext.get("selected").get("folders")
52
+ return None
53
+
54
+ EagleCoolerCore._EagleCoolerCore__load()
55
+
eagle_cooler/model.py ADDED
@@ -0,0 +1,48 @@
1
+ from typing import TypedDict, Any
2
+
3
+
4
+ class FolderModel(TypedDict):
5
+ """TypedDict model for Eagle folder structure"""
6
+ id: str
7
+ name: str
8
+ description: str
9
+ children: list[str] # List of child folder IDs
10
+ modificationTime: int # Unix timestamp in milliseconds
11
+ tags: list[str]
12
+ extendTags: list[str]
13
+ pinyin: str
14
+ password: str
15
+ passwordTips: str
16
+
17
+
18
+ class OCRModel(TypedDict):
19
+ """TypedDict model for OCR data"""
20
+ done: str
21
+ text: str
22
+
23
+
24
+ class DetectionModel(TypedDict):
25
+ """TypedDict model for object detection data"""
26
+ done: str
27
+ objects: list[Any] # Object detection results
28
+
29
+
30
+ class ItemModel(TypedDict):
31
+ """TypedDict model for Eagle item structure"""
32
+ id: str
33
+ name: str
34
+ size: int # File size in bytes
35
+ btime: int # Birth time (creation time) in milliseconds
36
+ mtime: int # File modification time in milliseconds
37
+ ext: str # File extension
38
+ tags: list[str]
39
+ folders: list[str] # List of folder IDs this item belongs to
40
+ isDeleted: bool
41
+ url: str
42
+ annotation: str
43
+ modificationTime: int # Eagle modification time in milliseconds
44
+ noPreview: bool
45
+ lastModified: int # Last modified time in milliseconds
46
+ ocr: OCRModel
47
+ detection: DetectionModel
48
+
eagle_cooler/webapi.py ADDED
@@ -0,0 +1,246 @@
1
+
2
+
3
+ import requests
4
+ from typing import Optional, Dict, Any, List
5
+ from .core import EagleCoolerCore
6
+
7
+
8
+ class EagleWebApi:
9
+ """Python implementation of Eagle API client for HTTP requests to Eagle application"""
10
+
11
+ _token: Optional[str] = None
12
+
13
+ @classmethod
14
+ def _make_request(cls, path: str, method: str = "GET", data: Optional[Dict[str, Any]] = None,
15
+ params: Optional[Dict[str, Any]] = None) -> Any:
16
+ """Makes internal HTTP request to Eagle API"""
17
+ token = EagleCoolerCore.token()
18
+ if not token:
19
+ raise Exception("No API token found")
20
+
21
+ url = f"http://localhost:41595/api/{path}"
22
+
23
+ # Add token to params
24
+ if params is None:
25
+ params = {}
26
+ params["token"] = token
27
+
28
+ # Remove None values from params and data
29
+ if params:
30
+ params = {k: v for k, v in params.items() if v is not None}
31
+ if data:
32
+ data = {k: v for k, v in data.items() if v is not None}
33
+
34
+ try:
35
+ if method.upper() == "POST":
36
+ response = requests.post(url, params=params, json=data)
37
+ else:
38
+ response = requests.get(url, params=params)
39
+
40
+ response.raise_for_status()
41
+ result = response.json()
42
+ return result.get("data")
43
+ except Exception as e:
44
+ print(f"Request failed: {e}")
45
+ raise e
46
+
47
+ class _Application:
48
+ """_Application-related API methods"""
49
+
50
+ @staticmethod
51
+ def info() -> Any:
52
+ """Gets application information"""
53
+ return EagleWebApi._make_request("application/info")
54
+
55
+ class _Folder:
56
+ """_Folder-related API methods"""
57
+
58
+ @staticmethod
59
+ def create(name: str, parent_id: Optional[str] = None) -> Any:
60
+ """Creates a new folder"""
61
+ return EagleWebApi._make_request("folder/create", "POST", {
62
+ "folderName": name,
63
+ "parent": parent_id
64
+ })
65
+
66
+ @staticmethod
67
+ def rename(folder_id: str, new_name: str) -> Any:
68
+ """Renames a folder"""
69
+ return EagleWebApi._make_request("folder/rename", "POST", {
70
+ "folderId": folder_id,
71
+ "newName": new_name
72
+ })
73
+
74
+ @staticmethod
75
+ def update(folder_id: str, new_name: Optional[str] = None,
76
+ new_description: Optional[str] = None, new_color: Optional[str] = None) -> Any:
77
+ """Updates folder properties"""
78
+ return EagleWebApi._make_request("folder/update", "POST", {
79
+ "folderId": folder_id,
80
+ "newName": new_name,
81
+ "newDescription": new_description,
82
+ "newColor": new_color
83
+ })
84
+
85
+ @staticmethod
86
+ def list() -> Any:
87
+ """Lists all folders"""
88
+ return EagleWebApi._make_request("folder/list")
89
+
90
+ @staticmethod
91
+ def list_recent() -> Any:
92
+ """Lists recent folders"""
93
+ return EagleWebApi._make_request("folder/listRecent")
94
+
95
+ class _Library:
96
+ """_Library-related API methods"""
97
+
98
+ @staticmethod
99
+ def info() -> Any:
100
+ """Gets library information"""
101
+ return EagleWebApi._make_request("library/info")
102
+
103
+ @staticmethod
104
+ def history() -> Any:
105
+ """Gets library history"""
106
+ return EagleWebApi._make_request("library/history")
107
+
108
+ @staticmethod
109
+ def switch(library_path: str) -> Any:
110
+ """Switches to a different library"""
111
+ return EagleWebApi._make_request("library/switch", "POST", {
112
+ "libraryPath": library_path
113
+ })
114
+
115
+ @staticmethod
116
+ def icon(library_path: str) -> Any:
117
+ """Gets library icon"""
118
+ return EagleWebApi._make_request("library/icon", params={
119
+ "libraryPath": library_path
120
+ })
121
+
122
+ class _Item:
123
+ """_Item-related API methods"""
124
+
125
+ @staticmethod
126
+ def update(item_id: str, tags: Optional[List[str]] = None, annotation: Optional[str] = None,
127
+ url: Optional[str] = None, star: Optional[int] = None) -> Any:
128
+ """Updates item properties"""
129
+ return EagleWebApi._make_request("item/update", "POST", {
130
+ "id": item_id,
131
+ "tags": tags,
132
+ "annotation": annotation,
133
+ "url": url,
134
+ "star": star
135
+ })
136
+
137
+ @staticmethod
138
+ def refresh_thumbnail(item_id: str) -> Any:
139
+ """Refreshes item thumbnail"""
140
+ return EagleWebApi._make_request("item/refreshThumbnail", "POST", {
141
+ "id": item_id
142
+ })
143
+
144
+ @staticmethod
145
+ def refresh_palette(item_id: str) -> Any:
146
+ """Refreshes item color palette"""
147
+ return EagleWebApi._make_request("item/refreshPalette", "POST", {
148
+ "id": item_id
149
+ })
150
+
151
+ @staticmethod
152
+ def move_to_trash(item_ids: List[str]) -> Any:
153
+ """Moves items to trash"""
154
+ return EagleWebApi._make_request("item/moveToTrash", "POST", {
155
+ "itemIds": item_ids
156
+ })
157
+
158
+ @staticmethod
159
+ def list(limit: Optional[int] = None, offset: Optional[int] = None,
160
+ order_by: Optional[str] = None, keyword: Optional[str] = None,
161
+ ext: Optional[str] = None, tags: Optional[List[str]] = None,
162
+ folders: Optional[List[str]] = None) -> Any:
163
+ """Lists items with filters"""
164
+ return EagleWebApi._make_request("item/list", params={
165
+ "limit": limit,
166
+ "offset": offset,
167
+ "orderBy": order_by,
168
+ "keyword": keyword,
169
+ "ext": ext,
170
+ "tags": tags,
171
+ "folders": folders
172
+ })
173
+
174
+ @staticmethod
175
+ def get_thumbnail(item_id: str) -> Any:
176
+ """Gets item thumbnail"""
177
+ return EagleWebApi._make_request("item/thumbnail", params={
178
+ "id": item_id
179
+ })
180
+
181
+ @staticmethod
182
+ def get_info(item_id: str) -> Any:
183
+ """Gets item information"""
184
+ return EagleWebApi._make_request("item/info", params={
185
+ "id": item_id
186
+ })
187
+
188
+ @staticmethod
189
+ def add_bookmark(url: str, name: str, base64: Optional[str] = None,
190
+ tags: Optional[List[str]] = None, modification_time: Optional[int] = None,
191
+ folder_id: Optional[str] = None) -> Any:
192
+ """Adds bookmark item"""
193
+ return EagleWebApi._make_request("item/addBookmark", "POST", {
194
+ "url": url,
195
+ "name": name,
196
+ "base64": base64,
197
+ "tags": tags,
198
+ "modificationTime": modification_time,
199
+ "folderId": folder_id
200
+ })
201
+
202
+ @staticmethod
203
+ def add_from_url(url: str, name: str, website: Optional[str] = None,
204
+ tags: Optional[List[str]] = None, star: Optional[int] = None,
205
+ annotation: Optional[str] = None, modification_time: Optional[int] = None,
206
+ folder_id: Optional[str] = None, headers: Optional[Dict[str, Any]] = None) -> Any:
207
+ """Adds item from URL"""
208
+ return EagleWebApi._make_request("item/addFromUrl", "POST", {
209
+ "url": url,
210
+ "name": name,
211
+ "website": website,
212
+ "tags": tags,
213
+ "star": star,
214
+ "annotation": annotation,
215
+ "modificationTime": modification_time,
216
+ "folderId": folder_id,
217
+ "headers": headers
218
+ })
219
+
220
+ @staticmethod
221
+ def add_from_path(path: str, name: str, website: Optional[str] = None,
222
+ annotation: Optional[str] = None, tags: Optional[List[str]] = None,
223
+ folder_id: Optional[str] = None) -> Any:
224
+ """Adds item from file path"""
225
+ return EagleWebApi._make_request("item/addFromPath", "POST", {
226
+ "path": path,
227
+ "name": name,
228
+ "website": website,
229
+ "annotation": annotation,
230
+ "tags": tags,
231
+ "folderId": folder_id
232
+ })
233
+
234
+ @staticmethod
235
+ def add_from_urls(items: List[Dict[str, Any]], folder_id: Optional[str] = None) -> Any:
236
+ """Adds multiple items from URLs"""
237
+ return EagleWebApi._make_request("item/addFromURLs", "POST", {
238
+ "items": items,
239
+ "folderId": folder_id
240
+ })
241
+
242
+ # Create class-level aliases for easier access
243
+ application = _Application()
244
+ folder = _Folder()
245
+ library = _Library()
246
+ item = _Item()
@@ -0,0 +1,245 @@
1
+ Metadata-Version: 2.3
2
+ Name: eagle-cooler
3
+ Version: 0.1.0
4
+ Summary: Python library for bridging Eagle js environment
5
+ Author: ZackaryW
6
+ Author-email: ZackaryW <gitzackw@gmail.com>
7
+ Requires-Dist: requests>=2.25.0
8
+ Requires-Python: >=3.13
9
+ Description-Content-Type: text/markdown
10
+
11
+ # Eagle Cooler 🦅❄️
12
+
13
+ [![PyPI version](https://badge.fury.io/py/eagle-cooler.svg)](https://badge.fury.io/py/eagle-cooler)
14
+ [![Python Support](https://img.shields.io/pypi/pyversions/eagle-cooler.svg)](https://pypi.org/project/eagle-cooler/)
15
+ [![License](https://img.shields.io/github/license/ZackaryW/py-eagle-cooler.svg)](LICENSE)
16
+
17
+ A modern Python wrapper for the [Eagle.cool](https://eagle.cool) HTTP API. Works independently with Eagle's web API or seamlessly integrates with the Power Eagle plugin system for enhanced functionality.
18
+
19
+ ## ✨ Features
20
+
21
+ - 🚀 **Complete API Coverage** - Full implementation of Eagle's HTTP API
22
+ - 🔐 **Flexible Authentication** - Works standalone with manual token setup or automatically with Power Eagle
23
+ - 📝 **Type Hints** - Better development experience with full type annotations
24
+ - 🎯 **Easy-to-use Interface** - Clean, class-based API design
25
+ - ⚡ **Modern Python** - Built for Python 3.13+ with modern best practices
26
+ - 🔌 **Plugin Ready** - Enhanced integration with Power Eagle plugin ecosystem
27
+ - 🏠 **Standalone Ready** - Can work independently with just Eagle's web API
28
+
29
+ ## 📦 Installation
30
+
31
+ Install the latest stable version from PyPI:
32
+
33
+ ```bash
34
+ # Using uv (recommended)
35
+ uv add eagle-cooler
36
+
37
+ # Using pip
38
+ pip install eagle-cooler
39
+
40
+ # For development
41
+ git clone https://github.com/ZackaryW/py-eagle-cooler.git
42
+ cd py-eagle-cooler
43
+ uv sync --dev
44
+ ```
45
+
46
+ ## 🚀 Quick Start
47
+
48
+ ### Standalone Usage (Web API Only)
49
+
50
+ For basic functionality using Eagle's HTTP API directly:
51
+
52
+ ```python
53
+ from eagle_cooler import EagleWebApi
54
+
55
+ # Get application info
56
+ app_info = EagleWebApi.application.info()
57
+ print(f"Eagle version: {app_info['version']}")
58
+
59
+ # List folders
60
+ folders = EagleWebApi.folder.list()
61
+ print(f"Found {len(folders)} folders")
62
+
63
+ # Create a new folder
64
+ new_folder = EagleWebApi.folder.create("My New Folder")
65
+ print(f"Created: {new_folder['name']}")
66
+
67
+ # List items with filters
68
+ items = EagleWebApi.item.list(limit=10, ext="jpg")
69
+ print(f"Found {len(items)} JPG images")
70
+
71
+ # Add item from URL
72
+ result = EagleWebApi.item.add_from_url(
73
+ url="https://example.com/image.jpg",
74
+ name="Example Image",
75
+ tags=["example", "test"]
76
+ )
77
+
78
+ # Add item from local file
79
+ local_item = EagleWebApi.item.add_from_path(
80
+ path="/path/to/image.jpg",
81
+ name="Local Image",
82
+ tags=["local"]
83
+ )
84
+ ```
85
+
86
+ ### Power Eagle Integration (Enhanced Features)
87
+
88
+ When running in a Power Eagle context, access to enhanced features:
89
+
90
+ ```python
91
+ # In a Power Eagle Python script
92
+ from eagle_cooler import eagleContext, EagleCallback
93
+
94
+ # Get selected items and folders from Power Eagle context
95
+ selected_items = eagleContext.get_selected_items()
96
+ selected_folders = eagleContext.get_selected_folders()
97
+
98
+ # Get just the IDs if needed
99
+ selected_item_ids = eagleContext.get_selected_item_ids()
100
+ selected_folder_ids = eagleContext.get_selected_folder_ids()
101
+
102
+ # Use callback system for advanced plugin operations
103
+ # (extensive callback API available for plugin development)
104
+ ```
105
+
106
+ ## 📚 API Reference
107
+
108
+ ### 🏢 Application
109
+ - `EagleWebApi.application.info()` - Get application information
110
+
111
+ ### 📁 Folders
112
+ - `EagleWebApi.folder.create(name, parent_id=None)` - Create folder
113
+ - `EagleWebApi.folder.rename(folder_id, new_name)` - Rename folder
114
+ - `EagleWebApi.folder.update(folder_id, new_name=None, new_description=None, new_color=None)` - Update folder properties
115
+ - `EagleWebApi.folder.list()` - List all folders
116
+ - `EagleWebApi.folder.list_recent()` - List recent folders
117
+
118
+ ### 📚 Library
119
+ - `EagleWebApi.library.info()` - Get library information
120
+ - `EagleWebApi.library.history()` - Get library history
121
+ - `EagleWebApi.library.switch(library_path)` - Switch to different library
122
+ - `EagleWebApi.library.icon(library_path)` - Get library icon
123
+
124
+ ### 🖼️ Items
125
+ - `EagleWebApi.item.list(limit=None, offset=None, order_by=None, keyword=None, ext=None, tags=None, folders=None)` - List items with filters
126
+ - `EagleWebApi.item.get_info(item_id)` - Get item details
127
+ - `EagleWebApi.item.get_thumbnail(item_id)` - Get item thumbnail
128
+ - `EagleWebApi.item.update(item_id, tags=None, annotation=None, url=None, star=None)` - Update item properties
129
+ - `EagleWebApi.item.add_from_url(url, name, website=None, tags=None, star=None, annotation=None, modification_time=None, folder_id=None, headers=None)` - Add item from URL
130
+ - `EagleWebApi.item.add_from_path(path, name, website=None, annotation=None, tags=None, folder_id=None)` - Add item from file path
131
+ - `EagleWebApi.item.add_from_urls(items, folder_id=None)` - Add multiple items from URLs
132
+ - `EagleWebApi.item.add_bookmark(url, name, base64=None, tags=None, modification_time=None, folder_id=None)` - Add bookmark
133
+ - `EagleWebApi.item.move_to_trash(item_ids)` - Move items to trash
134
+ - `EagleWebApi.item.refresh_thumbnail(item_id)` - Refresh thumbnail
135
+ - `EagleWebApi.item.refresh_palette(item_id)` - Refresh color palette
136
+
137
+ ### 🔧 Context (Power Eagle Mode)
138
+ - `eagleContext.get_selected_item_ids()` - Get selected item IDs from Power Eagle context
139
+ - `eagleContext.get_selected_folder_ids()` - Get selected folder IDs from Power Eagle context
140
+ - `eagleContext.get_selected_items(throw=False)` - Get selected items as typed ItemModel objects
141
+ - `eagleContext.get_selected_folders()` - Get selected folders as typed FolderModel objects
142
+
143
+ ### 📞 Callbacks (Power Eagle Plugins)
144
+ - `EagleCallback` - Callback system for Power Eagle plugin integration (extensive API available)
145
+
146
+ ## 🔌 Usage Modes
147
+
148
+ Eagle Cooler supports two usage modes:
149
+
150
+ ### 🏠 Standalone Mode (Web API)
151
+ - **Limited Capacity**: Basic API operations through Eagle's HTTP interface
152
+ - **Manual Setup**: Requires Eagle application running with HTTP API enabled
153
+ - **Authentication**: Uses direct API calls (no token management)
154
+ - **Features**: Core operations like listing, creating folders, basic item management
155
+
156
+ ### 🚀 Power Eagle Mode (Enhanced)
157
+ - **Full Capacity**: Complete feature set with enhanced functionality
158
+ - **Automatic Setup**: Token and context management handled automatically
159
+ - **Authentication**: Seamless integration with Power Eagle's security context
160
+ - **Features**: All core operations plus callbacks, advanced file operations, and plugin ecosystem integration
161
+
162
+ ```python
163
+ # Standalone mode example
164
+ from eagle_cooler import EagleWebApi
165
+
166
+ # Basic operations available
167
+ folders = EagleWebApi.folder.list()
168
+
169
+ # Power Eagle mode example (when POWEREAGLE_CONTEXT is available)
170
+ from eagle_cooler import EagleWebApi, eagleContext, EagleCallback
171
+
172
+ # Enhanced operations available:
173
+ # 1. Automatic token management
174
+ folders = EagleWebApi.folder.list()
175
+
176
+ # 2. Access to Power Eagle context with typed models
177
+ selected_items = eagleContext.get_selected_items() # Returns list[ItemModel]
178
+ selected_folders = eagleContext.get_selected_folders() # Returns list[FolderModel]
179
+
180
+ # 3. Callback system for advanced plugin operations
181
+ if selected_items:
182
+ for item in selected_items:
183
+ print(f"Selected item: {item['name']} ({item['ext']})")
184
+ # Use callback system to interact with Power Eagle host
185
+ # (extensive callback API available - see METHODS_WITH_RETURN_VALUES)
186
+ ```
187
+
188
+ ## ⚙️ Requirements
189
+
190
+ - **Python**: >= 3.13
191
+ - **Eagle.cool**: Application running with API access enabled
192
+ - **Dependencies**: `requests >= 2.25.0`
193
+ - **For Power Eagle**: `POWEREAGLE_CONTEXT` environment variable set
194
+
195
+ ### Setup Eagle API Access
196
+
197
+ 1. Open Eagle.cool application
198
+ 2. Go to **Preferences** → **Plugin**
199
+ 3. Enable **HTTP API**
200
+ 4. Note the API port (default: 41595)
201
+
202
+ ## 🤝 Contributing
203
+
204
+ We welcome contributions! Here's how to get started:
205
+
206
+ 1. **Fork the repository**
207
+ 2. **Clone your fork**:
208
+ ```bash
209
+ git clone https://github.com/yourusername/py-eagle-cooler.git
210
+ cd py-eagle-cooler
211
+ ```
212
+ 3. **Install development dependencies**:
213
+ ```bash
214
+ uv sync --dev
215
+ ```
216
+ 4. **Make your changes**
217
+ 5. **Run tests** (when available):
218
+ ```bash
219
+ uv run pytest
220
+ ```
221
+ 6. **Submit a pull request**
222
+
223
+ ### Development Setup
224
+
225
+ ```bash
226
+ # Clone the repository
227
+ git clone https://github.com/ZackaryW/py-eagle-cooler.git
228
+ cd py-eagle-cooler
229
+
230
+ # Install with development dependencies
231
+ uv sync --dev
232
+
233
+ # Run the package in development mode
234
+ uv run python -m eagle_cooler
235
+ ```
236
+
237
+ ## 📜 License
238
+
239
+ This project is licensed under the same terms as Power Eagle.
240
+
241
+ ---
242
+
243
+ <p align="center">
244
+ <sub>Built with ❤️ for the Eagle.cool community</sub>
245
+ </p>
@@ -0,0 +1,10 @@
1
+ eagle_cooler/__init__.py,sha256=DJU-5HsVAnYftHWy4Ue6HCSt_U4Ex-j7n8UOQzxBX0I,231
2
+ eagle_cooler/callback.py,sha256=Eg6hZAtr6uKVES7xgV3hoTejMqtRFbu8qM9KWr7tyNo,36119
3
+ eagle_cooler/context.py,sha256=ICNi8vfep2UH9TKMniKYy3IvHgbMGynyVJhWPmeV-Ik,1462
4
+ eagle_cooler/core.py,sha256=JXBf3hUgPpWKfPWyAn7EE60nKPR7QSfk7w8y9y0PA8o,1945
5
+ eagle_cooler/model.py,sha256=BSjxgPw9_5t7J5Lcjt2VaH6KSdz21MgKemGGyCKZXAY,1257
6
+ eagle_cooler/webapi.py,sha256=COCCh1o_9saf5huCV7AGvoDSLouafVe3aor5wUq1uOQ,9091
7
+ eagle_cooler-0.1.0.dist-info/WHEEL,sha256=Pi5uDq5Fdo_Rr-HD5h9BiPn9Et29Y9Sh8NhcJNnFU1c,79
8
+ eagle_cooler-0.1.0.dist-info/entry_points.txt,sha256=pDVCjWW7abbqDCWZdMaowSdiRl4TkDQ5eTajXU1YEck,52
9
+ eagle_cooler-0.1.0.dist-info/METADATA,sha256=B1YbxPqYG1NfYXDn1hahC9PFnVql5cbS614YZIBk-ck,8582
10
+ eagle_cooler-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.8.17
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ eagle-cooler = eagle_cooler:main
3
+