@follenfang/fupload 0.0.0-bootstrap.0 → 0.0.1

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.
Files changed (43) hide show
  1. package/README.md +208 -3
  2. package/fupload/SKILL.md +129 -0
  3. package/fupload/agents/openai.yaml +4 -0
  4. package/fupload/examples/dd-config-delete.json +5 -0
  5. package/fupload/examples/dd-config-update.json +25 -0
  6. package/fupload/examples/dd-plugin-delete.json +5 -0
  7. package/fupload/examples/dd-plugin-update.json +9 -0
  8. package/fupload/examples/dd-wa-delete.json +5 -0
  9. package/fupload/examples/dd-wa-edit.json +9 -0
  10. package/fupload/examples/newbee-config-delete.json +5 -0
  11. package/fupload/examples/newbee-config-update.json +10 -0
  12. package/fupload/examples/newbee-plugin-create.json +14 -0
  13. package/fupload/examples/newbee-plugin-delete.json +5 -0
  14. package/fupload/examples/newbee-wa-delete.json +5 -0
  15. package/fupload/examples/newbee-wa-update.json +8 -0
  16. package/fupload/references/dd.md +105 -0
  17. package/fupload/references/newbee-official-cli.md +288 -0
  18. package/fupload/references/newbee.md +80 -0
  19. package/fupload/references/workflow.md +67 -0
  20. package/fupload/scripts/fupload.py +17 -0
  21. package/fupload/scripts/fupload_cli/__init__.py +3 -0
  22. package/fupload/scripts/fupload_cli/cli.py +266 -0
  23. package/fupload/scripts/fupload_cli/dd.py +2406 -0
  24. package/fupload/scripts/fupload_cli/dd_broker.py +634 -0
  25. package/fupload/scripts/fupload_cli/dd_sidecar.py +860 -0
  26. package/fupload/scripts/fupload_cli/errors.py +94 -0
  27. package/fupload/scripts/fupload_cli/io.py +125 -0
  28. package/fupload/scripts/fupload_cli/newbee.py +1412 -0
  29. package/fupload/scripts/fupload_cli/newbee_auth.py +135 -0
  30. package/fupload/scripts/fupload_cli/schema.py +539 -0
  31. package/fupload/scripts/fupload_cli/transport.py +125 -0
  32. package/fupload/scripts/fupload_cli/trust.py +207 -0
  33. package/npm/bin/fupload.mjs +90 -0
  34. package/npm/lib/managed-install.mjs +86 -0
  35. package/npm/lib/options.mjs +38 -0
  36. package/npm/lib/python.mjs +45 -0
  37. package/npm/lib/skill-installer.mjs +228 -0
  38. package/npm/lib/uninstall.mjs +211 -0
  39. package/npm/lib/update.mjs +99 -0
  40. package/npm/lib/versions.mjs +63 -0
  41. package/npm/postinstall.mjs +18 -0
  42. package/npm/skill-manifest.json +164 -0
  43. package/package.json +50 -6
