pentool 0.2.1__tar.gz → 0.2.2__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.
- {pentool-0.2.1 → pentool-0.2.2}/PKG-INFO +1 -1
- {pentool-0.2.1 → pentool-0.2.2}/pentool/__init__.py +1 -1
- {pentool-0.2.1 → pentool-0.2.2}/pentool/cli/main.py +27 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/license.py +86 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/app.py +30 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool.egg-info/PKG-INFO +1 -1
- {pentool-0.2.1 → pentool-0.2.2}/pyproject.toml +1 -1
- {pentool-0.2.1 → pentool-0.2.2}/LICENSE +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/README.md +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/__main__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/base_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/comparer_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/decoder_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/intruder_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/proxy_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/repeater_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/sequencer_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/spider_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/api/target_api.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/cli/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/cli/project.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/cli/proxy.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/cli/scan.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/config.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/crash_reporter.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/database.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/event_bus.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/events.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/features.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/logging.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/plugin_manager.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/project.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/storage_interface.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/updater.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/core/utils.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/comparer.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/decoder.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/intruder.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/match_replace.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/proxy.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/repeater.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/sequencer.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/spider.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/target.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/modules/websocket_handler.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/plugins/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/plugins/builtin/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/plugins/example_plugin.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/plugins/example_plugin.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/services/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/services/base_service.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/services/intruder_service.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/services/proxy_service.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/services/repeater_service.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/services/scan_service.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/storage/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/storage/http_storage.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/storage/large_body_handler.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/storage/lru_cache.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/t.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/constants.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/cert_dialog.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/cert_dialog.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/file_selector.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/file_selector.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/load_from_proxy.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/load_from_proxy.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/match_replace_dialog.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/match_replace_dialog.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/project_dialog.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/project_dialog.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/scope_dialog.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/dialogs/scope_dialog.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/messages.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/mixins/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/mixins/app_mixin.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/mixins/dialog_cancel.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/mixins/request_context_menu.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/project_manager.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/base.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/comparer/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/comparer/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/comparer/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/dashboard/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/dashboard/live_dashboard.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/dashboard/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/dashboard/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/decoder/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/decoder/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/decoder/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/extensions/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/extensions/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/extensions/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/intruder/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/intruder/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/intruder/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/proxy/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/proxy/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/proxy/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/repeater/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/repeater/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/repeater/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/scanner_unavailable.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/sequencer/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/sequencer/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/sequencer/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/settings/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/settings/hotkeys.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/settings/hotkeys.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/settings/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/settings/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/spider/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/spider/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/spider/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/target/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/target/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/target/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/terminal/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/terminal/screen.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/screens/terminal/screen.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/activity_indicator.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/activity_indicator.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/context_menu.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/context_menu.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/filter_bar.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/filter_bar.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/inspector_panel.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/inspector_panel.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/menu.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/menu_bar.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/module_tabs.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/module_tabs.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/payload_drop_zone.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/payload_drop_zone.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/request_editor.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/request_editor.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/resize_handle.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/resize_handle.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/search_bar.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/search_bar.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/statusbar.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/statusbar.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/toolbar_button.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/tui/widgets/toolbar_button.tcss +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/utils/__init__.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/utils/cert.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/utils/coder.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/utils/copy_as.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/utils/diff.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/utils/http_client.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/utils/parser.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool/utils/terminal_check.py +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool.egg-info/SOURCES.txt +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool.egg-info/dependency_links.txt +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool.egg-info/entry_points.txt +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool.egg-info/requires.txt +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/pentool.egg-info/top_level.txt +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/setup.cfg +0 -0
- {pentool-0.2.1 → pentool-0.2.2}/tests/test_user_workflow.py +0 -0
|
@@ -126,6 +126,7 @@ def update_cmd(check_only: bool) -> None:
|
|
|
126
126
|
click.echo("Upgrading via pip...")
|
|
127
127
|
if do_pip_upgrade():
|
|
128
128
|
click.echo("Upgrade successful. Restart Pentool to use the new version.")
|
|
129
|
+
_sync_pro_package_after_upgrade()
|
|
129
130
|
else:
|
|
130
131
|
click.echo(
|
|
131
132
|
"pip upgrade failed. You can update manually:\n"
|
|
@@ -135,6 +136,32 @@ def update_cmd(check_only: bool) -> None:
|
|
|
135
136
|
raise SystemExit(1)
|
|
136
137
|
|
|
137
138
|
|
|
139
|
+
def _sync_pro_package_after_upgrade() -> None:
|
|
140
|
+
"""Re-download the PRO package too, if one is active on this machine.
|
|
141
|
+
|
|
142
|
+
`pip install --upgrade pentool` only touches the FREE package — the PRO
|
|
143
|
+
package lives separately in ~/.pentool/pro/ and is otherwise only ever
|
|
144
|
+
(re)downloaded by check_and_update_pro_package(), which normally runs in
|
|
145
|
+
the background on TUI startup. Doing it here too means a CLI-driven
|
|
146
|
+
upgrade doesn't leave a stale PRO build around until the next TUI launch.
|
|
147
|
+
Best-effort — never fails the overall upgrade if this part doesn't work.
|
|
148
|
+
"""
|
|
149
|
+
import asyncio
|
|
150
|
+
|
|
151
|
+
from pentool.core.license import PRO_PACKAGE_DIR, check_and_update_pro_package
|
|
152
|
+
|
|
153
|
+
if not PRO_PACKAGE_DIR.exists():
|
|
154
|
+
return # PRO was never activated on this machine — nothing to sync
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
click.echo("Checking for a newer PRO build...")
|
|
158
|
+
updated = asyncio.run(check_and_update_pro_package())
|
|
159
|
+
if updated:
|
|
160
|
+
click.echo("PRO package updated to the latest build.")
|
|
161
|
+
except Exception as exc:
|
|
162
|
+
click.echo(f"Could not check for a PRO update: {exc}", err=True)
|
|
163
|
+
|
|
164
|
+
|
|
138
165
|
@cli.group()
|
|
139
166
|
def license() -> None:
|
|
140
167
|
"""License management: trial, activation, status."""
|
|
@@ -503,6 +503,13 @@ def _current_platform() -> str:
|
|
|
503
503
|
return "linux"
|
|
504
504
|
|
|
505
505
|
|
|
506
|
+
# File recording which PRO build is currently unpacked into PRO_PACKAGE_DIR —
|
|
507
|
+
# lets check_and_update_pro_package() tell "same build as last time" apart
|
|
508
|
+
# from "a new build was published" without re-downloading the archive to
|
|
509
|
+
# compare it. See _fetch_pro_build_id() / /api/pro/version on the Worker.
|
|
510
|
+
_PRO_BUILD_MARKER = PRO_PACKAGE_DIR / ".build_id"
|
|
511
|
+
|
|
512
|
+
|
|
506
513
|
async def download_pro_package(key: str, machine_id: str) -> bool:
|
|
507
514
|
"""Download, verify, and install the obfuscated PRO package.
|
|
508
515
|
|
|
@@ -555,6 +562,85 @@ async def download_pro_package(key: str, machine_id: str) -> bool:
|
|
|
555
562
|
finally:
|
|
556
563
|
tmp_archive.unlink(missing_ok=True)
|
|
557
564
|
|
|
565
|
+
# Record which build this is, so a later check_and_update_pro_package()
|
|
566
|
+
# can tell "nothing changed" from "a new build was published" without
|
|
567
|
+
# re-downloading the archive just to compare it.
|
|
568
|
+
try:
|
|
569
|
+
build_id = await _fetch_pro_build_id(plat)
|
|
570
|
+
if build_id:
|
|
571
|
+
_PRO_BUILD_MARKER.write_text(build_id, encoding="utf-8")
|
|
572
|
+
except Exception:
|
|
573
|
+
pass # non-fatal — worst case, next check just re-downloads once more
|
|
574
|
+
|
|
558
575
|
return True
|
|
559
576
|
except Exception:
|
|
560
577
|
return False
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
async def _fetch_pro_build_id(plat: str) -> str | None:
|
|
581
|
+
"""Query /api/pro/version for the current PRO release's build_id.
|
|
582
|
+
|
|
583
|
+
No license key needed — this only identifies which build is published,
|
|
584
|
+
not whether the caller is entitled to it (that's still enforced by
|
|
585
|
+
/api/download re-validating the key server-side). Returns None on any
|
|
586
|
+
failure (network, missing asset, etc.) — callers should treat that as
|
|
587
|
+
"couldn't check, don't act on it" rather than "no update available".
|
|
588
|
+
"""
|
|
589
|
+
try:
|
|
590
|
+
import aiohttp
|
|
591
|
+
async with aiohttp.ClientSession(
|
|
592
|
+
timeout=aiohttp.ClientTimeout(total=8)
|
|
593
|
+
) as session:
|
|
594
|
+
async with session.get(
|
|
595
|
+
f"{_LICENSE_API_BASE}/api/pro/version",
|
|
596
|
+
params={"platform": plat},
|
|
597
|
+
ssl=False,
|
|
598
|
+
) as resp:
|
|
599
|
+
if resp.status != 200:
|
|
600
|
+
return None
|
|
601
|
+
data = await resp.json()
|
|
602
|
+
return data.get("build_id")
|
|
603
|
+
except Exception:
|
|
604
|
+
return None
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
async def check_and_update_pro_package() -> bool:
|
|
608
|
+
"""Re-download the PRO package if a newer build has been published.
|
|
609
|
+
|
|
610
|
+
Called on TUI startup (background worker, same pattern as
|
|
611
|
+
_check_for_updates for the pip package) and after `pentool update` — this
|
|
612
|
+
is what closes the gap where pentool-pro ships a fix but a machine that
|
|
613
|
+
already activated its license keeps running the stale code from
|
|
614
|
+
~/.pentool/pro/ forever, because download_pro_package() otherwise only
|
|
615
|
+
ever runs once, at initial activation/trial-start.
|
|
616
|
+
|
|
617
|
+
Silently does nothing (returns False) if:
|
|
618
|
+
- there's no active PRO license (nothing to update)
|
|
619
|
+
- the PRO package was never downloaded in the first place (that's
|
|
620
|
+
activate_license's job, not this one)
|
|
621
|
+
- the version check or download fails for any reason
|
|
622
|
+
|
|
623
|
+
Returns True if a new build was found and successfully installed.
|
|
624
|
+
"""
|
|
625
|
+
info = get_license()
|
|
626
|
+
if not info.valid or not info.features:
|
|
627
|
+
return False
|
|
628
|
+
if not PRO_PACKAGE_DIR.exists():
|
|
629
|
+
return False # never activated on this machine — nothing to refresh
|
|
630
|
+
|
|
631
|
+
plat = _current_platform()
|
|
632
|
+
remote_build_id = await _fetch_pro_build_id(plat)
|
|
633
|
+
if not remote_build_id:
|
|
634
|
+
return False # couldn't reach the server — don't touch anything
|
|
635
|
+
|
|
636
|
+
local_build_id = None
|
|
637
|
+
try:
|
|
638
|
+
if _PRO_BUILD_MARKER.exists():
|
|
639
|
+
local_build_id = _PRO_BUILD_MARKER.read_text(encoding="utf-8").strip()
|
|
640
|
+
except Exception:
|
|
641
|
+
pass
|
|
642
|
+
|
|
643
|
+
if local_build_id == remote_build_id:
|
|
644
|
+
return False # already on the latest build
|
|
645
|
+
|
|
646
|
+
return await download_pro_package(info.license_key, info.machine_id)
|
|
@@ -408,6 +408,12 @@ class PentoolApp(App):
|
|
|
408
408
|
if getattr(self._cfg, "check_updates", True):
|
|
409
409
|
self.run_worker(self._check_for_updates(), exclusive=False, thread=False)
|
|
410
410
|
|
|
411
|
+
# Check for a newer PRO build in background (if a PRO license is
|
|
412
|
+
# active) — closes the gap where pentool-pro ships a fix but an
|
|
413
|
+
# already-activated machine keeps running the stale code forever,
|
|
414
|
+
# since the PRO package otherwise only downloads once, at activation.
|
|
415
|
+
self.run_worker(self._check_for_pro_update(), exclusive=False, thread=False)
|
|
416
|
+
|
|
411
417
|
async def _auto_open_last_project(self) -> None:
|
|
412
418
|
"""Open the last project from recent_projects at startup."""
|
|
413
419
|
await asyncio.sleep(0.2) # give Textual time to render the UI
|
|
@@ -439,6 +445,30 @@ class PentoolApp(App):
|
|
|
439
445
|
except Exception as exc:
|
|
440
446
|
logger.debug("APP: update check failed: %s", exc)
|
|
441
447
|
|
|
448
|
+
async def _check_for_pro_update(self) -> None:
|
|
449
|
+
"""Re-download the PRO package in the background if a newer build
|
|
450
|
+
has been published, then notify so the user knows to restart.
|
|
451
|
+
|
|
452
|
+
No-op (silently) if there's no active PRO license or the package was
|
|
453
|
+
never downloaded in the first place — see
|
|
454
|
+
pentool.core.license.check_and_update_pro_package for the actual
|
|
455
|
+
build_id comparison logic.
|
|
456
|
+
"""
|
|
457
|
+
import asyncio as _asyncio
|
|
458
|
+
await _asyncio.sleep(4.0) # after the pip-update check, UI settled
|
|
459
|
+
try:
|
|
460
|
+
from pentool.core.license import check_and_update_pro_package
|
|
461
|
+
updated = await check_and_update_pro_package()
|
|
462
|
+
if updated:
|
|
463
|
+
self.notify(
|
|
464
|
+
"PRO package updated to the latest build — restart Pentool to use it.",
|
|
465
|
+
severity="information",
|
|
466
|
+
timeout=10,
|
|
467
|
+
)
|
|
468
|
+
logger.info("APP: PRO package updated to a newer build")
|
|
469
|
+
except Exception as exc:
|
|
470
|
+
logger.debug("APP: PRO update check failed: %s", exc)
|
|
471
|
+
|
|
442
472
|
def _setup_auto_save(self) -> None:
|
|
443
473
|
"""Configure / restart the auto-save timer from the current config.
|
|
444
474
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|