@follenfang/fupload 0.0.16 → 0.0.17
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/scripts/fupload_cli/__init__.py +1 -1
- package/fupload/scripts/fupload_cli/dd.py +16 -5
- package/fupload/scripts/fupload_cli/dd_broker.py +25 -5
- package/fupload/scripts/fupload_cli/dd_sidecar.py +60 -10
- package/npm/skill-manifest.json +11 -11
- package/package.json +1 -1
package/fupload/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: fupload
|
|
3
3
|
description: Explicit author-publishing workflow for World of Warcraft plugins, configuration shares, and WA/strings on NewBeeBox, NetEase DD, CurseForge, Heybox Workshop, and ModUs.Creator, including local Creator login reuse and plugin ZIP publishing. Use only when the user explicitly invokes `$fupload`, explicitly asks to use the Fupload Skill, or loads this Skill by path. Do not trigger from ordinary mentions of publishing, NewBeeBox, DD, CurseForge, Heybox, ModUs, plugins, configurations, or WA.
|
|
4
4
|
metadata:
|
|
5
|
-
version: "0.0.
|
|
5
|
+
version: "0.0.17"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Fupload
|
|
@@ -240,6 +240,20 @@ def state_dir() -> Path:
|
|
|
240
240
|
return result
|
|
241
241
|
|
|
242
242
|
|
|
243
|
+
def _sidecar_startup_error(error: Mapping[str, Any]) -> FuploadError:
|
|
244
|
+
message = str(error.get("message") or "DD sidecar failed to start")
|
|
245
|
+
return FuploadError(
|
|
246
|
+
message,
|
|
247
|
+
kind=str(error.get("kind") or "authentication_error"),
|
|
248
|
+
stage=str(error.get("stage") or "session"),
|
|
249
|
+
endpoint=error.get("endpoint"),
|
|
250
|
+
http_status=error.get("http_status"),
|
|
251
|
+
business_code=error.get("business_code"),
|
|
252
|
+
verification_required=bool(error.get("verification_required")),
|
|
253
|
+
details=error.get("details") if isinstance(error.get("details"), dict) else None,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
243
257
|
class Sidecar:
|
|
244
258
|
def __init__(self) -> None:
|
|
245
259
|
self.dd_dir, self.signature = discover_dd_info()
|
|
@@ -272,11 +286,8 @@ class Sidecar:
|
|
|
272
286
|
raise
|
|
273
287
|
if not ready.get("ready"):
|
|
274
288
|
self.close()
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
kind="authentication_error",
|
|
278
|
-
stage="session",
|
|
279
|
-
)
|
|
289
|
+
error = ready.get("error") if isinstance(ready.get("error"), dict) else {}
|
|
290
|
+
raise _sidecar_startup_error(error)
|
|
280
291
|
credential_kind = ready.get("credential_kind")
|
|
281
292
|
if credential_kind not in ("email", "mobile"):
|
|
282
293
|
self.close()
|
|
@@ -13,7 +13,7 @@ import time
|
|
|
13
13
|
import uuid
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from types import SimpleNamespace
|
|
16
|
-
from typing import Any, Dict, List, Optional
|
|
16
|
+
from typing import Any, Dict, List, Mapping, Optional
|
|
17
17
|
|
|
18
18
|
from .errors import FuploadError, redact
|
|
19
19
|
from .trust import verify_dd_executable
|
|
@@ -406,6 +406,17 @@ def _send(value: Dict[str, Any], timeout: float = 300) -> Dict[str, Any]:
|
|
|
406
406
|
return response.get("data")
|
|
407
407
|
|
|
408
408
|
|
|
409
|
+
def _startup_error(pending: Mapping[str, Any]) -> FuploadError:
|
|
410
|
+
error = pending.get("error")
|
|
411
|
+
if isinstance(error, dict):
|
|
412
|
+
return FuploadError.from_dict(error)
|
|
413
|
+
return FuploadError(
|
|
414
|
+
str(error or "DD task session failed to start"),
|
|
415
|
+
kind="session_start_failed",
|
|
416
|
+
stage="session",
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
|
|
409
420
|
def start(confirm_close_gui: bool) -> Dict[str, Any]:
|
|
410
421
|
existing = _load_live_state()
|
|
411
422
|
if existing:
|
|
@@ -447,7 +458,7 @@ def start(confirm_close_gui: bool) -> Dict[str, Any]:
|
|
|
447
458
|
creationflags=flags,
|
|
448
459
|
)
|
|
449
460
|
deadline = time.time() + 90
|
|
450
|
-
last_error = ""
|
|
461
|
+
last_error: Any = ""
|
|
451
462
|
while time.time() < deadline:
|
|
452
463
|
state = _load_live_state()
|
|
453
464
|
if state and state.get("startup_id") == startup_id:
|
|
@@ -465,7 +476,7 @@ def start(confirm_close_gui: bool) -> Dict[str, Any]:
|
|
|
465
476
|
if startup.exists():
|
|
466
477
|
try:
|
|
467
478
|
pending = _read_json(startup)
|
|
468
|
-
last_error =
|
|
479
|
+
last_error = pending.get("error") or ""
|
|
469
480
|
except FuploadError:
|
|
470
481
|
pass
|
|
471
482
|
if process.poll() is not None:
|
|
@@ -487,10 +498,11 @@ def start(confirm_close_gui: bool) -> Dict[str, Any]:
|
|
|
487
498
|
try:
|
|
488
499
|
pending = _read_json(startup)
|
|
489
500
|
if pending.get("startup_id") == startup_id:
|
|
501
|
+
last_error = pending.get("error") or last_error
|
|
490
502
|
startup.unlink()
|
|
491
503
|
except (FuploadError, OSError):
|
|
492
504
|
pass
|
|
493
|
-
raise
|
|
505
|
+
raise _startup_error({"error": last_error})
|
|
494
506
|
|
|
495
507
|
|
|
496
508
|
def status(session_id: Optional[str] = None) -> Dict[str, Any]:
|
|
@@ -655,9 +667,17 @@ def _serve(startup_id: str) -> int:
|
|
|
655
667
|
connection.sendall((json.dumps(response, ensure_ascii=False, separators=(",", ":")) + "\n").encode("utf-8"))
|
|
656
668
|
return 0
|
|
657
669
|
except Exception as exc:
|
|
670
|
+
if isinstance(exc, FuploadError):
|
|
671
|
+
error = exc.as_dict()
|
|
672
|
+
else:
|
|
673
|
+
error = FuploadError(
|
|
674
|
+
redact(str(exc))[:400] or "DD task session failed to start",
|
|
675
|
+
kind="session_start_failed",
|
|
676
|
+
stage="session",
|
|
677
|
+
).as_dict()
|
|
658
678
|
_atomic_json(startup_path, {
|
|
659
679
|
"startup_id": startup_id,
|
|
660
|
-
"error":
|
|
680
|
+
"error": error,
|
|
661
681
|
})
|
|
662
682
|
return 1
|
|
663
683
|
finally:
|
|
@@ -362,12 +362,13 @@ def _validation_details(probe):
|
|
|
362
362
|
return result
|
|
363
363
|
|
|
364
364
|
|
|
365
|
-
def install_response_probe(client):
|
|
365
|
+
def install_response_probe(client, capture_success=False):
|
|
366
366
|
"""Capture response objects and HTTPError bodies without changing DD behavior."""
|
|
367
367
|
session = getattr(client, "_session", None)
|
|
368
368
|
restore = getattr(client, "_fupload_restore_response_probe", None)
|
|
369
369
|
if callable(restore):
|
|
370
370
|
restore()
|
|
371
|
+
client._fupload_capture_success = bool(capture_success)
|
|
371
372
|
original_opener_open = urllib.request.OpenerDirector.open
|
|
372
373
|
|
|
373
374
|
def opener_open(opener, *args, **kwargs):
|
|
@@ -400,6 +401,7 @@ def install_response_probe(client):
|
|
|
400
401
|
if getattr(urllib.request.OpenerDirector, "open", None) is opener_open:
|
|
401
402
|
urllib.request.OpenerDirector.open = original_opener_open
|
|
402
403
|
client._fupload_restore_response_probe = None
|
|
404
|
+
client._fupload_capture_success = False
|
|
403
405
|
|
|
404
406
|
client._fupload_restore_response_probe = restore_probe
|
|
405
407
|
for method_name in ("get", "post"):
|
|
@@ -415,7 +417,7 @@ def install_response_probe(client):
|
|
|
415
417
|
status = int(getattr(response, "status_code", 0) or 0)
|
|
416
418
|
except (TypeError, ValueError):
|
|
417
419
|
status = 0
|
|
418
|
-
if status >= 400:
|
|
420
|
+
if status >= 400 or getattr(client, "_fupload_capture_success", False):
|
|
419
421
|
try:
|
|
420
422
|
body = str(getattr(response, "text", "") or "")
|
|
421
423
|
except Exception:
|
|
@@ -507,6 +509,59 @@ def failure_from_exception(exc, stage, response=None):
|
|
|
507
509
|
)
|
|
508
510
|
|
|
509
511
|
|
|
512
|
+
def author_login_failure(client):
|
|
513
|
+
probe = getattr(client, "_fupload_last_response_error", None)
|
|
514
|
+
return failure_from_exception(
|
|
515
|
+
RuntimeError("DD author API login failed"), "session", probe,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def _last_response_payload(client):
|
|
520
|
+
probe = getattr(client, "_fupload_last_response_error", None)
|
|
521
|
+
if not isinstance(probe, dict) or not probe.get("body"):
|
|
522
|
+
return None
|
|
523
|
+
try:
|
|
524
|
+
payload = json.loads(probe["body"])
|
|
525
|
+
except (TypeError, ValueError):
|
|
526
|
+
return None
|
|
527
|
+
return payload if isinstance(payload, dict) else None
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def login_author_client(client):
|
|
531
|
+
try:
|
|
532
|
+
if client.login():
|
|
533
|
+
return
|
|
534
|
+
if _retryable_get_signature_rejection(_last_response_payload(client)):
|
|
535
|
+
client._fupload_last_response_error = None
|
|
536
|
+
if client.login():
|
|
537
|
+
return
|
|
538
|
+
raise author_login_failure(client)
|
|
539
|
+
finally:
|
|
540
|
+
client._fupload_capture_success = False
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def author_session_or_cleanup(qt, container, flow, jwt_helper, client):
|
|
544
|
+
session = (qt, container, flow, jwt_helper, client)
|
|
545
|
+
try:
|
|
546
|
+
login_author_client(client)
|
|
547
|
+
except Exception:
|
|
548
|
+
try:
|
|
549
|
+
close_session(session)
|
|
550
|
+
except Exception:
|
|
551
|
+
pass
|
|
552
|
+
raise
|
|
553
|
+
return session
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def startup_error_payload(exc):
|
|
557
|
+
if isinstance(exc, SidecarFailure):
|
|
558
|
+
return exc.as_dict()
|
|
559
|
+
return {
|
|
560
|
+
"type": type(exc).__name__,
|
|
561
|
+
"message": safe_exception_message(exc)[:400],
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
|
|
510
565
|
def bootstrap():
|
|
511
566
|
resource = os.path.join(DD_DIR, "ccvoicehub.res")
|
|
512
567
|
pyqt_dir = os.path.join(DD_DIR, "ccsub64", "PyQt5")
|
|
@@ -643,10 +698,8 @@ def open_session(timeout=45):
|
|
|
643
698
|
raise RuntimeError("DD NEP module is not initialized")
|
|
644
699
|
client = UiApiClient(nep, login_cookie=getattr(controller, "_cookie", None))
|
|
645
700
|
client._session.headers["User-Agent"] = USER_AGENT
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
install_response_probe(client)
|
|
649
|
-
return qt, container, flow, jwt_helper, client
|
|
701
|
+
install_response_probe(client, capture_success=True)
|
|
702
|
+
return author_session_or_cleanup(qt, container, flow, jwt_helper, client)
|
|
650
703
|
|
|
651
704
|
|
|
652
705
|
def api_result(payload):
|
|
@@ -905,10 +958,7 @@ def main():
|
|
|
905
958
|
"error": error})
|
|
906
959
|
return 0
|
|
907
960
|
except Exception as exc:
|
|
908
|
-
output({"ready": False, "error":
|
|
909
|
-
"type": type(exc).__name__,
|
|
910
|
-
"message": safe_exception_message(exc)[:400],
|
|
911
|
-
}})
|
|
961
|
+
output({"ready": False, "error": startup_error_payload(exc)})
|
|
912
962
|
return 1
|
|
913
963
|
finally:
|
|
914
964
|
machine_data.clientNo = original
|
package/npm/skill-manifest.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "fupload.npm-skill-manifest.v1",
|
|
3
3
|
"package_name": "@follenfang/fupload",
|
|
4
|
-
"package_version": "0.0.
|
|
5
|
-
"skill_version": "0.0.
|
|
6
|
-
"tree_sha256": "
|
|
4
|
+
"package_version": "0.0.17",
|
|
5
|
+
"skill_version": "0.0.17",
|
|
6
|
+
"tree_sha256": "064aeb1a1291d240a3bea7e71458846395abe29c6e91f682d269ec0e684c4294",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
9
9
|
"path": "agents/openai.yaml",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
{
|
|
114
114
|
"path": "scripts/fupload_cli/__init__.py",
|
|
115
115
|
"bytes": 50,
|
|
116
|
-
"sha256": "
|
|
116
|
+
"sha256": "8e72acb94a18f173cf9cbbf0436b20f64e1991f54a3537b7e930b4196bab8439"
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
"path": "scripts/fupload_cli/blackbox_web.py",
|
|
@@ -137,18 +137,18 @@
|
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
139
|
"path": "scripts/fupload_cli/dd_broker.py",
|
|
140
|
-
"bytes":
|
|
141
|
-
"sha256": "
|
|
140
|
+
"bytes": 28248,
|
|
141
|
+
"sha256": "6bbae4ed7aef4feca552816d7dbefa6069dc3d1d9a39a4af35582a06db4ce2d5"
|
|
142
142
|
},
|
|
143
143
|
{
|
|
144
144
|
"path": "scripts/fupload_cli/dd_sidecar.py",
|
|
145
|
-
"bytes":
|
|
146
|
-
"sha256": "
|
|
145
|
+
"bytes": 39150,
|
|
146
|
+
"sha256": "42791c82e5518838aedbe209823b94deb7de09b2f3eccb4fe563364f58becc6c"
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
149
|
"path": "scripts/fupload_cli/dd.py",
|
|
150
|
-
"bytes":
|
|
151
|
-
"sha256": "
|
|
150
|
+
"bytes": 114434,
|
|
151
|
+
"sha256": "cf808efdd4a93d80bddb533af4ac14696e889e0fd359acdacd58a7a49ce050b0"
|
|
152
152
|
},
|
|
153
153
|
{
|
|
154
154
|
"path": "scripts/fupload_cli/errors.py",
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
{
|
|
209
209
|
"path": "SKILL.md",
|
|
210
210
|
"bytes": 28383,
|
|
211
|
-
"sha256": "
|
|
211
|
+
"sha256": "9aebce883e6d83f0e3c8b36f497e59eb6f4df5e1e4af231df60ca3299188fb86"
|
|
212
212
|
}
|
|
213
213
|
]
|
|
214
214
|
}
|