@follenfang/fupload 0.0.11 → 0.0.15
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.
- package/fupload/SKILL.md +1 -1
- package/fupload/references/modus.md +275 -30
- package/fupload/scripts/fupload_cli/__init__.py +1 -1
- package/fupload/scripts/fupload_cli/cli.py +59 -11
- package/fupload/scripts/fupload_cli/dd_broker.py +42 -14
- package/fupload/scripts/fupload_cli/io.py +17 -1
- package/fupload/scripts/fupload_cli/modus.py +571 -18
- package/fupload/scripts/fupload_cli/schema.py +105 -7
- package/fupload/scripts/fupload_cli/transport.py +13 -1
- package/npm/skill-manifest.json +19 -19
- package/package.json +1 -1
|
@@ -282,6 +282,31 @@ def _load_live_state() -> Optional[Dict[str, Any]]:
|
|
|
282
282
|
return value
|
|
283
283
|
|
|
284
284
|
|
|
285
|
+
def _remove_session_state(path: Path, session_id: str, timeout: float = 5) -> bool:
|
|
286
|
+
deadline = time.monotonic() + timeout
|
|
287
|
+
while True:
|
|
288
|
+
try:
|
|
289
|
+
current = _read_json(path)
|
|
290
|
+
except FuploadError as exc:
|
|
291
|
+
if isinstance(exc.__cause__, FileNotFoundError):
|
|
292
|
+
return True
|
|
293
|
+
if not isinstance(exc.__cause__, OSError):
|
|
294
|
+
return False
|
|
295
|
+
else:
|
|
296
|
+
if current.get("session_id") != session_id:
|
|
297
|
+
return False
|
|
298
|
+
try:
|
|
299
|
+
path.unlink()
|
|
300
|
+
return True
|
|
301
|
+
except FileNotFoundError:
|
|
302
|
+
return True
|
|
303
|
+
except OSError:
|
|
304
|
+
pass
|
|
305
|
+
if time.monotonic() >= deadline:
|
|
306
|
+
return False
|
|
307
|
+
time.sleep(0.05)
|
|
308
|
+
|
|
309
|
+
|
|
285
310
|
def doctor() -> Dict[str, Any]:
|
|
286
311
|
dd_dir, signature = _dd_module().discover_dd_info()
|
|
287
312
|
processes = running_dd_processes()
|
|
@@ -471,15 +496,23 @@ def status(session_id: Optional[str] = None) -> Dict[str, Any]:
|
|
|
471
496
|
|
|
472
497
|
def stop(session_id: str) -> Dict[str, Any]:
|
|
473
498
|
data = _send({"session_id": session_id, "command": "stop"}, timeout=30)
|
|
474
|
-
deadline = time.
|
|
475
|
-
while
|
|
499
|
+
deadline = time.monotonic() + 30
|
|
500
|
+
while True:
|
|
501
|
+
try:
|
|
502
|
+
state = _load_live_state()
|
|
503
|
+
except FuploadError as exc:
|
|
504
|
+
if exc.kind != "session_error" or not isinstance(exc.__cause__, OSError):
|
|
505
|
+
raise
|
|
506
|
+
else:
|
|
507
|
+
if not state:
|
|
508
|
+
break
|
|
509
|
+
if time.monotonic() >= deadline:
|
|
510
|
+
raise FuploadError(
|
|
511
|
+
"DD task session acknowledged stop but did not finish cleanup",
|
|
512
|
+
kind="session_stop_failed",
|
|
513
|
+
stage="session",
|
|
514
|
+
)
|
|
476
515
|
time.sleep(0.05)
|
|
477
|
-
if _load_live_state():
|
|
478
|
-
raise FuploadError(
|
|
479
|
-
"DD task session acknowledged stop but did not finish cleanup",
|
|
480
|
-
kind="session_stop_failed",
|
|
481
|
-
stage="session",
|
|
482
|
-
)
|
|
483
516
|
result = dict(data or {})
|
|
484
517
|
result["cleanup_complete"] = True
|
|
485
518
|
return result
|
|
@@ -615,12 +648,7 @@ def _serve(startup_id: str) -> int:
|
|
|
615
648
|
listener.close()
|
|
616
649
|
if sidecar is not None:
|
|
617
650
|
sidecar.__exit__(None, None, None)
|
|
618
|
-
|
|
619
|
-
current = _read_json(state_path)
|
|
620
|
-
if current.get("session_id") == session_id:
|
|
621
|
-
state_path.unlink()
|
|
622
|
-
except (FuploadError, OSError):
|
|
623
|
-
pass
|
|
651
|
+
_remove_session_state(state_path, session_id)
|
|
624
652
|
|
|
625
653
|
|
|
626
654
|
def main(argv: Optional[List[str]] = None) -> int:
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import base64
|
|
6
|
+
import binascii
|
|
5
7
|
import hashlib
|
|
6
8
|
import json
|
|
7
9
|
import sys
|
|
@@ -22,8 +24,10 @@ _SENSITIVE_KEYS = {
|
|
|
22
24
|
}
|
|
23
25
|
_RAW_CONTENT_KEYS = {
|
|
24
26
|
"content", "wa_str", "t_wa_str", "raw_wtf", "wtf_zip", "download_url",
|
|
25
|
-
"import_string",
|
|
27
|
+
"import_string", "content_text", "contenttext", "code_text", "codetext",
|
|
28
|
+
"description", "changelog", "license_content", "licensecontent",
|
|
26
29
|
}
|
|
30
|
+
_BASE64_CONTENT_KEYS = {"base64", "logo_base64", "screenshot_base64s"}
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
class _DuplicateKey(ValueError):
|
|
@@ -102,6 +106,18 @@ def sanitize_output(value: Any) -> Any:
|
|
|
102
106
|
"bytes": len(text),
|
|
103
107
|
"sha256": hashlib.sha256(text).hexdigest(),
|
|
104
108
|
}
|
|
109
|
+
elif normalized in _BASE64_CONTENT_KEYS and isinstance(item, str):
|
|
110
|
+
try:
|
|
111
|
+
content = base64.b64decode(item, validate=True)
|
|
112
|
+
encoding = "base64"
|
|
113
|
+
except (binascii.Error, ValueError):
|
|
114
|
+
content = item.encode("utf-8")
|
|
115
|
+
encoding = "utf-8"
|
|
116
|
+
result[str(key) + "_summary"] = {
|
|
117
|
+
"bytes": len(content),
|
|
118
|
+
"sha256": hashlib.sha256(content).hexdigest(),
|
|
119
|
+
"source_encoding": encoding,
|
|
120
|
+
}
|
|
105
121
|
else:
|
|
106
122
|
result[key] = sanitize_output(item)
|
|
107
123
|
return result
|