@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
|
@@ -17,6 +17,7 @@ JSON_TYPES = {
|
|
|
17
17
|
"boolean": (bool,),
|
|
18
18
|
"array": (list,),
|
|
19
19
|
"object": (dict,),
|
|
20
|
+
"identifier": (int, str),
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
|
|
@@ -98,6 +99,52 @@ class Schema:
|
|
|
98
99
|
return checked
|
|
99
100
|
|
|
100
101
|
def _validate_conditionals(self, value: Dict[str, Any]) -> None:
|
|
102
|
+
if self.name.startswith("fupload.v1.modus.config"):
|
|
103
|
+
exclude = value.get("exclude_wtf")
|
|
104
|
+
if exclude == 1 and (value.get("account_name") or value.get("role_name")):
|
|
105
|
+
raise ValidationError("account_name and role_name must be empty when exclude_wtf=1", path="$.account_name")
|
|
106
|
+
if exclude == 0 and not str(value.get("account_name") or "").strip():
|
|
107
|
+
raise ValidationError("account_name is required when exclude_wtf=0", path="$.account_name")
|
|
108
|
+
if self.name.startswith(("fupload.v1.modus.config", "fupload.v1.modus.wa")):
|
|
109
|
+
if "title" in value and len(str(value["title"]).strip()) < 6:
|
|
110
|
+
raise ValidationError("must contain at least 6 characters", path="$.title")
|
|
111
|
+
if "content_text" in value and len(str(value["content_text"]).strip()) <= 20:
|
|
112
|
+
raise ValidationError("must contain more than 20 characters", path="$.content_text")
|
|
113
|
+
if "tags" in value:
|
|
114
|
+
tags = [item.strip() for item in str(value["tags"]).split(",")]
|
|
115
|
+
if any(not item for item in tags) or not 1 <= len(tags) <= 3:
|
|
116
|
+
raise ValidationError("must contain 1 to 3 comma-delimited tag IDs", path="$.tags")
|
|
117
|
+
if len(set(tags)) != len(tags):
|
|
118
|
+
raise ValidationError("must not contain duplicate tag IDs", path="$.tags")
|
|
119
|
+
if "image_url" in value:
|
|
120
|
+
images = [item.strip() for item in str(value["image_url"]).split(",")]
|
|
121
|
+
if any(not item for item in images) or not 1 <= len(images) <= 10:
|
|
122
|
+
raise ValidationError("must contain 1 to 10 comma-delimited image references", path="$.image_url")
|
|
123
|
+
if value.get("required_tier_id") is not None and (
|
|
124
|
+
value.get("platform") == 3 or value.get("synchronization_type") == 3
|
|
125
|
+
):
|
|
126
|
+
raise ValidationError(
|
|
127
|
+
"required_tier_id cannot be combined with BigFoot synchronization",
|
|
128
|
+
path="$.required_tier_id",
|
|
129
|
+
)
|
|
130
|
+
if value.get("is_paid", 0) != 0:
|
|
131
|
+
raise ValidationError("current ModUs form requires is_paid=0", path="$.is_paid")
|
|
132
|
+
if value.get("price", 0) != 0:
|
|
133
|
+
raise ValidationError("current ModUs form requires price=0", path="$.price")
|
|
134
|
+
if value.get("share_type", 0) != 0:
|
|
135
|
+
raise ValidationError("current ModUs form requires share_type=0", path="$.share_type")
|
|
136
|
+
platform = value.get("platform")
|
|
137
|
+
synchronization = value.get("synchronization_type")
|
|
138
|
+
if (platform is None) != (synchronization is None):
|
|
139
|
+
raise ValidationError(
|
|
140
|
+
"platform and synchronization_type must be provided together",
|
|
141
|
+
path="$.synchronization_type" if synchronization is None else "$.platform",
|
|
142
|
+
)
|
|
143
|
+
if platform is not None and synchronization is not None and platform != synchronization:
|
|
144
|
+
raise ValidationError(
|
|
145
|
+
"platform and synchronization_type must select the same ModUs/BigFoot targets",
|
|
146
|
+
path="$.synchronization_type",
|
|
147
|
+
)
|
|
101
148
|
if self.name in ("fupload.v1.modus.project.create", "fupload.v1.modus.project.edit"):
|
|
102
149
|
snapshot = value.get("project_state")
|
|
103
150
|
if snapshot is None:
|
|
@@ -424,14 +471,28 @@ class Schema:
|
|
|
424
471
|
if "categories" in value and len(value["categories"]) > 5:
|
|
425
472
|
raise ValidationError("must contain at most 5 items", path="$.categories")
|
|
426
473
|
if "image_ops" in value:
|
|
427
|
-
|
|
474
|
+
if not isinstance(value["image_ops"], list) or not value["image_ops"]:
|
|
475
|
+
raise ValidationError("expected nonempty array", path="$.image_ops")
|
|
428
476
|
if "images" not in value:
|
|
429
477
|
raise ValidationError("images is required when image_ops is supplied", path="$.images")
|
|
430
478
|
for index, operation in enumerate(value["image_ops"]):
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
479
|
+
path = "$.image_ops[%d]" % index
|
|
480
|
+
if not isinstance(operation, dict):
|
|
481
|
+
raise ValidationError("expected object", path=path)
|
|
482
|
+
op = operation.get("op")
|
|
483
|
+
if op not in ("upload", "delete", "rename"):
|
|
484
|
+
raise ValidationError("must be upload, delete, or rename", path=path + ".op")
|
|
485
|
+
allowed = {"op", "from", "to"} if op == "rename" else {"op", "name", "base64"}
|
|
486
|
+
unknown = sorted(set(operation) - allowed)
|
|
487
|
+
if unknown:
|
|
488
|
+
raise ValidationError("unknown field", path=path + "." + unknown[0])
|
|
489
|
+
if op == "rename":
|
|
490
|
+
for name in ("from", "to"):
|
|
491
|
+
if not isinstance(operation.get(name), str) or not operation[name].strip():
|
|
492
|
+
raise ValidationError("must not be empty", path=path + "." + name)
|
|
493
|
+
continue
|
|
494
|
+
if not isinstance(operation.get("name"), str) or not operation["name"].strip():
|
|
495
|
+
raise ValidationError("must not be empty", path=path + ".name")
|
|
435
496
|
if operation["op"] == "upload" and (not isinstance(operation.get("base64"), str) or not operation["base64"].strip()):
|
|
436
497
|
raise ValidationError("base64 is required for upload", path="$.image_ops[%d].base64" % index)
|
|
437
498
|
if operation["op"] == "delete" and "base64" in operation:
|
|
@@ -439,7 +500,7 @@ class Schema:
|
|
|
439
500
|
for name in ("version", "type"):
|
|
440
501
|
if name in value and isinstance(value[name], str) and not value[name].strip():
|
|
441
502
|
raise ValidationError("must not be empty", path="$.%s" % name)
|
|
442
|
-
if "file" in value and not zipfile.is_zipfile(value["file"]):
|
|
503
|
+
if self.name.startswith("fupload.v1.modus.plugin") and "file" in value and not zipfile.is_zipfile(value["file"]):
|
|
443
504
|
raise ValidationError("file must be a valid ZIP archive", path="$.file")
|
|
444
505
|
|
|
445
506
|
|
|
@@ -693,6 +754,31 @@ MODUS_RELEASE = {
|
|
|
693
754
|
"file": f("string", local_file=True),
|
|
694
755
|
"transaction_log": f("string", max_length=1000),
|
|
695
756
|
}
|
|
757
|
+
MODUS_BUILD_ID = f("integer", minimum=0, maximum=4)
|
|
758
|
+
MODUS_SHARE = {
|
|
759
|
+
"share_id": f("identifier", nonempty=True), "addons_id": f("string", max_length=10000),
|
|
760
|
+
"account_name": f("string", nullable=True, max_length=120), "backup_id": f("integer", minimum=1),
|
|
761
|
+
"content": f("string", nonempty=True), "content_text": f("string", nonempty=True),
|
|
762
|
+
"image_url": f("string", nonempty=True, max_length=1000), "is_paid": f("integer", choices=(0, 1)),
|
|
763
|
+
"is_public": f("integer", choices=(0, 1)), "price": f("number", minimum=0),
|
|
764
|
+
"share_type": f("integer", minimum=0), "tags": f("string", nonempty=True, max_length=1000),
|
|
765
|
+
"title": f("string", nonempty=True, max_length=120), "exclude_wtf": f("integer", choices=(0, 1)),
|
|
766
|
+
"role_name": f("string", nullable=True, max_length=120), "required_tier_id": f("integer", nullable=True, minimum=1),
|
|
767
|
+
"sub_type": f("integer", choices=(0, 1)), "platform": f("integer", choices=(1, 3)),
|
|
768
|
+
"synchronization_type": f("integer", choices=(1, 3)), "server_type": MODUS_BUILD_ID,
|
|
769
|
+
}
|
|
770
|
+
MODUS_IMPORT = {
|
|
771
|
+
"import_id": f("identifier", nonempty=True), "code_text": f("string", nonempty=True),
|
|
772
|
+
"content": f("string", nonempty=True), "addons_id": f("string", nonempty=True, max_length=10000),
|
|
773
|
+
"content_text": f("string", nonempty=True), "file_path": f("string", choices=("",)),
|
|
774
|
+
"image_url": f("string", nonempty=True, max_length=1000), "is_paid": f("integer", choices=(0, 1)),
|
|
775
|
+
"is_public": f("integer", choices=(0, 1)), "price": f("number", minimum=0),
|
|
776
|
+
"share_type": f("integer", minimum=0), "support_addon": f("string", nonempty=True, max_length=120),
|
|
777
|
+
"tags": f("string", nonempty=True, max_length=1000), "title": f("string", nonempty=True, max_length=120),
|
|
778
|
+
"version": f("string", nonempty=True, max_length=120), "required_tier_id": f("integer", nullable=True, minimum=1),
|
|
779
|
+
"sub_type": f("integer", choices=(0, 1)), "platform": f("integer", choices=(1, 3)),
|
|
780
|
+
"synchronization_type": f("integer", choices=(1, 3)), "server_type": MODUS_BUILD_ID,
|
|
781
|
+
}
|
|
696
782
|
register("modus", "project", "create", required(MODUS_PROJECT_CREATE, ("project_state",)))
|
|
697
783
|
register("modus", "project", "edit", required(with_id(MODUS_PROJECT_EDIT, "project_id"), ("project_id", "project_state")))
|
|
698
784
|
register("modus", "project", "delete", required({"project_id": f("integer", minimum=1), "confirm": f("string", choices=("DELETE",))}, ("project_id", "confirm")))
|
|
@@ -701,7 +787,19 @@ register("modus", "plugin", "upload", required(MODUS_RELEASE, ("project_id", "fi
|
|
|
701
787
|
register("modus", "plugin", "update", required(MODUS_RELEASE, ("project_id", "file_id")))
|
|
702
788
|
register("modus", "plugin", "edit", required(MODUS_RELEASE, ("project_id", "file_id")))
|
|
703
789
|
register("modus", "plugin", "delete", required({"project_id": f("integer", minimum=1), "file_id": f("integer", minimum=1), "confirm": f("string", choices=("DELETE",))}, ("project_id", "file_id", "confirm")))
|
|
704
|
-
|
|
790
|
+
register("modus", "media", "upload", required({"file": f("string", local_file=True)}, ("file",)))
|
|
791
|
+
register("modus", "config", "create", required(MODUS_SHARE, ("addons_id", "backup_id", "content", "content_text", "image_url", "tags", "title", "exclude_wtf")))
|
|
792
|
+
register("modus", "config", "update", required(MODUS_SHARE, ("share_id",)))
|
|
793
|
+
register("modus", "config", "edit", required(MODUS_SHARE, ("share_id",)))
|
|
794
|
+
register("modus", "config", "delete", required({"share_id": f("identifier", nonempty=True), "confirm": f("string", choices=("DELETE",)), "server_type": MODUS_BUILD_ID}, ("share_id", "confirm")))
|
|
795
|
+
register("modus", "config", "backup-edit", required({"backup_id": f("integer", minimum=1), "backup_name": f("string", nonempty=True, max_length=200), "server_type": MODUS_BUILD_ID}, ("backup_id", "backup_name")))
|
|
796
|
+
register("modus", "config", "backup-delete", required({"backup_id": f("integer", minimum=1), "confirm": f("string", choices=("DELETE",)), "server_type": MODUS_BUILD_ID}, ("backup_id", "confirm")))
|
|
797
|
+
register("modus", "wa", "create", required(MODUS_IMPORT, ("code_text", "content", "addons_id", "content_text", "image_url", "support_addon", "tags", "title", "version")))
|
|
798
|
+
register("modus", "wa", "update", required(MODUS_IMPORT, ("import_id",)))
|
|
799
|
+
register("modus", "wa", "edit", required(MODUS_IMPORT, ("import_id",)))
|
|
800
|
+
register("modus", "wa", "delete", required({"import_id": f("identifier", nonempty=True), "confirm": f("string", choices=("DELETE",)), "server_type": MODUS_BUILD_ID}, ("import_id", "confirm")))
|
|
801
|
+
register("modus", "wa", "version-publish", required({"import_id": f("identifier", nonempty=True), "version": f("string", nonempty=True, max_length=120), "code_text": f("string", nonempty=True), "changelog": f("string", max_length=10000), "server_type": MODUS_BUILD_ID}, ("import_id", "version", "code_text")))
|
|
802
|
+
register("modus", "wa", "version-delete", required({"version_id": f("identifier", nonempty=True), "confirm": f("string", choices=("DELETE",)), "server_type": MODUS_BUILD_ID}, ("version_id", "confirm")))
|
|
705
803
|
register("blackbox", "plugin", "edit", required({
|
|
706
804
|
"id": f("integer"), "name": f("string"), "logo_url": f("string"), "category_ids": f("array"),
|
|
707
805
|
"type": f("integer", choices=(1, 9)), "desc": f("string"), "official": f("string"),
|
|
@@ -14,6 +14,17 @@ from .errors import FuploadError
|
|
|
14
14
|
from .trust import official_opener, require_official_url
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
_IMAGE_CONTENT_TYPES = {
|
|
18
|
+
".bmp": "image/bmp",
|
|
19
|
+
".gif": "image/gif",
|
|
20
|
+
".jpeg": "image/jpeg",
|
|
21
|
+
".jpg": "image/jpeg",
|
|
22
|
+
".png": "image/png",
|
|
23
|
+
".svg": "image/svg+xml",
|
|
24
|
+
".webp": "image/webp",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
17
28
|
def _http_error_details(raw: bytes, fallback: str) -> tuple[str, Any]:
|
|
18
29
|
"""Extract a server error only from a conforming UTF-8 JSON object."""
|
|
19
30
|
try:
|
|
@@ -88,7 +99,8 @@ def multipart_request(
|
|
|
88
99
|
str(value).encode("utf-8"), b"\r\n",
|
|
89
100
|
])
|
|
90
101
|
filename = os.path.basename(file_path)
|
|
91
|
-
|
|
102
|
+
suffix = os.path.splitext(filename)[1].lower()
|
|
103
|
+
content_type = _IMAGE_CONTENT_TYPES.get(suffix) or mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
|
92
104
|
chunks.extend([
|
|
93
105
|
("--%s\r\n" % boundary).encode(),
|
|
94
106
|
('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (file_field, filename)).encode("utf-8"),
|
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.15",
|
|
5
|
+
"skill_version": "0.0.15",
|
|
6
|
+
"tree_sha256": "cfccc3347d2013188a37b120b94282c839ad81eb4fa92f8617e0cfa12dfbbaef",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
9
9
|
"path": "agents/openai.yaml",
|
|
@@ -92,8 +92,8 @@
|
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
"path": "references/modus.md",
|
|
95
|
-
"bytes":
|
|
96
|
-
"sha256": "
|
|
95
|
+
"bytes": 37142,
|
|
96
|
+
"sha256": "84e1524bc3404de3c3bc0582d85651b884c616ff009b1d01839fb84e348917b3"
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"path": "references/newbee-official-cli.md",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
{
|
|
114
114
|
"path": "scripts/fupload_cli/__init__.py",
|
|
115
115
|
"bytes": 50,
|
|
116
|
-
"sha256": "
|
|
116
|
+
"sha256": "9159fa55aa8ef07864abf206b64aabd44ea78c2e987ead8b51ec9384b5d1cebf"
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
"path": "scripts/fupload_cli/blackbox_web.py",
|
|
@@ -127,8 +127,8 @@
|
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
129
|
"path": "scripts/fupload_cli/cli.py",
|
|
130
|
-
"bytes":
|
|
131
|
-
"sha256": "
|
|
130
|
+
"bytes": 37420,
|
|
131
|
+
"sha256": "3a052ec53c29ca95eafc9ba845d448ef45e4bd0231bf41ec1906e55281aa1650"
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
"path": "scripts/fupload_cli/curseforge.py",
|
|
@@ -137,8 +137,8 @@
|
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
139
|
"path": "scripts/fupload_cli/dd_broker.py",
|
|
140
|
-
"bytes":
|
|
141
|
-
"sha256": "
|
|
140
|
+
"bytes": 26638,
|
|
141
|
+
"sha256": "a23fb486dd1ca9d0a1d2f2ca642690535a28e05051ce828ce066cdbc67b37b37"
|
|
142
142
|
},
|
|
143
143
|
{
|
|
144
144
|
"path": "scripts/fupload_cli/dd_sidecar.py",
|
|
@@ -157,8 +157,8 @@
|
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
"path": "scripts/fupload_cli/io.py",
|
|
160
|
-
"bytes":
|
|
161
|
-
"sha256": "
|
|
160
|
+
"bytes": 5319,
|
|
161
|
+
"sha256": "57af3921d016815ed5747788881f86c8a6ce04a939e9f6f6571bd6a3f7383a4d"
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
"path": "scripts/fupload_cli/modus_zip.py",
|
|
@@ -167,8 +167,8 @@
|
|
|
167
167
|
},
|
|
168
168
|
{
|
|
169
169
|
"path": "scripts/fupload_cli/modus.py",
|
|
170
|
-
"bytes":
|
|
171
|
-
"sha256": "
|
|
170
|
+
"bytes": 74134,
|
|
171
|
+
"sha256": "9841ce7188083b62c1c217d89436b3c3ddc3ecf726719d3c44c0b83027172fac"
|
|
172
172
|
},
|
|
173
173
|
{
|
|
174
174
|
"path": "scripts/fupload_cli/newbee_auth.py",
|
|
@@ -182,8 +182,8 @@
|
|
|
182
182
|
},
|
|
183
183
|
{
|
|
184
184
|
"path": "scripts/fupload_cli/schema.py",
|
|
185
|
-
"bytes":
|
|
186
|
-
"sha256": "
|
|
185
|
+
"bytes": 54561,
|
|
186
|
+
"sha256": "17d57a9c9c53e556faf9e415020d47f84965283680c66f3f1c33beef6b83d104"
|
|
187
187
|
},
|
|
188
188
|
{
|
|
189
189
|
"path": "scripts/fupload_cli/state_machine.py",
|
|
@@ -192,8 +192,8 @@
|
|
|
192
192
|
},
|
|
193
193
|
{
|
|
194
194
|
"path": "scripts/fupload_cli/transport.py",
|
|
195
|
-
"bytes":
|
|
196
|
-
"sha256": "
|
|
195
|
+
"bytes": 5195,
|
|
196
|
+
"sha256": "37eb742c1cf75184d14f4f8ae85117222cad3d0748ab4c211452e78b5da0c279"
|
|
197
197
|
},
|
|
198
198
|
{
|
|
199
199
|
"path": "scripts/fupload_cli/trust.py",
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
{
|
|
209
209
|
"path": "SKILL.md",
|
|
210
210
|
"bytes": 25720,
|
|
211
|
-
"sha256": "
|
|
211
|
+
"sha256": "fae17edc0a59ea74dc70a5744b5aef46a057c23a9056e6d6b1e60990fa54782f"
|
|
212
212
|
}
|
|
213
213
|
]
|
|
214
214
|
}
|