remotescout 1.0.0__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.
@@ -0,0 +1,35 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ *.egg
7
+
8
+ # Build
9
+ build/
10
+ build_log*.txt
11
+
12
+ # Config & data (contains credentials/user data)
13
+ config/
14
+ data/cache/
15
+ data/downloads/
16
+ logs/
17
+
18
+ # Keep uploads structure but ignore large files
19
+ data/uploads/*/Remote/
20
+
21
+ # OS
22
+ desktop.ini
23
+ .megaignore
24
+ Thumbs.db
25
+ .DS_Store
26
+
27
+ # IDE
28
+ .vscode/
29
+ .idea/
30
+
31
+ # Legacy reference file
32
+ main-sinAPI.py
33
+
34
+ # Claude Code
35
+ .claude/
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: remotescout
3
+ Version: 1.0.0
4
+ Summary: Python library for managing IoT agents via MQTT — screenshots, commands, file transfers, tunnels, cameras
5
+ Project-URL: Homepage, https://github.com/Pedro77339/remotescout-mcp
6
+ Project-URL: Repository, https://github.com/Pedro77339/remotescoutapp
7
+ Author: Pedro Camacho
8
+ License: MIT
9
+ Keywords: agent,iot,mcp,mqtt,remote-management
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: System :: Monitoring
18
+ Classifier: Topic :: System :: Systems Administration
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: paho-mqtt>=1.6
21
+ Requires-Dist: pillow>=9.0
22
+ Requires-Dist: requests>=2.28
23
+ Description-Content-Type: text/markdown
24
+
25
+ # remotescout
26
+
27
+ Python library for managing IoT agents via MQTT. Provides a unified API for remote device management: screenshots, shell commands, file transfers, TCP tunnels, camera feeds, and OTA deployments.
28
+
29
+ Built for the [Remote Scout MCP Server](https://github.com/Pedro77339/remotescout-mcp) but can be used standalone.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install remotescout
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ ### Option 1: Environment variables
40
+
41
+ ```bash
42
+ export RS_MQTT_GLOBAL_PASSWORD="your_global_password"
43
+ export RS_MQTT_ROUTER_PASSWORD="your_router_password"
44
+ ```
45
+
46
+ ### Option 2: settings.json
47
+
48
+ Create a `config/settings.json` in your base path:
49
+
50
+ ```json
51
+ {
52
+ "mqtt_router_host": "mrouter.ci24.com",
53
+ "mqtt_router_port": 8001,
54
+ "mqtt_router_user": "ci24",
55
+ "mqtt_router_password": "YOUR_PASSWORD",
56
+ "mqtt_global_host": "mgovernor.ci24.com",
57
+ "mqtt_global_port": 1883,
58
+ "mqtt_global_user": "ci24",
59
+ "mqtt_global_password": "YOUR_PASSWORD"
60
+ }
61
+ ```
62
+
63
+ > Contact the project admin for MQTT broker credentials.
64
+
65
+ ## Quick start
66
+
67
+ ```python
68
+ from remotescout import RemoteScoutAPI
69
+
70
+ api = RemoteScoutAPI(base_path="./my_base")
71
+ api.initialize()
72
+ api.connect()
73
+
74
+ # Login
75
+ api.login("your_username")
76
+
77
+ # List agents
78
+ agents = api.get_agents_by_domain("BLX")
79
+ for a in agents:
80
+ print(f"{a.mac} - {a.id} ({a.status.value})")
81
+
82
+ # Execute command
83
+ result = api.execute_command("d83addc8721e", "uptime")
84
+ print(result.output)
85
+
86
+ # Screenshot
87
+ img_bytes = api.get_screenshot("d83addc8721e", ratio=3, timeout=10)
88
+ ```
89
+
90
+ ## Features
91
+
92
+ | Feature | Method |
93
+ |---------|--------|
94
+ | Agent discovery | `get_agents_by_domain()`, `get_agent()` |
95
+ | Shell commands | `execute_command()` |
96
+ | Screenshots | `get_screenshot()` |
97
+ | File browser | `list_directory()`, `get_file_bytes()` |
98
+ | File transfer | `download_file()` |
99
+ | TCP tunnels | `open_tunnel()`, `close_all_tunnels()` |
100
+ | Camera feeds | `discover_cameras()`, `get_camera_frame()` |
101
+ | OTA packages | `list_packages()`, `upload_package()` |
102
+ | OTP tokens | `get_otp()` |
103
+
104
+ ## Requirements
105
+
106
+ - Python 3.10+
107
+ - Access to the MQTT broker (credentials from admin)
108
+ - User permissions file or governor-validated credentials
@@ -0,0 +1,84 @@
1
+ # remotescout
2
+
3
+ Python library for managing IoT agents via MQTT. Provides a unified API for remote device management: screenshots, shell commands, file transfers, TCP tunnels, camera feeds, and OTA deployments.
4
+
5
+ Built for the [Remote Scout MCP Server](https://github.com/Pedro77339/remotescout-mcp) but can be used standalone.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install remotescout
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ ### Option 1: Environment variables
16
+
17
+ ```bash
18
+ export RS_MQTT_GLOBAL_PASSWORD="your_global_password"
19
+ export RS_MQTT_ROUTER_PASSWORD="your_router_password"
20
+ ```
21
+
22
+ ### Option 2: settings.json
23
+
24
+ Create a `config/settings.json` in your base path:
25
+
26
+ ```json
27
+ {
28
+ "mqtt_router_host": "mrouter.ci24.com",
29
+ "mqtt_router_port": 8001,
30
+ "mqtt_router_user": "ci24",
31
+ "mqtt_router_password": "YOUR_PASSWORD",
32
+ "mqtt_global_host": "mgovernor.ci24.com",
33
+ "mqtt_global_port": 1883,
34
+ "mqtt_global_user": "ci24",
35
+ "mqtt_global_password": "YOUR_PASSWORD"
36
+ }
37
+ ```
38
+
39
+ > Contact the project admin for MQTT broker credentials.
40
+
41
+ ## Quick start
42
+
43
+ ```python
44
+ from remotescout import RemoteScoutAPI
45
+
46
+ api = RemoteScoutAPI(base_path="./my_base")
47
+ api.initialize()
48
+ api.connect()
49
+
50
+ # Login
51
+ api.login("your_username")
52
+
53
+ # List agents
54
+ agents = api.get_agents_by_domain("BLX")
55
+ for a in agents:
56
+ print(f"{a.mac} - {a.id} ({a.status.value})")
57
+
58
+ # Execute command
59
+ result = api.execute_command("d83addc8721e", "uptime")
60
+ print(result.output)
61
+
62
+ # Screenshot
63
+ img_bytes = api.get_screenshot("d83addc8721e", ratio=3, timeout=10)
64
+ ```
65
+
66
+ ## Features
67
+
68
+ | Feature | Method |
69
+ |---------|--------|
70
+ | Agent discovery | `get_agents_by_domain()`, `get_agent()` |
71
+ | Shell commands | `execute_command()` |
72
+ | Screenshots | `get_screenshot()` |
73
+ | File browser | `list_directory()`, `get_file_bytes()` |
74
+ | File transfer | `download_file()` |
75
+ | TCP tunnels | `open_tunnel()`, `close_all_tunnels()` |
76
+ | Camera feeds | `discover_cameras()`, `get_camera_frame()` |
77
+ | OTA packages | `list_packages()`, `upload_package()` |
78
+ | OTP tokens | `get_otp()` |
79
+
80
+ ## Requirements
81
+
82
+ - Python 3.10+
83
+ - Access to the MQTT broker (credentials from admin)
84
+ - User permissions file or governor-validated credentials
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "remotescout"
7
+ version = "1.0.0"
8
+ description = "Python library for managing IoT agents via MQTT — screenshots, commands, file transfers, tunnels, cameras"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+ authors = [{name = "Pedro Camacho"}]
13
+ keywords = ["iot", "mqtt", "remote-management", "mcp", "agent"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: System :: Monitoring",
23
+ "Topic :: System :: Systems Administration",
24
+ ]
25
+ dependencies = [
26
+ "paho-mqtt>=1.6",
27
+ "pillow>=9.0",
28
+ "requests>=2.28",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/Pedro77339/remotescout-mcp"
33
+ Repository = "https://github.com/Pedro77339/remotescoutapp"
34
+
35
+ [tool.hatch.build.targets.wheel]
36
+ packages = ["remotescout"]
@@ -0,0 +1,35 @@
1
+ """RemoteScout — Agent management via MQTT."""
2
+
3
+ from remotescout.api import RemoteScoutAPI
4
+ from remotescout.models import (
5
+ AgentInfo,
6
+ AgentStatus,
7
+ UserPermissions,
8
+ Settings,
9
+ MqttConfig,
10
+ FileEntry,
11
+ CommandResult,
12
+ CameraInfo,
13
+ PortForwardInfo,
14
+ TransferProgress,
15
+ )
16
+ from remotescout.events import EventEmitter, EventType, Event
17
+
18
+ __version__ = "1.0.0"
19
+
20
+ __all__ = [
21
+ "RemoteScoutAPI",
22
+ "AgentInfo",
23
+ "AgentStatus",
24
+ "UserPermissions",
25
+ "Settings",
26
+ "MqttConfig",
27
+ "FileEntry",
28
+ "CommandResult",
29
+ "CameraInfo",
30
+ "PortForwardInfo",
31
+ "TransferProgress",
32
+ "EventEmitter",
33
+ "EventType",
34
+ "Event",
35
+ ]
@@ -0,0 +1,3 @@
1
+ from remotescout.agents.manager import AgentManager, AgentData
2
+
3
+ __all__ = ["AgentManager", "AgentData"]
@@ -0,0 +1,197 @@
1
+ """AgentManager: Centralized agent state management.
2
+
3
+ Copied from remotev3/agents/agent_manager.py — no import changes needed.
4
+ """
5
+
6
+ import threading
7
+ import queue
8
+ import json
9
+ from datetime import datetime
10
+ from dataclasses import dataclass, field
11
+ from typing import Dict, Optional, List
12
+ from os.path import exists
13
+
14
+
15
+ @dataclass
16
+ class AgentData:
17
+ """Datos de un agente"""
18
+ mac: str
19
+ id: str
20
+ domain: str
21
+ ip_public: str
22
+ ip_local: str
23
+ description: str
24
+ ver: str = "2.0"
25
+ cpu: str = ""
26
+ os: str = ""
27
+ rotate: int = 90
28
+ login: str = "root"
29
+ ts: float = field(default_factory=lambda: datetime.now().timestamp())
30
+ frozen: int = 0
31
+
32
+ @property
33
+ def status(self) -> str:
34
+ """Calcular estado basado en timestamp"""
35
+ elapsed = datetime.now().timestamp() - self.ts
36
+ if elapsed > 60:
37
+ return "frozen"
38
+ elif elapsed > 30:
39
+ return "offline"
40
+ else:
41
+ return "online"
42
+
43
+ @property
44
+ def status_color(self) -> str:
45
+ status_map = {
46
+ "online": "#4CAF50",
47
+ "offline": "#FF5722",
48
+ "frozen": "#9E9E9E"
49
+ }
50
+ return status_map.get(self.status, "#9E9E9E")
51
+
52
+ @property
53
+ def status_icon(self) -> str:
54
+ status_map = {
55
+ "online": "check_circle",
56
+ "offline": "error",
57
+ "frozen": "snowflake"
58
+ }
59
+ return status_map.get(self.status, "help")
60
+
61
+
62
+ class AgentManager:
63
+ """Gestiona el estado de todos los agentes"""
64
+
65
+ def __init__(self, user: str = ""):
66
+ self.user = user
67
+ self.agents: Dict[str, AgentData] = {}
68
+ self.agents_lock = threading.Lock()
69
+ self.update_queue = queue.Queue()
70
+ self.agents_status: Dict[str, dict] = {}
71
+ self.screenshot_cache: Dict[str, bytes] = {}
72
+ self.screenshot_lock = threading.Lock()
73
+
74
+ def add_agent(self, mac: str, agent_data) -> bool:
75
+ is_new = False
76
+ with self.agents_lock:
77
+ if isinstance(agent_data, dict):
78
+ agent_obj = AgentData(**agent_data)
79
+ else:
80
+ agent_obj = agent_data
81
+ if mac not in self.agents:
82
+ is_new = True
83
+ self.agents[mac] = agent_obj
84
+ self.update_queue.put(('add', mac, agent_obj))
85
+ return is_new
86
+
87
+ def update_agent_status(self, mac: str, status_data: dict):
88
+ with self.agents_lock:
89
+ if mac in self.agents:
90
+ if mac not in self.agents_status:
91
+ self.agents_status[mac] = {}
92
+ self.agents_status[mac].update(status_data)
93
+ self.update_queue.put(('status', mac, status_data))
94
+
95
+ def update_screenshot(self, mac: str, screenshot_data: bytes):
96
+ with self.screenshot_lock:
97
+ self.screenshot_cache[mac] = screenshot_data
98
+ self.update_queue.put(('screenshot', mac, screenshot_data))
99
+
100
+ def get_agent(self, mac: str) -> Optional[AgentData]:
101
+ with self.agents_lock:
102
+ return self.agents.get(mac)
103
+
104
+ def get_screenshot(self, mac: str) -> Optional[bytes]:
105
+ with self.screenshot_lock:
106
+ return self.screenshot_cache.get(mac)
107
+
108
+ def get_agents_by_domain(self, domain_path: str = "") -> List[tuple]:
109
+ with self.agents_lock:
110
+ agents = []
111
+ for mac, agent in self.agents.items():
112
+ if domain_path == "" or agent.domain == domain_path:
113
+ agents.append((mac, agent))
114
+ agents.sort(key=lambda x: x[1].id.lower())
115
+ return agents
116
+
117
+ def get_domain_hierarchy(self) -> dict:
118
+ hierarchy = {}
119
+ with self.agents_lock:
120
+ for mac, agent in self.agents.items():
121
+ domain = agent.domain
122
+ domain_parts = domain.split('/') if domain else ['']
123
+ current_level = hierarchy
124
+ for i, part in enumerate(domain_parts):
125
+ if part not in current_level:
126
+ current_level[part] = {'agents': [], 'subdomains': {}}
127
+ if i == len(domain_parts) - 1:
128
+ current_level[part]['agents'].append((mac, agent))
129
+ else:
130
+ current_level = current_level[part]['subdomains']
131
+ return hierarchy
132
+
133
+ def get_main_domains(self) -> Dict[str, int]:
134
+ hierarchy = self.get_domain_hierarchy()
135
+ main_domains = {}
136
+
137
+ def count_agents_recursive(data: dict) -> int:
138
+ count = len(data.get('agents', []))
139
+ for subdomain_data in data.get('subdomains', {}).values():
140
+ count += count_agents_recursive(subdomain_data)
141
+ return count
142
+
143
+ for domain, data in hierarchy.items():
144
+ main_domains[domain] = count_agents_recursive(data)
145
+ return dict(sorted(main_domains.items()))
146
+
147
+ def get_subdomains(self, domain_path: str) -> Dict[str, int]:
148
+ hierarchy = self.get_domain_hierarchy()
149
+ path_parts = domain_path.split('/') if domain_path else []
150
+ current_level = hierarchy
151
+ for part in path_parts:
152
+ if part in current_level:
153
+ current_level = current_level[part].get('subdomains', {})
154
+ else:
155
+ return {}
156
+
157
+ def count_agents_recursive(data: dict) -> int:
158
+ count = len(data.get('agents', []))
159
+ for subdomain_data in data.get('subdomains', {}).values():
160
+ count += count_agents_recursive(subdomain_data)
161
+ return count
162
+
163
+ subdomains = {}
164
+ for subdomain, data in current_level.items():
165
+ subdomains[subdomain] = count_agents_recursive(data)
166
+ return dict(sorted(subdomains.items()))
167
+
168
+ def count_agents(self) -> int:
169
+ with self.agents_lock:
170
+ return len(self.agents)
171
+
172
+ def get_visible_agents(self, domain_path: str = "", limit: int = 50) -> List[tuple]:
173
+ agents = self.get_agents_by_domain(domain_path)
174
+ return agents[:limit]
175
+
176
+ def save_to_file(self, filepath: str):
177
+ with self.agents_lock:
178
+ agents_dict = {}
179
+ for mac, agent in self.agents.items():
180
+ agents_dict[mac] = {
181
+ 'mac': agent.mac, 'id': agent.id, 'domain': agent.domain,
182
+ 'ip_public': agent.ip_public, 'ip_local': agent.ip_local,
183
+ 'description': agent.description, 'ver': agent.ver,
184
+ 'cpu': agent.cpu, 'os': agent.os, 'rotate': agent.rotate,
185
+ 'login': agent.login, 'ts': agent.ts, 'frozen': agent.frozen,
186
+ }
187
+ with open(filepath, 'w') as f:
188
+ json.dump(agents_dict, f, indent=4)
189
+
190
+ def load_from_file(self, filepath: str):
191
+ if not exists(filepath):
192
+ return
193
+ with open(filepath, 'r') as f:
194
+ agents_dict = json.load(f)
195
+ with self.agents_lock:
196
+ for mac, agent_data in agents_dict.items():
197
+ self.agents[mac] = AgentData(**agent_data)
@@ -0,0 +1,13 @@
1
+ """AI Chat module for Remote Scout."""
2
+
3
+ from remotescout.ai.chat_engine import AIChatEngine
4
+ from remotescout.ai.providers import LLMProvider, HttpApiProvider, HttpOllamaProvider, MqttOllamaProvider
5
+ from remotescout.ai.models import ChatMessage, MessageRole, ToolCall, ToolResult
6
+ from remotescout.ai.tools import TOOLS, ToolExecutor
7
+ from remotescout.ai.scripts import ScriptIndex
8
+
9
+ __all__ = [
10
+ "AIChatEngine", "LLMProvider", "HttpApiProvider", "HttpOllamaProvider", "MqttOllamaProvider",
11
+ "ChatMessage", "MessageRole", "ToolCall", "ToolResult",
12
+ "TOOLS", "ToolExecutor", "ScriptIndex",
13
+ ]