portacode 1.4.17.dev8__py3-none-any.whl → 1.4.17.dev9__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.
- portacode/_version.py +2 -2
- portacode/service.py +109 -2
- {portacode-1.4.17.dev8.dist-info → portacode-1.4.17.dev9.dist-info}/METADATA +1 -1
- {portacode-1.4.17.dev8.dist-info → portacode-1.4.17.dev9.dist-info}/RECORD +8 -8
- {portacode-1.4.17.dev8.dist-info → portacode-1.4.17.dev9.dist-info}/WHEEL +0 -0
- {portacode-1.4.17.dev8.dist-info → portacode-1.4.17.dev9.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.17.dev8.dist-info → portacode-1.4.17.dev9.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.17.dev8.dist-info → portacode-1.4.17.dev9.dist-info}/top_level.txt +0 -0
portacode/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '1.4.17.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4, 17, '
|
|
31
|
+
__version__ = version = '1.4.17.dev9'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 17, 'dev9')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
portacode/service.py
CHANGED
|
@@ -6,6 +6,7 @@ runs ``portacode connect`` automatically at login / boot.
|
|
|
6
6
|
Platforms implemented:
|
|
7
7
|
|
|
8
8
|
• Linux (systemd **user** service) – no root privileges required
|
|
9
|
+
• Linux (OpenRC service) – system-wide (e.g., Alpine)
|
|
9
10
|
• macOS (launchd LaunchAgent plist) – per-user
|
|
10
11
|
• Windows (Task Scheduler *ONLOGON* task) – highest privilege, current user
|
|
11
12
|
|
|
@@ -26,6 +27,7 @@ import os
|
|
|
26
27
|
from typing import Protocol
|
|
27
28
|
import shutil
|
|
28
29
|
import pwd
|
|
30
|
+
import tempfile
|
|
29
31
|
|
|
30
32
|
__all__ = [
|
|
31
33
|
"ServiceManager",
|
|
@@ -199,6 +201,107 @@ class _SystemdUserService:
|
|
|
199
201
|
return (status or "") + "\n--- recent logs ---\n" + (journal or "<no logs>")
|
|
200
202
|
|
|
201
203
|
|
|
204
|
+
# ---------------------------------------------------------------------------
|
|
205
|
+
# Linux – OpenRC implementation (e.g., Alpine)
|
|
206
|
+
# ---------------------------------------------------------------------------
|
|
207
|
+
class _OpenRCService:
|
|
208
|
+
NAME = "portacode"
|
|
209
|
+
|
|
210
|
+
def __init__(self) -> None:
|
|
211
|
+
self.init_path = Path("/etc/init.d") / self.NAME
|
|
212
|
+
self.user = os.environ.get("SUDO_USER") or os.environ.get("USER") or os.getlogin()
|
|
213
|
+
try:
|
|
214
|
+
self.home = Path(pwd.getpwnam(self.user).pw_dir)
|
|
215
|
+
except KeyError:
|
|
216
|
+
self.home = Path("/root") if self.user == "root" else Path(f"/home/{self.user}")
|
|
217
|
+
self.python = shutil.which("python3") or sys.executable
|
|
218
|
+
self.log_dir = Path("/var/log/portacode")
|
|
219
|
+
self.log_path = self.log_dir / "connect.log"
|
|
220
|
+
|
|
221
|
+
def _run(self, *args: str) -> subprocess.CompletedProcess[str]:
|
|
222
|
+
prefix = ["sudo"] if os.geteuid() != 0 else []
|
|
223
|
+
cmd = [*prefix, *args]
|
|
224
|
+
return subprocess.run(cmd, text=True, capture_output=True)
|
|
225
|
+
|
|
226
|
+
def _write_init_script(self) -> None:
|
|
227
|
+
script = textwrap.dedent(f"""
|
|
228
|
+
#!/sbin/openrc-run
|
|
229
|
+
description="Portacode persistent connection"
|
|
230
|
+
|
|
231
|
+
command="{self.python}"
|
|
232
|
+
command_args="-m portacode connect --non-interactive"
|
|
233
|
+
command_user="{self.user}"
|
|
234
|
+
command_background="yes"
|
|
235
|
+
pidfile="/run/portacode.pid"
|
|
236
|
+
directory="{self.home}"
|
|
237
|
+
output_log="{self.log_path}"
|
|
238
|
+
error_log="{self.log_path}"
|
|
239
|
+
|
|
240
|
+
depend() {{
|
|
241
|
+
need net
|
|
242
|
+
}}
|
|
243
|
+
|
|
244
|
+
start_pre() {{
|
|
245
|
+
checkpath --directory --mode 0755 /var/log/portacode
|
|
246
|
+
}}
|
|
247
|
+
""").lstrip()
|
|
248
|
+
|
|
249
|
+
tmp_path = Path(tempfile.gettempdir()) / f"portacode-init-{os.getpid()}"
|
|
250
|
+
tmp_path.write_text(script)
|
|
251
|
+
if os.geteuid() != 0:
|
|
252
|
+
self._run("install", "-m", "755", str(tmp_path), str(self.init_path))
|
|
253
|
+
else:
|
|
254
|
+
self.init_path.parent.mkdir(parents=True, exist_ok=True)
|
|
255
|
+
shutil.copyfile(tmp_path, self.init_path)
|
|
256
|
+
self.init_path.chmod(0o755)
|
|
257
|
+
try:
|
|
258
|
+
tmp_path.unlink()
|
|
259
|
+
except Exception:
|
|
260
|
+
pass
|
|
261
|
+
|
|
262
|
+
def install(self) -> None:
|
|
263
|
+
self._write_init_script()
|
|
264
|
+
self._run("rc-update", "add", self.NAME, "default")
|
|
265
|
+
self._run("rc-service", self.NAME, "start")
|
|
266
|
+
|
|
267
|
+
def uninstall(self) -> None:
|
|
268
|
+
self._run("rc-service", self.NAME, "stop")
|
|
269
|
+
self._run("rc-update", "del", self.NAME, "default")
|
|
270
|
+
if self.init_path.exists():
|
|
271
|
+
if os.geteuid() != 0:
|
|
272
|
+
self._run("rm", "-f", str(self.init_path))
|
|
273
|
+
else:
|
|
274
|
+
self.init_path.unlink()
|
|
275
|
+
|
|
276
|
+
def start(self) -> None:
|
|
277
|
+
self._run("rc-service", self.NAME, "start")
|
|
278
|
+
|
|
279
|
+
def stop(self) -> None:
|
|
280
|
+
self._run("rc-service", self.NAME, "stop")
|
|
281
|
+
|
|
282
|
+
def status(self) -> str:
|
|
283
|
+
res = self._run("rc-service", self.NAME, "status")
|
|
284
|
+
out = (res.stdout or res.stderr or "").strip().lower()
|
|
285
|
+
if "started" in out or "running" in out:
|
|
286
|
+
return "running"
|
|
287
|
+
if "stopped" in out:
|
|
288
|
+
return "stopped"
|
|
289
|
+
return out or "unknown"
|
|
290
|
+
|
|
291
|
+
def status_verbose(self) -> str:
|
|
292
|
+
res = self._run("rc-service", self.NAME, "status")
|
|
293
|
+
status = res.stdout or res.stderr or ""
|
|
294
|
+
log_tail = "<no logs>"
|
|
295
|
+
try:
|
|
296
|
+
if self.log_path.exists():
|
|
297
|
+
with self.log_path.open("r", encoding="utf-8", errors="ignore") as fh:
|
|
298
|
+
lines = fh.readlines()
|
|
299
|
+
log_tail = "".join(lines[-20:]) or "<no logs>"
|
|
300
|
+
except Exception:
|
|
301
|
+
pass
|
|
302
|
+
return (status or "").rstrip() + "\n--- recent logs ---\n" + log_tail
|
|
303
|
+
|
|
304
|
+
|
|
202
305
|
# ---------------------------------------------------------------------------
|
|
203
306
|
# macOS – launchd (LaunchAgent) implementation
|
|
204
307
|
# ---------------------------------------------------------------------------
|
|
@@ -422,9 +525,13 @@ class _WindowsTask:
|
|
|
422
525
|
def get_manager(system_mode: bool = False) -> ServiceManager:
|
|
423
526
|
system = platform.system().lower()
|
|
424
527
|
if system == "linux":
|
|
425
|
-
|
|
528
|
+
if shutil.which("systemctl"):
|
|
529
|
+
return _SystemdUserService(system_mode=system_mode) # type: ignore[return-value]
|
|
530
|
+
if shutil.which("rc-service") or Path("/sbin/openrc").exists():
|
|
531
|
+
return _OpenRCService() # type: ignore[return-value]
|
|
532
|
+
raise RuntimeError("Unsupported Linux init system (no systemctl or rc-service found)")
|
|
426
533
|
if system == "darwin":
|
|
427
534
|
return _LaunchdService() # type: ignore[return-value]
|
|
428
535
|
if system.startswith("windows") or system == "windows":
|
|
429
536
|
return _WindowsTask() # type: ignore[return-value]
|
|
430
|
-
raise RuntimeError(f"Unsupported platform: {system}")
|
|
537
|
+
raise RuntimeError(f"Unsupported platform: {system}")
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
portacode/README.md,sha256=4dKtpvR8LNgZPVz37GmkQCMWIr_u25Ao63iW56s7Ke4,775
|
|
2
2
|
portacode/__init__.py,sha256=oB3sV1wXr-um-RXio73UG8E5Xx6cF2ZVJveqjNmC-vQ,1086
|
|
3
3
|
portacode/__main__.py,sha256=jmHTGC1hzmo9iKJLv-SSYe9BSIbPPZ2IOpecI03PlTs,296
|
|
4
|
-
portacode/_version.py,sha256=
|
|
4
|
+
portacode/_version.py,sha256=vNmWumwOSakJSXsv8x_EdHyiL23srCSUrbOcexyXbKc,719
|
|
5
5
|
portacode/cli.py,sha256=mGLKoZ-T2FBF7IA9wUq0zyG0X9__-A1ao7gajjcVRH8,21828
|
|
6
6
|
portacode/data.py,sha256=5-s291bv8J354myaHm1Y7CQZTZyRzMU3TGe5U4hb-FA,1591
|
|
7
7
|
portacode/keypair.py,sha256=0OO4vHDcF1XMxCDqce61xFTlFwlTcmqe5HyGsXFEt7s,5838
|
|
8
8
|
portacode/logging_categories.py,sha256=9m-BYrjyHh1vjZYBQT4JhAh6b_oYUhIWayO-noH1cSE,5063
|
|
9
9
|
portacode/pairing.py,sha256=OzSuc0GhrknrDrny4aBU6IUnmKzRDTtocuDpyaVnyrs,3116
|
|
10
|
-
portacode/service.py,sha256=
|
|
10
|
+
portacode/service.py,sha256=RkXWeTDT0t3rJ3szt_qlKR2P_SXIMUcFSl70BfIJpjo,20651
|
|
11
11
|
portacode/connection/README.md,sha256=f9rbuIEKa7cTm9C98rCiBbEtbiIXQU11esGSNhSMiJg,883
|
|
12
12
|
portacode/connection/__init__.py,sha256=atqcVGkViIEd7pRa6cP2do07RJOM0UWpbnz5zXjGktU,250
|
|
13
13
|
portacode/connection/client.py,sha256=jtLb9_YufqPkzi9t8VQH3iz_JEMisbtY6a8L9U5weiU,14181
|
|
@@ -65,7 +65,7 @@ portacode/utils/__init__.py,sha256=NgBlWTuNJESfIYJzP_3adI1yJQJR0XJLRpSdVNaBAN0,3
|
|
|
65
65
|
portacode/utils/diff_apply.py,sha256=4Oi7ft3VUCKmiUE4VM-OeqO7Gk6H7PF3WnN4WHXtjxI,15157
|
|
66
66
|
portacode/utils/diff_renderer.py,sha256=S76StnQ2DLfsz4Gg0m07UwPfRp8270PuzbNaQq-rmYk,13850
|
|
67
67
|
portacode/utils/ntp_clock.py,sha256=VqCnWCTehCufE43W23oB-WUdAZGeCcLxkmIOPwInYHc,2499
|
|
68
|
-
portacode-1.4.17.
|
|
68
|
+
portacode-1.4.17.dev9.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
|
|
69
69
|
test_modules/README.md,sha256=Do_agkm9WhSzueXjRAkV_xEj6Emy5zB3N3VKY5Roce8,9274
|
|
70
70
|
test_modules/__init__.py,sha256=1LcbHodIHsB0g-g4NGjSn6AMuCoGbymvXPYLOb6Z7F0,53
|
|
71
71
|
test_modules/test_device_online.py,sha256=QtYq0Dq9vME8Gp2O4fGSheqVf8LUtpsSKosXXk56gGM,1654
|
|
@@ -91,8 +91,8 @@ testing_framework/core/playwright_manager.py,sha256=Tw46qwxIhOFkS48C2IWIQHHNpEe-
|
|
|
91
91
|
testing_framework/core/runner.py,sha256=j2QwNJmAxVBmJvcbVS7DgPJUKPNzqfLmt_4NNdaKmZU,19297
|
|
92
92
|
testing_framework/core/shared_cli_manager.py,sha256=BESSNtyQb7BOlaOvZmm04T8Uezjms4KCBs2MzTxvzYQ,8790
|
|
93
93
|
testing_framework/core/test_discovery.py,sha256=2FZ9fJ8Dp5dloA-fkgXoJ_gCMC_nYPBnA3Hs2xlagzM,4928
|
|
94
|
-
portacode-1.4.17.
|
|
95
|
-
portacode-1.4.17.
|
|
96
|
-
portacode-1.4.17.
|
|
97
|
-
portacode-1.4.17.
|
|
98
|
-
portacode-1.4.17.
|
|
94
|
+
portacode-1.4.17.dev9.dist-info/METADATA,sha256=unCc_gEcNxw9Xt0mzn9Q7uPd9ENflwl8_o_LBb_uH4E,13051
|
|
95
|
+
portacode-1.4.17.dev9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
portacode-1.4.17.dev9.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.17.dev9.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.17.dev9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|