@follenfang/fupload 0.0.9 → 0.0.10
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 +17 -4
- package/fupload/references/modus.md +77 -0
- package/fupload/scripts/fupload_cli/__init__.py +1 -1
- package/fupload/scripts/fupload_cli/cli.py +70 -2
- package/fupload/scripts/fupload_cli/io.py +3 -1
- package/fupload/scripts/fupload_cli/modus.py +768 -0
- package/fupload/scripts/fupload_cli/modus_zip.py +175 -0
- package/fupload/scripts/fupload_cli/schema.py +141 -2
- package/fupload/scripts/fupload_cli/state_machine.py +346 -0
- package/npm/skill-manifest.json +33 -13
- package/package.json +1 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"""ModUs Creator project form state machine.
|
|
2
|
+
|
|
3
|
+
The desktop Creator exposes three form tabs in order: choose a game, enter
|
|
4
|
+
general project information, and select/build a license. This module keeps
|
|
5
|
+
that sequencing explicit for CLI callers and provides a small JSON-safe
|
|
6
|
+
snapshot that can be resumed after an interrupted form submission.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import copy
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import tempfile
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Dict, Mapping, Optional, Union
|
|
18
|
+
|
|
19
|
+
from .errors import ValidationError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ProjectStep(str, Enum):
|
|
23
|
+
"""Persisted steps exposed by the Creator form."""
|
|
24
|
+
|
|
25
|
+
CHOOSE_GAME = "choose_game"
|
|
26
|
+
SELECT_GAME = "choose_game"
|
|
27
|
+
BASIC_INFO = "basic_info"
|
|
28
|
+
GENERAL = "basic_info"
|
|
29
|
+
LICENSE = "license"
|
|
30
|
+
COMPLETE = "complete"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# String aliases make the state machine convenient for JSON/CLI consumers.
|
|
34
|
+
CHOOSE_GAME = ProjectStep.CHOOSE_GAME.value
|
|
35
|
+
SELECT_GAME = CHOOSE_GAME
|
|
36
|
+
BASIC_INFO = ProjectStep.BASIC_INFO.value
|
|
37
|
+
GENERAL = BASIC_INFO
|
|
38
|
+
LICENSE = ProjectStep.LICENSE.value
|
|
39
|
+
COMPLETE = ProjectStep.COMPLETE.value
|
|
40
|
+
|
|
41
|
+
_SCHEMA = "fupload.v1.modus.project-state"
|
|
42
|
+
_PLATFORMS = ("modus", "bigfoot")
|
|
43
|
+
_BIGFOOT_EXCLUSIVE_CATEGORY_ID = 998
|
|
44
|
+
_LICENSE_KEYS = {
|
|
45
|
+
"type", "template", "license_template", "licenseTemplate",
|
|
46
|
+
"holder", "copyright_holder", "copyrightHolder",
|
|
47
|
+
"year", "copyright_year", "copyrightYear",
|
|
48
|
+
"content", "license_content", "licenseContent",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _nonempty_text(value: Any, *, path: str) -> str:
|
|
53
|
+
if not isinstance(value, str) or not value.strip():
|
|
54
|
+
raise ValidationError("value must be a non-empty string", path=path)
|
|
55
|
+
return value.strip()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _required_tier(value: Any, *, path: str = "$.required_tier_id") -> Optional[int]:
|
|
59
|
+
"""Validate Creator's optional subscription-tier selection.
|
|
60
|
+
|
|
61
|
+
``None`` is the explicit "no required tier" branch. The API contract
|
|
62
|
+
uses a positive integer for a selected tier; booleans and numeric strings
|
|
63
|
+
are rejected to avoid silently selecting the wrong dropdown item.
|
|
64
|
+
"""
|
|
65
|
+
if value is None:
|
|
66
|
+
return None
|
|
67
|
+
if isinstance(value, bool) or not isinstance(value, int) or value <= 0:
|
|
68
|
+
raise ValidationError("required_tier_id must be null or a positive integer", path=path)
|
|
69
|
+
return value
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _platforms(value: Any, *, path: str = "$.publish_platforms") -> list[str]:
|
|
73
|
+
if not isinstance(value, (list, tuple)) or not value:
|
|
74
|
+
raise ValidationError("publish_platforms must contain at least one platform", path=path)
|
|
75
|
+
result: list[str] = []
|
|
76
|
+
for index, item in enumerate(value):
|
|
77
|
+
if not isinstance(item, str) or item not in _PLATFORMS:
|
|
78
|
+
raise ValidationError("platform must be modus or bigfoot", path=f"{path}[{index}]")
|
|
79
|
+
if item in result:
|
|
80
|
+
raise ValidationError("publish_platforms must not contain duplicates", path=path)
|
|
81
|
+
result.append(item)
|
|
82
|
+
return result
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _categories(value: Any, *, path: str = "$.categories") -> list[int]:
|
|
86
|
+
if not isinstance(value, list) or not value:
|
|
87
|
+
raise ValidationError("categories must contain at least one category ID", path=path)
|
|
88
|
+
if len(value) > 5:
|
|
89
|
+
raise ValidationError("categories must contain at most five category IDs", path=path)
|
|
90
|
+
result: list[int] = []
|
|
91
|
+
for index, item in enumerate(value):
|
|
92
|
+
if isinstance(item, bool) or not isinstance(item, int) or item <= 0:
|
|
93
|
+
raise ValidationError("category ID must be a positive integer", path=f"{path}[{index}]")
|
|
94
|
+
if item in result:
|
|
95
|
+
raise ValidationError("categories must not contain duplicates", path=path)
|
|
96
|
+
result.append(item)
|
|
97
|
+
return result
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _game(value: Any) -> Any:
|
|
101
|
+
"""Require a game object/value that can be serialized and read back."""
|
|
102
|
+
if isinstance(value, Mapping):
|
|
103
|
+
if not value:
|
|
104
|
+
raise ValidationError("game selection must not be empty", path="$.game")
|
|
105
|
+
# Creator game entries have an ID/key and a display name. Preserve
|
|
106
|
+
# unknown fields for forward compatibility, but require one stable
|
|
107
|
+
# identity field so a resumed snapshot remains addressable.
|
|
108
|
+
identity = ("id", "gameId", "game_id", "key", "gameVersion", "game_version", "server")
|
|
109
|
+
if not any(value.get(name) not in (None, "") for name in identity):
|
|
110
|
+
raise ValidationError("game selection must include an identifier", path="$.game")
|
|
111
|
+
return copy.deepcopy(dict(value))
|
|
112
|
+
if isinstance(value, str) and value.strip():
|
|
113
|
+
return value.strip()
|
|
114
|
+
raise ValidationError("game selection must be a non-empty object or string", path="$.game")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _license(value: Any) -> Dict[str, Any]:
|
|
118
|
+
if isinstance(value, str):
|
|
119
|
+
value = {"type": value}
|
|
120
|
+
if not isinstance(value, Mapping):
|
|
121
|
+
raise ValidationError("license must be a non-empty object or string", path="$.license")
|
|
122
|
+
unknown = sorted(set(value) - _LICENSE_KEYS)
|
|
123
|
+
if unknown:
|
|
124
|
+
raise ValidationError("unknown license field(s): %s" % ", ".join(unknown), path="$.license.%s" % unknown[0])
|
|
125
|
+
aliases = {
|
|
126
|
+
"type": ("type", "template", "license_template", "licenseTemplate"),
|
|
127
|
+
"holder": ("holder", "copyright_holder", "copyrightHolder"),
|
|
128
|
+
"year": ("year", "copyright_year", "copyrightYear"),
|
|
129
|
+
"content": ("content", "license_content", "licenseContent"),
|
|
130
|
+
}
|
|
131
|
+
result: Dict[str, Any] = {}
|
|
132
|
+
for target, names in aliases.items():
|
|
133
|
+
for name in names:
|
|
134
|
+
if name in value and value[name] is not None:
|
|
135
|
+
result[target] = value[name]
|
|
136
|
+
break
|
|
137
|
+
result["type"] = _nonempty_text(result.get("type"), path="$.license.type")
|
|
138
|
+
for name in ("holder", "year", "content"):
|
|
139
|
+
if name in result and result[name] is not None:
|
|
140
|
+
result[name] = _nonempty_text(result[name], path="$.license.%s" % name)
|
|
141
|
+
if result["type"].lower() in {"custom", "自定义"} and not result.get("content"):
|
|
142
|
+
raise ValidationError("custom license content must not be empty", path="$.license.content")
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class ProjectStateMachine:
|
|
147
|
+
"""Sequenced, resumable ModUs project creation/edit form."""
|
|
148
|
+
|
|
149
|
+
schema = _SCHEMA
|
|
150
|
+
|
|
151
|
+
def __init__(self, snapshot: Optional[Mapping[str, Any]] = None) -> None:
|
|
152
|
+
self._step = ProjectStep.CHOOSE_GAME.value
|
|
153
|
+
self._game: Any = None
|
|
154
|
+
self._basic_info: Dict[str, Any] = {}
|
|
155
|
+
self._license: Dict[str, Any] = {}
|
|
156
|
+
if snapshot is not None:
|
|
157
|
+
self._restore(snapshot)
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def state(self) -> str:
|
|
161
|
+
return self._step
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def current_step(self) -> str:
|
|
165
|
+
return self._step
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def game(self) -> Any:
|
|
169
|
+
return copy.deepcopy(self._game)
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def basic_info(self) -> Dict[str, Any]:
|
|
173
|
+
return copy.deepcopy(self._basic_info)
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def license(self) -> Dict[str, Any]:
|
|
177
|
+
return copy.deepcopy(self._license)
|
|
178
|
+
|
|
179
|
+
def _require(self, expected: ProjectStep) -> None:
|
|
180
|
+
if self._step != expected.value:
|
|
181
|
+
raise ValidationError(
|
|
182
|
+
"project state %s cannot submit step %s"
|
|
183
|
+
% (self._step, expected.value),
|
|
184
|
+
path="$.state",
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def select_game(self, game: Any) -> Dict[str, Any]:
|
|
188
|
+
self._require(ProjectStep.CHOOSE_GAME)
|
|
189
|
+
self._game = _game(game)
|
|
190
|
+
self._step = ProjectStep.BASIC_INFO.value
|
|
191
|
+
return self.snapshot()
|
|
192
|
+
|
|
193
|
+
def submit_basic_info(self, info: Optional[Mapping[str, Any]] = None, **fields: Any) -> Dict[str, Any]:
|
|
194
|
+
self._require(ProjectStep.BASIC_INFO)
|
|
195
|
+
value: Dict[str, Any] = dict(info or {})
|
|
196
|
+
value.update(fields)
|
|
197
|
+
name = value.get("name", value.get("project_name"))
|
|
198
|
+
summary = value.get("summary")
|
|
199
|
+
_nonempty_text(name, path="$.basic_info.name")
|
|
200
|
+
_nonempty_text(summary, path="$.basic_info.summary")
|
|
201
|
+
selected = value.get("publish_platforms", value.get("publishPlatforms"))
|
|
202
|
+
value["publish_platforms"] = _platforms(selected, path="$.basic_info.publish_platforms")
|
|
203
|
+
value.pop("publishPlatforms", None)
|
|
204
|
+
value["categories"] = _categories(value.get("categories"), path="$.basic_info.categories")
|
|
205
|
+
if _BIGFOOT_EXCLUSIVE_CATEGORY_ID in value["categories"] and value["publish_platforms"] != ["bigfoot"]:
|
|
206
|
+
raise ValidationError(
|
|
207
|
+
"category 998 requires bigfoot as the only publish platform",
|
|
208
|
+
path="$.basic_info.publish_platforms",
|
|
209
|
+
)
|
|
210
|
+
if "required_tier_id" in value:
|
|
211
|
+
value["required_tier_id"] = _required_tier(value["required_tier_id"], path="$.basic_info.required_tier_id")
|
|
212
|
+
elif "requiredTierId" in value:
|
|
213
|
+
value["required_tier_id"] = _required_tier(value.pop("requiredTierId"), path="$.basic_info.requiredTierId")
|
|
214
|
+
else:
|
|
215
|
+
# Explicitly persist the no-tier branch so reloads are stable.
|
|
216
|
+
value["required_tier_id"] = None
|
|
217
|
+
if "bigfoot" in value["publish_platforms"] and value["required_tier_id"] is not None:
|
|
218
|
+
raise ValidationError(
|
|
219
|
+
"required_tier_id must be null when bigfoot is selected",
|
|
220
|
+
path="$.basic_info.required_tier_id",
|
|
221
|
+
)
|
|
222
|
+
derived_sync_type = (1 if "modus" in value["publish_platforms"] else 0) | (
|
|
223
|
+
2 if "bigfoot" in value["publish_platforms"] else 0
|
|
224
|
+
)
|
|
225
|
+
# Creator exposes platform toggles, not synchronizationType itself.
|
|
226
|
+
# Always replace a stale caller value with the UI-derived bit mask.
|
|
227
|
+
value["synchronization_type"] = derived_sync_type
|
|
228
|
+
if "project_name" in value:
|
|
229
|
+
value["name"] = value.pop("project_name")
|
|
230
|
+
self._basic_info = copy.deepcopy(value)
|
|
231
|
+
self._step = ProjectStep.LICENSE.value
|
|
232
|
+
return self.snapshot()
|
|
233
|
+
|
|
234
|
+
def submit_license(self, license_value: Any = None, **fields: Any) -> Dict[str, Any]:
|
|
235
|
+
self._require(ProjectStep.LICENSE)
|
|
236
|
+
value: Any = license_value
|
|
237
|
+
if value is None and fields:
|
|
238
|
+
value = fields
|
|
239
|
+
elif fields:
|
|
240
|
+
if not isinstance(value, Mapping):
|
|
241
|
+
raise ValidationError("license fields require an object", path="$.license")
|
|
242
|
+
merged = dict(value)
|
|
243
|
+
merged.update(fields)
|
|
244
|
+
value = merged
|
|
245
|
+
self._license = _license(value)
|
|
246
|
+
self._step = ProjectStep.COMPLETE.value
|
|
247
|
+
return self.snapshot()
|
|
248
|
+
|
|
249
|
+
# Friendly aliases used by form adapters that name the first tab
|
|
250
|
+
# "select game" and the second tab "general".
|
|
251
|
+
choose_game = select_game
|
|
252
|
+
set_game = select_game
|
|
253
|
+
set_basic_info = submit_basic_info
|
|
254
|
+
set_license = submit_license
|
|
255
|
+
|
|
256
|
+
def submit(self, step: str, payload: Any = None, **fields: Any) -> Dict[str, Any]:
|
|
257
|
+
"""Dispatch a named step for generic CLI adapters."""
|
|
258
|
+
normalized = str(step).strip().lower().replace("-", "_")
|
|
259
|
+
if normalized in {"choose_game", "game"}:
|
|
260
|
+
return self.select_game(payload)
|
|
261
|
+
if normalized in {"basic_info", "general", "basic"}:
|
|
262
|
+
return self.submit_basic_info(payload, **fields)
|
|
263
|
+
if normalized == "license":
|
|
264
|
+
return self.submit_license(payload, **fields)
|
|
265
|
+
raise ValidationError("unsupported project state step", path="$.state")
|
|
266
|
+
|
|
267
|
+
def snapshot(self) -> Dict[str, Any]:
|
|
268
|
+
return {
|
|
269
|
+
"schema": self.schema,
|
|
270
|
+
"state": self._step,
|
|
271
|
+
"game": copy.deepcopy(self._game),
|
|
272
|
+
"basic_info": copy.deepcopy(self._basic_info),
|
|
273
|
+
"license": copy.deepcopy(self._license),
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
to_dict = snapshot
|
|
277
|
+
|
|
278
|
+
@classmethod
|
|
279
|
+
def from_snapshot(cls, snapshot: Mapping[str, Any]) -> "ProjectStateMachine":
|
|
280
|
+
return cls(snapshot)
|
|
281
|
+
|
|
282
|
+
def _restore(self, snapshot: Mapping[str, Any]) -> None:
|
|
283
|
+
if not isinstance(snapshot, Mapping) or snapshot.get("schema") != self.schema:
|
|
284
|
+
raise ValidationError("invalid project state snapshot", path="$.schema")
|
|
285
|
+
state = snapshot.get("state")
|
|
286
|
+
if state not in {item.value for item in ProjectStep}:
|
|
287
|
+
raise ValidationError("invalid project state", path="$.state")
|
|
288
|
+
game = snapshot.get("game")
|
|
289
|
+
basic = snapshot.get("basic_info") or {}
|
|
290
|
+
lic = snapshot.get("license") or {}
|
|
291
|
+
if state != CHOOSE_GAME:
|
|
292
|
+
self._game = _game(game)
|
|
293
|
+
if state in {LICENSE, COMPLETE}:
|
|
294
|
+
if not isinstance(basic, Mapping):
|
|
295
|
+
raise ValidationError("basic_info must be an object", path="$.basic_info")
|
|
296
|
+
self._basic_info = dict(basic)
|
|
297
|
+
_nonempty_text(self._basic_info.get("name"), path="$.basic_info.name")
|
|
298
|
+
_nonempty_text(self._basic_info.get("summary"), path="$.basic_info.summary")
|
|
299
|
+
self._basic_info["publish_platforms"] = _platforms(self._basic_info.get("publish_platforms"))
|
|
300
|
+
self._basic_info["categories"] = _categories(self._basic_info.get("categories"))
|
|
301
|
+
self._basic_info["required_tier_id"] = _required_tier(self._basic_info.get("required_tier_id"))
|
|
302
|
+
if _BIGFOOT_EXCLUSIVE_CATEGORY_ID in self._basic_info["categories"] and self._basic_info["publish_platforms"] != ["bigfoot"]:
|
|
303
|
+
raise ValidationError("category 998 requires bigfoot as the only publish platform", path="$.basic_info.publish_platforms")
|
|
304
|
+
if "bigfoot" in self._basic_info["publish_platforms"] and self._basic_info["required_tier_id"] is not None:
|
|
305
|
+
raise ValidationError("required_tier_id must be null when bigfoot is selected", path="$.basic_info.required_tier_id")
|
|
306
|
+
derived_sync_type = (1 if "modus" in self._basic_info["publish_platforms"] else 0) | (2 if "bigfoot" in self._basic_info["publish_platforms"] else 0)
|
|
307
|
+
if self._basic_info.get("synchronization_type") != derived_sync_type:
|
|
308
|
+
raise ValidationError("synchronization_type must match publish_platforms", path="$.basic_info.synchronization_type")
|
|
309
|
+
if state == COMPLETE:
|
|
310
|
+
self._license = _license(lic)
|
|
311
|
+
self._step = str(state)
|
|
312
|
+
|
|
313
|
+
def save(self, path: Union[str, os.PathLike[str]]) -> Path:
|
|
314
|
+
target = Path(path)
|
|
315
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
316
|
+
fd, temporary = tempfile.mkstemp(prefix=target.name + ".", suffix=".tmp", dir=str(target.parent))
|
|
317
|
+
try:
|
|
318
|
+
with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as handle:
|
|
319
|
+
json.dump(self.snapshot(), handle, ensure_ascii=False, indent=2)
|
|
320
|
+
handle.write("\n")
|
|
321
|
+
handle.flush()
|
|
322
|
+
os.fsync(handle.fileno())
|
|
323
|
+
os.replace(temporary, target)
|
|
324
|
+
finally:
|
|
325
|
+
if os.path.exists(temporary):
|
|
326
|
+
os.unlink(temporary)
|
|
327
|
+
return target
|
|
328
|
+
|
|
329
|
+
@classmethod
|
|
330
|
+
def load(cls, path: Union[str, os.PathLike[str]]) -> "ProjectStateMachine":
|
|
331
|
+
target = Path(path)
|
|
332
|
+
try:
|
|
333
|
+
with target.open("r", encoding="utf-8") as handle:
|
|
334
|
+
snapshot = json.load(handle)
|
|
335
|
+
except (OSError, ValueError) as exc:
|
|
336
|
+
raise ValidationError("project state snapshot could not be read", path=str(target)) from exc
|
|
337
|
+
return cls.from_snapshot(snapshot)
|
|
338
|
+
|
|
339
|
+
save_state = save
|
|
340
|
+
load_state = load
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
__all__ = [
|
|
344
|
+
"ProjectStep", "ProjectStateMachine", "CHOOSE_GAME", "SELECT_GAME",
|
|
345
|
+
"BASIC_INFO", "GENERAL", "LICENSE", "COMPLETE",
|
|
346
|
+
]
|
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.10",
|
|
5
|
+
"skill_version": "0.0.10",
|
|
6
|
+
"tree_sha256": "38621aa4709de68a1cc298c8183fe0a5647c8291b870defdbbb5f54d184fa94a",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
9
9
|
"path": "agents/openai.yaml",
|
|
@@ -90,6 +90,11 @@
|
|
|
90
90
|
"bytes": 16543,
|
|
91
91
|
"sha256": "782d7c89d865df545fb6ffe4d7ee4492e6e5b192a3f71e4aef6424183a6a4f24"
|
|
92
92
|
},
|
|
93
|
+
{
|
|
94
|
+
"path": "references/modus.md",
|
|
95
|
+
"bytes": 4492,
|
|
96
|
+
"sha256": "0b9cf6c4b3bc7aa37a3649ec6ccb6c8b8aa3d077c8febc2635548546150df356"
|
|
97
|
+
},
|
|
93
98
|
{
|
|
94
99
|
"path": "references/newbee-official-cli.md",
|
|
95
100
|
"bytes": 24919,
|
|
@@ -107,8 +112,8 @@
|
|
|
107
112
|
},
|
|
108
113
|
{
|
|
109
114
|
"path": "scripts/fupload_cli/__init__.py",
|
|
110
|
-
"bytes":
|
|
111
|
-
"sha256": "
|
|
115
|
+
"bytes": 50,
|
|
116
|
+
"sha256": "3f1afb95137eaedc622bb437c9c8a01bdaa729b911f3749e400a429d24abaaa2"
|
|
112
117
|
},
|
|
113
118
|
{
|
|
114
119
|
"path": "scripts/fupload_cli/blackbox_web.py",
|
|
@@ -122,8 +127,8 @@
|
|
|
122
127
|
},
|
|
123
128
|
{
|
|
124
129
|
"path": "scripts/fupload_cli/cli.py",
|
|
125
|
-
"bytes":
|
|
126
|
-
"sha256": "
|
|
130
|
+
"bytes": 31958,
|
|
131
|
+
"sha256": "897c2e7a7fea00125023d7fc977aaa6e3b7729b50a468e7deadfacc04d89f9b8"
|
|
127
132
|
},
|
|
128
133
|
{
|
|
129
134
|
"path": "scripts/fupload_cli/curseforge.py",
|
|
@@ -152,8 +157,18 @@
|
|
|
152
157
|
},
|
|
153
158
|
{
|
|
154
159
|
"path": "scripts/fupload_cli/io.py",
|
|
155
|
-
"bytes":
|
|
156
|
-
"sha256": "
|
|
160
|
+
"bytes": 4515,
|
|
161
|
+
"sha256": "dafcfcaa493def7bc42719f357416208a3cabbb163cf651fb18307b501b46e4b"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"path": "scripts/fupload_cli/modus_zip.py",
|
|
165
|
+
"bytes": 7833,
|
|
166
|
+
"sha256": "3b35b30266dd48874f679b40d12739534ede3dc58f0c34cdd35ac2deca4e07db"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"path": "scripts/fupload_cli/modus.py",
|
|
170
|
+
"bytes": 38821,
|
|
171
|
+
"sha256": "f5edb6898ce515a7e34f0fcab3dc022c2c5185f548644eabf5291e38b22dae22"
|
|
157
172
|
},
|
|
158
173
|
{
|
|
159
174
|
"path": "scripts/fupload_cli/newbee_auth.py",
|
|
@@ -167,8 +182,13 @@
|
|
|
167
182
|
},
|
|
168
183
|
{
|
|
169
184
|
"path": "scripts/fupload_cli/schema.py",
|
|
170
|
-
"bytes":
|
|
171
|
-
"sha256": "
|
|
185
|
+
"bytes": 46291,
|
|
186
|
+
"sha256": "025d6f4af1e8adda87aed14a075028c370e78bf0def6738ab303e2f1798aefd8"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"path": "scripts/fupload_cli/state_machine.py",
|
|
190
|
+
"bytes": 15325,
|
|
191
|
+
"sha256": "02f37b745bbb70860c09563b6778014bd1c69ca06ba28777415ad7ce84d02610"
|
|
172
192
|
},
|
|
173
193
|
{
|
|
174
194
|
"path": "scripts/fupload_cli/transport.py",
|
|
@@ -187,8 +207,8 @@
|
|
|
187
207
|
},
|
|
188
208
|
{
|
|
189
209
|
"path": "SKILL.md",
|
|
190
|
-
"bytes":
|
|
191
|
-
"sha256": "
|
|
210
|
+
"bytes": 25720,
|
|
211
|
+
"sha256": "2e20b91ded57f4d91dbb658abf6fedc636785cc1b3ada23f985c9241c53cd3e6"
|
|
192
212
|
}
|
|
193
213
|
]
|
|
194
214
|
}
|