webpilot-engine 2.0.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.
- adapters/__init__.py +14 -0
- adapters/base.py +34 -0
- adapters/browser_adapter.py +300 -0
- adapters/dom_scripts.py +518 -0
- adapters/envelope_builder.py +108 -0
- adapters/live_session_manager.py +326 -0
- adapters/upload_adapter.py +77 -0
- builder.py +83 -0
- cli.py +341 -0
- config/__init__.py +23 -0
- config/settings.py +339 -0
- constants/__init__.py +165 -0
- constants/contracts.py +95 -0
- constants/selectors.py +27 -0
- constants/timeouts.py +25 -0
- core/__init__.py +45 -0
- core/exceptions.py +33 -0
- core/models.py +273 -0
- engine.py +199 -0
- exceptions.py +23 -0
- mcp_server.py +347 -0
- models.py +29 -0
- orchestration/__init__.py +19 -0
- orchestration/context.py +71 -0
- orchestration/flow_orchestrator.py +358 -0
- orchestration/session_coordinator.py +88 -0
- services/__init__.py +17 -0
- services/auth_navigation_service.py +129 -0
- services/field_interaction_service.py +174 -0
- services/form_filler_service.py +106 -0
- services/inspection_formatter_service.py +68 -0
- services/inspection_service.py +40 -0
- services/reactive_interaction_service.py +174 -0
- supervisor/__init__.py +13 -0
- supervisor/client.py +171 -0
- supervisor/contracts.py +62 -0
- supervisor/master_daemon.py +303 -0
- supervisor/shell.py +194 -0
- supervisor/worker_process.py +400 -0
- validators/__init__.py +9 -0
- validators/form_validator.py +28 -0
- validators/payload_validator.py +25 -0
- webpilot_engine-2.0.0.dist-info/METADATA +299 -0
- webpilot_engine-2.0.0.dist-info/RECORD +47 -0
- webpilot_engine-2.0.0.dist-info/WHEEL +5 -0
- webpilot_engine-2.0.0.dist-info/entry_points.txt +6 -0
- webpilot_engine-2.0.0.dist-info/top_level.txt +14 -0
cli.py
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"""WebPilot: Autonomous Multi-Tier Browser Automation Engine & AI Agent Web Runtime."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any, Dict
|
|
10
|
+
|
|
11
|
+
from orchestration.context import ApplyRequestContext, InspectionRequestContext
|
|
12
|
+
from orchestration.flow_orchestrator import FlowOrchestrator
|
|
13
|
+
from services.inspection_formatter_service import InspectionFormatterService
|
|
14
|
+
from supervisor.client import DEFAULT_SUPERVISOR_URL, SupervisorClient
|
|
15
|
+
from supervisor.contracts import SupervisorActionRequest, SupervisorActionResponse
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def print_telemetry_banner(resp: SupervisorActionResponse) -> None:
|
|
19
|
+
"""Renders real-time process tree telemetry across Layer 1, 2, and 3."""
|
|
20
|
+
if resp.browser_pid or resp.worker_pid or resp.active_url:
|
|
21
|
+
print("\n" + "═" * 70)
|
|
22
|
+
print(" 🚀 WEBPILOT | AUTONOMOUS BROWSER RUNTIME & SUPERVISOR TELEMETRY")
|
|
23
|
+
print("═" * 70)
|
|
24
|
+
if resp.worker_pid:
|
|
25
|
+
print(f" • Layer 2 Worker PID : {resp.worker_pid}")
|
|
26
|
+
if resp.browser_pid:
|
|
27
|
+
print(f" • Layer 3 Chromium PID : {resp.browser_pid}")
|
|
28
|
+
if resp.active_tab_index is not None:
|
|
29
|
+
print(f" • Active Tab Index : Tab #{resp.active_tab_index} (Total: {resp.total_tabs})")
|
|
30
|
+
if resp.active_title:
|
|
31
|
+
print(f" • Active Page Title : {resp.active_title}")
|
|
32
|
+
if resp.active_url:
|
|
33
|
+
print(f" • Active Page URL : {resp.active_url}")
|
|
34
|
+
print("═" * 70)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_base_url(args: argparse.Namespace) -> str:
|
|
38
|
+
host = getattr(args, "host", None) or "127.0.0.1"
|
|
39
|
+
port = getattr(args, "port", None) or 9333
|
|
40
|
+
return f"http://{host}:{port}"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def inspect_cmd(args: argparse.Namespace) -> None:
|
|
44
|
+
"""Executes the universal inspection workflow."""
|
|
45
|
+
if getattr(args, "standalone", False):
|
|
46
|
+
req = InspectionRequestContext(
|
|
47
|
+
url=args.url,
|
|
48
|
+
output_path=args.output,
|
|
49
|
+
screenshot_path=args.screenshot,
|
|
50
|
+
cookies_path=args.cookies,
|
|
51
|
+
unpack_options=args.unpack_options,
|
|
52
|
+
headed=args.headed,
|
|
53
|
+
config_path=args.config,
|
|
54
|
+
device_profile_path=args.device_profile,
|
|
55
|
+
env_file=args.env_file,
|
|
56
|
+
keep_alive=args.keep_alive,
|
|
57
|
+
close_session=args.close_session,
|
|
58
|
+
list_tabs=args.list_tabs,
|
|
59
|
+
tab_index=args.tab,
|
|
60
|
+
timeout_minutes=args.timeout_minutes,
|
|
61
|
+
)
|
|
62
|
+
FlowOrchestrator.execute_inspection_flow(req)
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
# Multi-tier Master Supervisor execution (default)
|
|
66
|
+
client = SupervisorClient(base_url=get_base_url(args))
|
|
67
|
+
|
|
68
|
+
if args.close_session:
|
|
69
|
+
res = client.stop_service()
|
|
70
|
+
print(f"[✓] {res.message}")
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
if args.list_tabs:
|
|
74
|
+
resp = client.execute(SupervisorActionRequest(action="list_tabs"))
|
|
75
|
+
print("\n" + "═" * 70)
|
|
76
|
+
print(" 📑 OPEN BROWSER TABS")
|
|
77
|
+
print("═" * 70)
|
|
78
|
+
if not resp.tabs:
|
|
79
|
+
print(" [!] No open tabs detected in current session.")
|
|
80
|
+
else:
|
|
81
|
+
for t in resp.tabs:
|
|
82
|
+
active_mark = " [*ACTIVE*]" if t.get("is_active") else ""
|
|
83
|
+
print(f" • Tab #{t['index']}{active_mark}: '{t['title']}'\n URL: {t['url']}")
|
|
84
|
+
print("═" * 70 + "\n")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
req = SupervisorActionRequest(
|
|
88
|
+
action="inspect",
|
|
89
|
+
url=args.url,
|
|
90
|
+
cookies_path=args.cookies,
|
|
91
|
+
screenshot_path=args.screenshot,
|
|
92
|
+
tab_index=args.tab,
|
|
93
|
+
unpack_options=args.unpack_options,
|
|
94
|
+
headed=args.headed,
|
|
95
|
+
timeout_minutes=args.timeout_minutes,
|
|
96
|
+
)
|
|
97
|
+
resp = client.execute(req)
|
|
98
|
+
print_telemetry_banner(resp)
|
|
99
|
+
for line in resp.output_lines:
|
|
100
|
+
print(line)
|
|
101
|
+
|
|
102
|
+
if resp.schema:
|
|
103
|
+
if args.output:
|
|
104
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
105
|
+
json.dump(resp.schema, f, indent=2, ensure_ascii=False)
|
|
106
|
+
print(f"[✓] Full schema successfully saved to: {args.output}")
|
|
107
|
+
summary = InspectionFormatterService.format_summary(resp.schema)
|
|
108
|
+
print("\n" + summary)
|
|
109
|
+
|
|
110
|
+
if not resp.success:
|
|
111
|
+
print(f"[!] Inspection failed: {resp.message}")
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def apply_cmd(args: argparse.Namespace) -> None:
|
|
116
|
+
"""Executes the universal form filling and submission workflow."""
|
|
117
|
+
auto_inspect = not getattr(args, "no_auto_inspect", False)
|
|
118
|
+
|
|
119
|
+
if getattr(args, "standalone", False):
|
|
120
|
+
req = ApplyRequestContext(
|
|
121
|
+
url=args.url,
|
|
122
|
+
data_path=args.data,
|
|
123
|
+
fill_arguments=args.fill or [],
|
|
124
|
+
press_buttons=args.press or [],
|
|
125
|
+
upload_files=args.upload or [],
|
|
126
|
+
cookies_path=args.cookies,
|
|
127
|
+
submit=args.submit,
|
|
128
|
+
screenshot_path=args.screenshot,
|
|
129
|
+
headed=args.headed,
|
|
130
|
+
config_path=args.config,
|
|
131
|
+
device_profile_path=args.device_profile,
|
|
132
|
+
env_file=args.env_file,
|
|
133
|
+
keep_alive=args.keep_alive,
|
|
134
|
+
close_session=args.close_session,
|
|
135
|
+
list_tabs=args.list_tabs,
|
|
136
|
+
tab_index=args.tab,
|
|
137
|
+
timeout_minutes=args.timeout_minutes,
|
|
138
|
+
auto_inspect=auto_inspect,
|
|
139
|
+
)
|
|
140
|
+
FlowOrchestrator.execute_apply_flow(req)
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
# Multi-tier Master Supervisor execution (default)
|
|
144
|
+
client = SupervisorClient(base_url=get_base_url(args))
|
|
145
|
+
|
|
146
|
+
if args.close_session:
|
|
147
|
+
res = client.stop_service()
|
|
148
|
+
print(f"[✓] {res.message}")
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
if args.list_tabs:
|
|
152
|
+
resp = client.execute(SupervisorActionRequest(action="list_tabs"))
|
|
153
|
+
print("\n" + "═" * 70)
|
|
154
|
+
print(" 📑 OPEN BROWSER TABS")
|
|
155
|
+
print("═" * 70)
|
|
156
|
+
if not resp.tabs:
|
|
157
|
+
print(" [!] No open tabs detected in current session.")
|
|
158
|
+
else:
|
|
159
|
+
for t in resp.tabs:
|
|
160
|
+
active_mark = " [*ACTIVE*]" if t.get("is_active") else ""
|
|
161
|
+
print(f" • Tab #{t['index']}{active_mark}: '{t['title']}'\n URL: {t['url']}")
|
|
162
|
+
print("═" * 70 + "\n")
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
req = SupervisorActionRequest(
|
|
166
|
+
action="apply",
|
|
167
|
+
url=args.url,
|
|
168
|
+
data_path=args.data,
|
|
169
|
+
fill_arguments=args.fill or [],
|
|
170
|
+
press_buttons=args.press or [],
|
|
171
|
+
upload_files=args.upload or [],
|
|
172
|
+
cookies_path=args.cookies,
|
|
173
|
+
submit=args.submit,
|
|
174
|
+
screenshot_path=args.screenshot,
|
|
175
|
+
headed=args.headed,
|
|
176
|
+
auto_inspect=auto_inspect,
|
|
177
|
+
tab_index=args.tab,
|
|
178
|
+
timeout_minutes=args.timeout_minutes,
|
|
179
|
+
)
|
|
180
|
+
resp = client.execute(req)
|
|
181
|
+
print_telemetry_banner(resp)
|
|
182
|
+
for line in resp.output_lines:
|
|
183
|
+
print(line)
|
|
184
|
+
|
|
185
|
+
if resp.schema and auto_inspect:
|
|
186
|
+
summary = InspectionFormatterService.format_summary(resp.schema)
|
|
187
|
+
print("\n" + summary)
|
|
188
|
+
|
|
189
|
+
if not resp.success:
|
|
190
|
+
print(f"[!] Apply operation failed: {resp.message}")
|
|
191
|
+
sys.exit(1)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def service_cmd(args: argparse.Namespace) -> None:
|
|
195
|
+
"""Manages the Master Supervisor daemon (status, restart, stop, start)."""
|
|
196
|
+
client = SupervisorClient(base_url=get_base_url(args))
|
|
197
|
+
action = args.service_action.lower()
|
|
198
|
+
|
|
199
|
+
if action == "status":
|
|
200
|
+
status_info = client.get_status()
|
|
201
|
+
print("\n" + "═" * 70)
|
|
202
|
+
print(" 🛡️ MASTER SUPERVISOR & MULTI-TIER DAEMON STATUS")
|
|
203
|
+
print("═" * 70)
|
|
204
|
+
if not status_info.get("supervisor_alive"):
|
|
205
|
+
print(" • Layer 1 Supervisor : ⭕ STOPPED / INACTIVE")
|
|
206
|
+
print(" • Layer 2 Worker : ⭕ INACTIVE")
|
|
207
|
+
print(" • Layer 3 Chromium : ⭕ INACTIVE")
|
|
208
|
+
else:
|
|
209
|
+
print(f" • Layer 1 Supervisor : 🟢 RUNNING (PID: {status_info.get('supervisor_pid')}, Port: {status_info.get('supervisor_port')})")
|
|
210
|
+
worker_alive = status_info.get("worker_alive", False)
|
|
211
|
+
worker_mark = "🟢 RUNNING" if worker_alive else "⚪ IDLE"
|
|
212
|
+
print(f" • Layer 2 Worker : {worker_mark} (PID: {status_info.get('worker_pid') or 'None'})")
|
|
213
|
+
print(f" • Watchdog Inactivity : {status_info.get('idle_seconds', 0)}s idle / {status_info.get('inactivity_timeout_seconds')}s limit")
|
|
214
|
+
|
|
215
|
+
if worker_alive:
|
|
216
|
+
worker_resp = client.execute(SupervisorActionRequest(action="status"))
|
|
217
|
+
if worker_resp.browser_pid:
|
|
218
|
+
print(f" • Layer 3 Chromium : 🟢 RUNNING (PID: {worker_resp.browser_pid})")
|
|
219
|
+
print(f" • Active Page : {worker_resp.active_title} ({worker_resp.active_url})")
|
|
220
|
+
print(f" • Open Tabs Count : {worker_resp.total_tabs}")
|
|
221
|
+
else:
|
|
222
|
+
print(" • Layer 3 Chromium : ⚪ NOT SPAWNED YET")
|
|
223
|
+
print("═" * 70 + "\n")
|
|
224
|
+
|
|
225
|
+
elif action == "restart":
|
|
226
|
+
res = client.restart_worker()
|
|
227
|
+
print(f"[*] {res.message}")
|
|
228
|
+
|
|
229
|
+
elif action == "stop":
|
|
230
|
+
res = client.stop_service()
|
|
231
|
+
print(f"[✓] {res.message}")
|
|
232
|
+
|
|
233
|
+
elif action == "start":
|
|
234
|
+
client.ensure_running()
|
|
235
|
+
print("[✓] Master Supervisor is active and listening.")
|
|
236
|
+
|
|
237
|
+
else:
|
|
238
|
+
print(f"[!] Unknown service action: '{action}'. Use status, restart, stop, or start.")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def shell_cmd(args: argparse.Namespace) -> None:
|
|
242
|
+
"""Launches the interactive REPL shell."""
|
|
243
|
+
from supervisor.shell import run_interactive_shell
|
|
244
|
+
run_interactive_shell(host=args.host, port=args.port)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def main():
|
|
248
|
+
if "--run-daemon" in sys.argv:
|
|
249
|
+
from supervisor.master_daemon import run_master_daemon
|
|
250
|
+
idx = sys.argv.index("--run-daemon")
|
|
251
|
+
port = 9333
|
|
252
|
+
if len(sys.argv) > idx + 1 and sys.argv[idx + 1].isdigit():
|
|
253
|
+
port = int(sys.argv[idx + 1])
|
|
254
|
+
run_master_daemon(port=port)
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
if "--run-worker" in sys.argv:
|
|
258
|
+
from supervisor.worker_process import run_worker_loop
|
|
259
|
+
run_worker_loop()
|
|
260
|
+
return
|
|
261
|
+
|
|
262
|
+
parser = argparse.ArgumentParser(
|
|
263
|
+
prog="webpilot",
|
|
264
|
+
description="WebPilot: Autonomous Multi-Tier Browser Automation Engine & AI Agent Web Runtime",
|
|
265
|
+
)
|
|
266
|
+
parser.add_argument("--config", help="Path to custom settings.json configuration file")
|
|
267
|
+
parser.add_argument("--device-profile", help="Path to custom device_profile.json configuration file")
|
|
268
|
+
parser.add_argument("--env-file", help="Path to custom .env file")
|
|
269
|
+
parser.add_argument("--host", default="127.0.0.1", help="Supervisor host address (default: 127.0.0.1)")
|
|
270
|
+
parser.add_argument("--port", type=int, default=9333, help="Supervisor port (default: 9333)")
|
|
271
|
+
|
|
272
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
273
|
+
|
|
274
|
+
# 1. Inspect Subcommand
|
|
275
|
+
p_inspect = subparsers.add_parser("inspect", help="Inspect URL and return full schema of inputs, dropdowns, buttons, uploads")
|
|
276
|
+
p_inspect.add_argument("--url", nargs="?", default=None, help="Target URL to inspect (optional if connecting to existing live session)")
|
|
277
|
+
p_inspect.add_argument("--cookies", help="Path to cookies JSON file")
|
|
278
|
+
p_inspect.add_argument("--output", help="Path to save full inspection JSON schema")
|
|
279
|
+
p_inspect.add_argument("--screenshot", help="Path to save visual screenshot")
|
|
280
|
+
p_inspect.add_argument("--unpack-options", action="store_true", help="Probe dropdowns to sample options (burns more tokens/time)")
|
|
281
|
+
p_inspect.add_argument("--headed", action="store_true", help="Run headed browser")
|
|
282
|
+
p_inspect.add_argument("--keep-alive", action="store_true", help="Keep browser open across sequential commands")
|
|
283
|
+
p_inspect.add_argument("--close-session", action="store_true", help="Terminate active live browser session and exit")
|
|
284
|
+
p_inspect.add_argument("--list-tabs", action="store_true", help="List all open tabs in active session")
|
|
285
|
+
p_inspect.add_argument("--tab", type=int, default=None, help="Target specific tab by index (0-based)")
|
|
286
|
+
p_inspect.add_argument("--timeout-minutes", type=int, default=30, help="Inactivity timeout in minutes (default: 30, 0 = disable)")
|
|
287
|
+
p_inspect.add_argument("--standalone", action="store_true", help="Run in-process via FlowOrchestrator instead of Master Supervisor")
|
|
288
|
+
p_inspect.add_argument("--host", default="127.0.0.1", help="Supervisor host address")
|
|
289
|
+
p_inspect.add_argument("--port", type=int, default=9333, help="Supervisor port")
|
|
290
|
+
p_inspect.add_argument("--config", help="Path to custom settings.json configuration file")
|
|
291
|
+
p_inspect.add_argument("--device-profile", help="Path to custom device_profile.json configuration file")
|
|
292
|
+
p_inspect.add_argument("--env-file", help="Path to custom .env file")
|
|
293
|
+
|
|
294
|
+
# 2. Apply / Fill Subcommand
|
|
295
|
+
p_apply = subparsers.add_parser("apply", help="Fill fields dynamically via JSON file or CLI flags, and optionally submit")
|
|
296
|
+
p_apply.add_argument("--url", nargs="?", default=None, help="Target URL to fill (optional if connecting to existing live session)")
|
|
297
|
+
p_apply.add_argument("--data", help="Path to JSON file containing key-value mappings")
|
|
298
|
+
p_apply.add_argument("--fill", action="append", help="Field to set, e.g. --fill 'First Name=John' (can be repeated)")
|
|
299
|
+
p_apply.add_argument("--press", action="append", help="Button to click, e.g. --press 'Next' (can be repeated)")
|
|
300
|
+
p_apply.add_argument("--upload", action="append", help="File to upload, e.g. --upload 'resume=sample.pdf' (can be repeated)")
|
|
301
|
+
p_apply.add_argument("--cookies", help="Path to cookies JSON file")
|
|
302
|
+
p_apply.add_argument("--submit", action="store_true", help="Click Apply/Submit after filling")
|
|
303
|
+
p_apply.add_argument("--screenshot", help="Path to save screenshot")
|
|
304
|
+
p_apply.add_argument("--headed", action="store_true", help="Run headed browser")
|
|
305
|
+
p_apply.add_argument("--keep-alive", action="store_true", help="Keep browser open across sequential commands")
|
|
306
|
+
p_apply.add_argument("--close-session", action="store_true", help="Terminate active live browser session and exit")
|
|
307
|
+
p_apply.add_argument("--list-tabs", action="store_true", help="List all open tabs in active session")
|
|
308
|
+
p_apply.add_argument("--tab", type=int, default=None, help="Target specific tab by index (0-based)")
|
|
309
|
+
p_apply.add_argument("--timeout-minutes", type=int, default=30, help="Inactivity timeout in minutes (default: 30, 0 = disable)")
|
|
310
|
+
p_apply.add_argument("--no-auto-inspect", action="store_true", help="Disable automatic post-interaction form inspection")
|
|
311
|
+
p_apply.add_argument("--standalone", action="store_true", help="Run in-process via FlowOrchestrator instead of Master Supervisor")
|
|
312
|
+
p_apply.add_argument("--host", default="127.0.0.1", help="Supervisor host address")
|
|
313
|
+
p_apply.add_argument("--port", type=int, default=9333, help="Supervisor port")
|
|
314
|
+
p_apply.add_argument("--config", help="Path to custom settings.json configuration file")
|
|
315
|
+
p_apply.add_argument("--device-profile", help="Path to custom device_profile.json configuration file")
|
|
316
|
+
p_apply.add_argument("--env-file", help="Path to custom .env file")
|
|
317
|
+
|
|
318
|
+
# 3. Interactive REPL Shell Subcommand
|
|
319
|
+
p_shell = subparsers.add_parser("shell", help="Launch interactive REPL automation shell")
|
|
320
|
+
p_shell.add_argument("--host", default="127.0.0.1", help="Supervisor host address")
|
|
321
|
+
p_shell.add_argument("--port", type=int, default=9333, help="Supervisor port")
|
|
322
|
+
|
|
323
|
+
# 4. Supervisor Service Management Subcommand
|
|
324
|
+
p_service = subparsers.add_parser("service", help="Manage Master Supervisor background daemon")
|
|
325
|
+
p_service.add_argument("service_action", choices=["status", "restart", "stop", "start"], help="Action to execute")
|
|
326
|
+
p_service.add_argument("--host", default="127.0.0.1", help="Supervisor host address")
|
|
327
|
+
p_service.add_argument("--port", type=int, default=9333, help="Supervisor port")
|
|
328
|
+
|
|
329
|
+
args = parser.parse_args()
|
|
330
|
+
if args.command == "inspect":
|
|
331
|
+
inspect_cmd(args)
|
|
332
|
+
elif args.command == "apply":
|
|
333
|
+
apply_cmd(args)
|
|
334
|
+
elif args.command == "shell":
|
|
335
|
+
shell_cmd(args)
|
|
336
|
+
elif args.command == "service":
|
|
337
|
+
service_cmd(args)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
if __name__ == "__main__":
|
|
341
|
+
main()
|
config/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Configuration package for Universal Web Applier."""
|
|
2
|
+
|
|
3
|
+
from config.settings import (
|
|
4
|
+
Settings,
|
|
5
|
+
get_settings,
|
|
6
|
+
load_device_profile,
|
|
7
|
+
reset_settings_cache,
|
|
8
|
+
PROJECT_ROOT,
|
|
9
|
+
CONFIG_DIR,
|
|
10
|
+
DEFAULT_SETTINGS_JSON,
|
|
11
|
+
DEFAULT_DEVICE_PROFILE_JSON,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Settings",
|
|
16
|
+
"get_settings",
|
|
17
|
+
"load_device_profile",
|
|
18
|
+
"reset_settings_cache",
|
|
19
|
+
"PROJECT_ROOT",
|
|
20
|
+
"CONFIG_DIR",
|
|
21
|
+
"DEFAULT_SETTINGS_JSON",
|
|
22
|
+
"DEFAULT_DEVICE_PROFILE_JSON",
|
|
23
|
+
]
|