@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.
- package/README.md +208 -3
- package/fupload/SKILL.md +129 -0
- package/fupload/agents/openai.yaml +4 -0
- package/fupload/examples/dd-config-delete.json +5 -0
- package/fupload/examples/dd-config-update.json +25 -0
- package/fupload/examples/dd-plugin-delete.json +5 -0
- package/fupload/examples/dd-plugin-update.json +9 -0
- package/fupload/examples/dd-wa-delete.json +5 -0
- package/fupload/examples/dd-wa-edit.json +9 -0
- package/fupload/examples/newbee-config-delete.json +5 -0
- package/fupload/examples/newbee-config-update.json +10 -0
- package/fupload/examples/newbee-plugin-create.json +14 -0
- package/fupload/examples/newbee-plugin-delete.json +5 -0
- package/fupload/examples/newbee-wa-delete.json +5 -0
- package/fupload/examples/newbee-wa-update.json +8 -0
- package/fupload/references/dd.md +105 -0
- package/fupload/references/newbee-official-cli.md +288 -0
- package/fupload/references/newbee.md +80 -0
- package/fupload/references/workflow.md +67 -0
- package/fupload/scripts/fupload.py +17 -0
- package/fupload/scripts/fupload_cli/__init__.py +3 -0
- package/fupload/scripts/fupload_cli/cli.py +266 -0
- package/fupload/scripts/fupload_cli/dd.py +2406 -0
- package/fupload/scripts/fupload_cli/dd_broker.py +634 -0
- package/fupload/scripts/fupload_cli/dd_sidecar.py +860 -0
- package/fupload/scripts/fupload_cli/errors.py +94 -0
- package/fupload/scripts/fupload_cli/io.py +125 -0
- package/fupload/scripts/fupload_cli/newbee.py +1412 -0
- package/fupload/scripts/fupload_cli/newbee_auth.py +135 -0
- package/fupload/scripts/fupload_cli/schema.py +539 -0
- package/fupload/scripts/fupload_cli/transport.py +125 -0
- package/fupload/scripts/fupload_cli/trust.py +207 -0
- package/npm/bin/fupload.mjs +90 -0
- package/npm/lib/managed-install.mjs +86 -0
- package/npm/lib/options.mjs +38 -0
- package/npm/lib/python.mjs +45 -0
- package/npm/lib/skill-installer.mjs +228 -0
- package/npm/lib/uninstall.mjs +211 -0
- package/npm/lib/update.mjs +99 -0
- package/npm/lib/versions.mjs +63 -0
- package/npm/postinstall.mjs +18 -0
- package/npm/skill-manifest.json +164 -0
- 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"}
|