@@ -0,0 +1,135 @@
1
+ """Reuse the current user's NewBeeBox desktop authentication state."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import json
7
+ import os
8
+ import socket
9
+ import tempfile
10
+ import time
11
+ import urllib.parse
12
+ import urllib.request
13
+ from pathlib import Path
14
+ from typing import Dict, Tuple
15
+
16
+ from .errors import FuploadError
17
+ from .transport import json_request
18
+ from .trust import NEWBEE_ORIGINS, official_opener, require_official_url, trusted_roaming_dir
19
+
20
+
21
+ API_BASE = NEWBEE_ORIGINS["creator"]
22
+ AUTH_BASE = NEWBEE_ORIGINS["auth"] + "/auth"
23
+ API_ORIGIN = "creator"
24
+ AUTH_ORIGIN = "auth"
25
+
26
+
27
+ def _auth_dir() -> Path:
28
+ return auth_store_dir()
29
+
30
+
31
+ def auth_store_dir() -> Path:
32
+ return trusted_roaming_dir("NewBeeBox", "auth-store")
33
+
34
+
35
+ def _read(name: str, optional: bool = False) -> str:
36
+ try:
37
+ return (_auth_dir() / name).read_text(encoding="utf-8").strip()
38
+ except OSError as exc:
39
+ if optional:
40
+ return ""
41
+ raise FuploadError(
42
+ "NewBeeBox desktop login state is missing; sign in with the desktop client first",
43
+ kind="authentication_error",
44
+ ) from exc
45
+
46
+
47
+ def _jwt_fresh(token: str, leeway: int = 30) -> bool:
48
+ try:
49
+ part = token.split(".")[1]
50
+ part += "=" * ((4 - len(part) % 4) % 4)
51
+ claims = json.loads(base64.urlsafe_b64decode(part.encode()).decode("utf-8"))
52
+ return int(claims["exp"]) > int(time.time()) + leeway
53
+ except (ValueError, KeyError, IndexError, TypeError):
54
+ return False
55
+
56
+
57
+ def _atomic_write(path: Path, value: str) -> None:
58
+ path.parent.mkdir(parents=True, exist_ok=True)
59
+ fd, temporary = tempfile.mkstemp(prefix=".fupload-credential-", dir=str(path.parent))
60
+ try:
61
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
62
+ handle.write(value)
63
+ os.replace(temporary, path)
64
+ finally:
65
+ try:
66
+ os.unlink(temporary)
67
+ except OSError:
68
+ pass
69
+
70
+
71
+ def _refresh(access: str, refresh: str, proof: str) -> Tuple[str, str, str]:
72
+ if not refresh:
73
+ raise FuploadError("NewBeeBox refresh token is missing; sign in again", kind="authentication_error")
74
+ form = {
75
+ "client_id": "nbb-desktop", "grant_type": "refresh_token", "refresh_token": refresh,
76
+ "device_name": socket.gethostname(), "device_type": "desktop",
77
+ }
78
+ if proof:
79
+ form["device_proof"] = proof
80
+ request = urllib.request.Request(
81
+ require_official_url(AUTH_BASE + "/connect/token", AUTH_ORIGIN, path_prefix="/auth/"),
82
+ data=urllib.parse.urlencode(form).encode(),
83
+ method="POST",
84
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
85
+ )
86
+ try:
87
+ with official_opener(NEWBEE_ORIGINS[AUTH_ORIGIN]).open(request, timeout=60) as response:
88
+ payload = json.loads(response.read().decode("utf-8"))
89
+ except FuploadError:
90
+ raise
91
+ except Exception as exc:
92
+ raise FuploadError("cannot refresh NewBeeBox desktop session", kind="authentication_error") from exc
93
+ access = str(payload.get("access_token") or "")
94
+ refresh = str(payload.get("refresh_token") or refresh)
95
+ proof = str(payload.get("device_proof") or proof)
96
+ if not access:
97
+ raise FuploadError("NewBeeBox refresh response did not contain an access token", kind="authentication_error")
98
+ for name, value in (("access-token", access), ("refresh-token", refresh), ("device-proof", proof)):
99
+ if value:
100
+ _atomic_write(_auth_dir() / name, value)
101
+ return access, refresh, proof
102
+
103
+
104
+ def creator_headers() -> Dict[str, str]:
105
+ access, refresh, proof = _read("access-token"), _read("refresh-token"), _read("device-proof", True)
106
+ if not _jwt_fresh(access):
107
+ access, refresh, proof = _refresh(access, refresh, proof)
108
+ handoff = json_request(
109
+ API_BASE + "/v3/user/auth2web", method="POST", trusted_service=API_ORIGIN,
110
+ headers={"Authorization": "Bearer " + access, "boxversion": "1.1.17", "Accept-Language": "zh-CN"},
111
+ body={},
112
+ )
113
+ if handoff.get("code") != 1:
114
+ raise FuploadError("NewBeeBox Creator handoff failed", kind="authentication_error")
115
+ web_code = str((handoff.get("data") or {}).get("code") or "")
116
+ exchange = json_request(
117
+ API_BASE + "/v3/user/exchange_web_code", method="POST", trusted_service=API_ORIGIN,
118
+ headers={"appId": "6", "Accept-Language": "zh-CN"}, body={"code": web_code},
119
+ )
120
+ data = exchange.get("data") or {}
121
+ author = str(data.get("token") or "")
122
+ initial = str(data.get("jwtToken") or "")
123
+ if exchange.get("code") != 1 or not author:
124
+ raise FuploadError("NewBeeBox Creator token exchange failed", kind="authentication_error")
125
+ headers = {"appId": "6", "token": author, "Accept-Language": "zh-CN"}
126
+ if initial:
127
+ headers["Authorization"] = "Bearer " + initial
128
+ resource = json_request(
129
+ API_BASE + "/v3/user/refresh_web_resource_token", method="POST", headers=headers,
130
+ body={}, trusted_service=API_ORIGIN,
131
+ )
132
+ token = str((resource.get("data") or {}).get("resource_token") or "")
133
+ if resource.get("code") != 1 or not token:
134
+ raise FuploadError("NewBeeBox Creator resource token refresh failed", kind="authentication_error")
135
+ return {"appId": "6", "token": author, "Authorization": "Bearer " + token, "Accept-Language": "zh-CN"}