@follenfang/fupload 0.0.20 → 0.0.22
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
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.22"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Fupload
|
|
@@ -22,7 +22,7 @@ Map natural language consistently:
|
|
|
22
22
|
|
|
23
23
|
- `create`: create the main record. If first content is a separate platform action, plan it as a following atomic step.
|
|
24
24
|
- `update`: publish a plugin version, change selected configuration-backup content, or publish a WA/string version.
|
|
25
|
-
- `edit`: change fields allowed by the target platform's action allowlist. For DD plugins, this includes `description` and existing-record commercial, association, room/channel, and creation-statement settings; other first-publication metadata, categories, media, and version fields are create/update-only. Description edits read both detail and author-list projections; either may confirm the value, but if neither matches, report verification-required.
|
|
25
|
+
- `edit`: change fields allowed by the target platform's action allowlist. For DD plugins, this includes `description`, `html_desc`, and existing-record commercial, association, room/channel, and creation-statement settings; other first-publication metadata, categories, media, and version fields are create/update-only. Description edits read both detail and author-list projections; either may confirm the value, but if neither matches, report verification-required.
|
|
26
26
|
- `delete`: delete one main record only. It never deletes an individual version, media object, or associated record.
|
|
27
27
|
|
|
28
28
|
Never offer bulk delete, drafts, guides, messages, or GUI control automation. Heybox supports individual version deletion with asynchronous readback; whole-module deletion is not supported. The DD task-session command may close verified official GUI processes only after the consent flow below.
|
package/fupload/references/dd.md
CHANGED
|
@@ -73,7 +73,7 @@ A published version's `update_desc` cannot be corrected through the CLI: `plugin
|
|
|
73
73
|
|
|
74
74
|
Every path in `detail_img_files` must exist as a regular local file. Validation reports the exact indexed path, such as `$.detail_img_files[0]`, before any upload or mutation.
|
|
75
75
|
|
|
76
|
-
`game_type` and the outer free/paid state are create-only. `plugin update` requires `sn`, `game_versions`, `version`, and `update_desc`; optional `file`, `detail_url`, and `release_type` publish a new version. `plugin edit` requires `sn` and accepts `description` plus the official existing-record commercial and association controls (`scope`, payment/lifetime/VIP fields, room/channel linkage, `creation_statement`, and associated content). Other first-publication metadata (`addon_type`, `name`, logo, detail images,
|
|
76
|
+
`game_type` and the outer free/paid state are create-only. `plugin update` requires `sn`, `game_versions`, `version`, and `update_desc`; optional `file`, `detail_url`, and `release_type` publish a new version. `plugin edit` requires `sn` and accepts `description`, `html_desc`, plus the official existing-record commercial and association controls (`scope`, payment/lifetime/VIP fields, room/channel linkage, `creation_statement`, and associated content). Other first-publication metadata (`addon_type`, `name`, logo, detail images, and categories) and version fields are not edit fields. The CLI verifies description edits through both detail and author-list readback; if DD accepts the request but leaves the remote value unchanged, it reports a verification-required error rather than false success.
|
|
77
77
|
|
|
78
78
|
When `assign_user_sn` is present, the official UI exposes only the public scope option. Python validates the final rebuilt form and rejects a private scope before upload or mutation.
|
|
79
79
|
|
|
@@ -1465,10 +1465,11 @@ PLUGIN_CREATE_DEFAULTS = {
|
|
|
1465
1465
|
|
|
1466
1466
|
# DD's modify form is not a second create form. The official edit page
|
|
1467
1467
|
# rebuilds the payload from existing commercial/association controls. The
|
|
1468
|
-
# brief
|
|
1469
|
-
# detail and author-list readback after every
|
|
1468
|
+
# brief and rich-text detail descriptions are the supported metadata
|
|
1469
|
+
# exceptions and are verified by detail and author-list readback after every
|
|
1470
|
+
# edit. Other first-publication metadata remains create-only.
|
|
1470
1471
|
PLUGIN_EDIT_FIELDS = (
|
|
1471
|
-
"description",
|
|
1472
|
+
"description", "html_desc",
|
|
1472
1473
|
"scope", "share_code_life_type", "need_buy", "price_fen", "buy_life_type",
|
|
1473
1474
|
"jump_room", "room_id", "channel_id", "channel_type", "sync_room",
|
|
1474
1475
|
"creation_statement", "with_associate", "associated_acts", "need_anchor_vip",
|
|
@@ -1485,13 +1486,15 @@ def plugin_form(
|
|
|
1485
1486
|
if isinstance(author_value, Mapping) and isinstance(author_value.get("latest_version"), dict)
|
|
1486
1487
|
else {}
|
|
1487
1488
|
)
|
|
1488
|
-
#
|
|
1489
|
+
# The author list is the live publication projection. DD detail_v2 can
|
|
1490
|
+
# retain an older latest_version, so an available author-list value wins
|
|
1491
|
+
# over detail for the version/package fields used to rebuild modify form.
|
|
1489
1492
|
source = {name: copy.deepcopy(value[name]) for name in PLUGIN_FIELDS if name in value}
|
|
1490
1493
|
for name in ("detail_url", "release_type", "version"):
|
|
1491
1494
|
latest_name = {"detail_url": "file_path"}.get(name, name)
|
|
1492
|
-
projected =
|
|
1495
|
+
projected = author_latest.get(latest_name)
|
|
1493
1496
|
if projected is None:
|
|
1494
|
-
projected =
|
|
1497
|
+
projected = latest.get(latest_name)
|
|
1495
1498
|
if projected is None:
|
|
1496
1499
|
source.pop(name, None)
|
|
1497
1500
|
else:
|
|
@@ -1516,22 +1519,29 @@ def plugin_form(
|
|
|
1516
1519
|
return form
|
|
1517
1520
|
|
|
1518
1521
|
|
|
1519
|
-
def plugin_version_projection(
|
|
1522
|
+
def plugin_version_projection(
|
|
1523
|
+
value: Mapping[str, Any], author_value: Optional[Mapping[str, Any]] = None,
|
|
1524
|
+
) -> Dict[str, Any]:
|
|
1520
1525
|
"""Project version fields for readback without feeding them into modify."""
|
|
1521
1526
|
latest = value.get("latest_version") if isinstance(value.get("latest_version"), dict) else {}
|
|
1527
|
+
author_latest = (
|
|
1528
|
+
author_value.get("latest_version")
|
|
1529
|
+
if isinstance(author_value, Mapping) and isinstance(author_value.get("latest_version"), dict)
|
|
1530
|
+
else {}
|
|
1531
|
+
)
|
|
1522
1532
|
projection: Dict[str, Any] = {}
|
|
1523
1533
|
sources = {
|
|
1524
|
-
"game_versions": (latest, "game_versions", value, "game_versions"),
|
|
1525
|
-
"detail_url": (latest, "file_path", value, "detail_url"),
|
|
1526
|
-
"release_type": (latest, "release_type", value, "release_type"),
|
|
1527
|
-
"version": (latest, "version", value, "version"),
|
|
1528
|
-
"update_desc": (latest, "update_desc", value, "update_desc"),
|
|
1534
|
+
"game_versions": ((author_latest, "game_versions"), (latest, "game_versions"), (value, "game_versions")),
|
|
1535
|
+
"detail_url": ((author_latest, "file_path"), (latest, "file_path"), (value, "detail_url")),
|
|
1536
|
+
"release_type": ((author_latest, "release_type"), (latest, "release_type"), (value, "release_type")),
|
|
1537
|
+
"version": ((author_latest, "version"), (latest, "version"), (value, "version")),
|
|
1538
|
+
"update_desc": ((author_latest, "update_desc"), (latest, "update_desc"), (value, "update_desc")),
|
|
1529
1539
|
}
|
|
1530
|
-
for name,
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1540
|
+
for name, candidates in sources.items():
|
|
1541
|
+
for source, source_name in candidates:
|
|
1542
|
+
if source.get(source_name) is not None:
|
|
1543
|
+
projection[name] = copy.deepcopy(source[source_name])
|
|
1544
|
+
break
|
|
1535
1545
|
return projection
|
|
1536
1546
|
|
|
1537
1547
|
|
|
@@ -2087,11 +2097,33 @@ class DD:
|
|
|
2087
2097
|
apply_present(form, doc, PLUGIN_FIELDS)
|
|
2088
2098
|
else:
|
|
2089
2099
|
current = self._fresh_detail(session, "plugin", doc["sn"])
|
|
2100
|
+
if current.get("assign_user_sn") and doc.get("scope") == "private":
|
|
2101
|
+
raise ValidationError("assigned plugins can only use public scope", path="$.scope")
|
|
2090
2102
|
game_type = current.get("game_type") or (current.get("game_types") or [None])[0]
|
|
2091
2103
|
listing = author_item(
|
|
2092
2104
|
session, "plugin", str(doc["sn"]), str(current.get("name") or ""), game_type,
|
|
2093
2105
|
)
|
|
2094
|
-
|
|
2106
|
+
if action == "edit":
|
|
2107
|
+
if not listing:
|
|
2108
|
+
raise FuploadError(
|
|
2109
|
+
"DD author-list projection is required to preserve the published plugin version",
|
|
2110
|
+
kind="verification_required", stage="dependency_get", endpoint="/addon/list",
|
|
2111
|
+
verification_required=True,
|
|
2112
|
+
)
|
|
2113
|
+
published_version = plugin_version_projection({}, listing)
|
|
2114
|
+
missing_published = [
|
|
2115
|
+
name for name in ("detail_url", "release_type", "version")
|
|
2116
|
+
if name not in published_version
|
|
2117
|
+
]
|
|
2118
|
+
if missing_published:
|
|
2119
|
+
raise FuploadError(
|
|
2120
|
+
"DD author-list projection is missing published plugin field(s): %s"
|
|
2121
|
+
% ", ".join(missing_published),
|
|
2122
|
+
kind="verification_required", stage="dependency_get", endpoint="/addon/list",
|
|
2123
|
+
verification_required=True,
|
|
2124
|
+
details={"fields": missing_published},
|
|
2125
|
+
)
|
|
2126
|
+
form = plugin_form(current, listing) if action == "edit" else plugin_form(current)
|
|
2095
2127
|
allowed = PLUGIN_EDIT_FIELDS if action == "edit" else ("game_versions", "detail_url", "release_type", "version", "update_desc")
|
|
2096
2128
|
current_version = str(form.get("version") or "").strip()
|
|
2097
2129
|
apply_present(form, doc, allowed)
|
|
@@ -2151,6 +2183,7 @@ class DD:
|
|
|
2151
2183
|
"creation_statement", "need_buy",
|
|
2152
2184
|
"price_fen", "buy_life_type", "jump_room", "room_id", "channel_id", "channel_type",
|
|
2153
2185
|
"sync_room", "with_associate", "associated_acts", "need_anchor_vip", "vip_levels",
|
|
2186
|
+
"game_versions", "detail_url", "release_type", "version",
|
|
2154
2187
|
)
|
|
2155
2188
|
if action != "update":
|
|
2156
2189
|
raw_readback: Any = {}
|
|
@@ -581,6 +581,10 @@ DD_PLUGIN_EDIT_META = {
|
|
|
581
581
|
for name in DD_COMMERCIAL
|
|
582
582
|
}
|
|
583
583
|
DD_PLUGIN_EDIT_META["description"] = DD_PLUGIN_META["description"]
|
|
584
|
+
# DD's modify form accepts the existing plugin's rich-text detail body as
|
|
585
|
+
# metadata. Keep the other first-publication metadata (name, logo, images,
|
|
586
|
+
# categories, etc.) create-only, but allow an explicit html_desc edit.
|
|
587
|
+
DD_PLUGIN_EDIT_META["html_desc"] = DD_PLUGIN_META["html_desc"]
|
|
584
588
|
DD_PLUGIN_VERSION = {
|
|
585
589
|
"game_versions": f("array", nonempty=True), "detail_url": f("string"),
|
|
586
590
|
"file": f("string", local_file=True), "release_type": f("integer", choices=(1, 2, 3)),
|
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.22",
|
|
5
|
+
"skill_version": "0.0.22",
|
|
6
|
+
"tree_sha256": "fffd84093cea75f44ca8ad836f4cfb643e692cf121162201892ad130f23fbc5a",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
9
9
|
"path": "agents/openai.yaml",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
{
|
|
89
89
|
"path": "references/dd.md",
|
|
90
90
|
"bytes": 20837,
|
|
91
|
-
"sha256": "
|
|
91
|
+
"sha256": "4a174fa9470a23aee5c607b85123575c157559570a2d3fd4caa9a0874f9ae934"
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
"path": "references/modus.md",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
{
|
|
114
114
|
"path": "scripts/fupload_cli/__init__.py",
|
|
115
115
|
"bytes": 50,
|
|
116
|
-
"sha256": "
|
|
116
|
+
"sha256": "0cec28503481955d702020619b9cd9b3ec7032c4d20be0bbad2aa9f4f5dac6f8"
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
"path": "scripts/fupload_cli/blackbox_web.py",
|
|
@@ -147,8 +147,8 @@
|
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
149
|
"path": "scripts/fupload_cli/dd.py",
|
|
150
|
-
"bytes":
|
|
151
|
-
"sha256": "
|
|
150
|
+
"bytes": 117372,
|
|
151
|
+
"sha256": "19a303e84ce08dd3af9a905d04d24f7224c3302f83f0d62f899618cf45fe0433"
|
|
152
152
|
},
|
|
153
153
|
{
|
|
154
154
|
"path": "scripts/fupload_cli/errors.py",
|
|
@@ -182,8 +182,8 @@
|
|
|
182
182
|
},
|
|
183
183
|
{
|
|
184
184
|
"path": "scripts/fupload_cli/schema.py",
|
|
185
|
-
"bytes":
|
|
186
|
-
"sha256": "
|
|
185
|
+
"bytes": 54911,
|
|
186
|
+
"sha256": "bede6cc93de8a9d766e463cab0acb8725745da781aebabba73744394b594b93c"
|
|
187
187
|
},
|
|
188
188
|
{
|
|
189
189
|
"path": "scripts/fupload_cli/state_machine.py",
|
|
@@ -207,8 +207,8 @@
|
|
|
207
207
|
},
|
|
208
208
|
{
|
|
209
209
|
"path": "SKILL.md",
|
|
210
|
-
"bytes":
|
|
211
|
-
"sha256": "
|
|
210
|
+
"bytes": 28582,
|
|
211
|
+
"sha256": "925b6b480edae4272a6922200a7f046e95df96908edecd4660e1fc49bdb6688d"
|
|
212
212
|
}
|
|
213
213
|
]
|
|
214
214
|
}
|