@follenfang/fupload 0.0.10 → 0.0.11

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.10"
5
+ version: "0.0.11"
6
6
  ---
7
7
 
8
8
  # Fupload
@@ -27,6 +27,39 @@ The complete project model includes `project_id`, `name`, `alt_name`, `summary`,
27
27
  `required_tier_id`, `required_dependencies`, `cf_url`, returned `logo`, `status`,
28
28
  `game`, and `description`. Write inputs use `project_state`, `publish_platforms`,
29
29
  `logo_base64`, the compatibility `screenshot_base64s`, and edit-only `image_ops`.
30
+
31
+ ### Project field contract
32
+
33
+ | CLI/state field | Creator wire/readback | JSON type | Required/default | Source and state dependency | Successful readback |
34
+ | --- | --- | --- | --- | --- | --- |
35
+ | `schema` | none | string | required by every write document | versioned CLI envelope | not sent |
36
+ | `project_id` | `projectId` | positive integer | edit/delete required; create assigned by server | outer write document, not form state | `projectId` |
37
+ | `game` | none confirmed | object or non-empty string | required; no default | `choose_game`; object needs a stable ID/key | preserved in state snapshot |
38
+ | `name` | `name` | non-empty string | required; no default | `basic_info` | `name` |
39
+ | `alt_name` | `altName` | string/null | optional; omitted preserves value | `basic_info`; edit clear uses `<null>` | `altName` |
40
+ | `summary` | `summary` | non-empty string | required; no default | `basic_info` | `summary` |
41
+ | `categories` | comma-delimited `categories` | 1-5 positive integer IDs | required; no default | service `categories` enum; `basic_info`; ID 998 requires BigFoot only | comma-delimited `categories` |
42
+ | `publish_platforms` | derived `synchronizationType` | non-empty unique enum array | required; no default | `basic_info`; values `modus`, `bigfoot`, or both | integer `1`, `2`, or `3` |
43
+ | `synchronization_type` | `synchronizationType` | integer | derived; caller value is replaced | derived from platform toggles in `basic_info` | `synchronizationType` |
44
+ | `required_tier_id` | `requiredTierId` | null for this account | defaults to null | service `subscription-tiers` enum; current result is `[]`; BigFoot requires null | `requiredTierId: null` |
45
+ | `repo_url` | `repoUrl` | string/null | optional; omitted preserves value | `basic_info`; create empty is omitted; edit clear uses `<null>` | `repoUrl` |
46
+ | `logo_base64` | `screenshotBase64sReqs.screenshotBase64s` | base64 string | optional; default empty string | create-only `basic_info`; sent as `logo.webp` | server-managed `logo` path |
47
+ | `screenshot_base64s` | same create logo payload | string array | compatibility alias; default empty array | create-only `basic_info`; first value is used when `logo_base64` is absent | server-managed `logo` path |
48
+ | `license.type` | `license` JSON string `type` | non-empty string | required; no default | separate `license` step | parsed `license` |
49
+ | `license.holder/year/content` | fields inside `license` JSON string | non-empty strings | optional except custom content | separate `license` step; custom requires content | parsed `license` |
50
+ | `description` | `description` | string/null | edit-only; omitted preserves value | edit `basic_info`; clear uses `<null>` | `description` |
51
+ | `required_dependencies` | `requiredDependencies` | string/null | edit-only; omitted preserves value | edit `basic_info`; clear uses `<null>`; dependency query is dynamic | `requiredDependencies` |
52
+ | `images` | `images` | non-negative integer | edit-only; required with `image_ops` | edit `basic_info` | `images` count |
53
+ | `image_ops` | `imagesOps` | operation array | edit-only; no default | edit `basic_info`; upload requires base64, delete forbids it | `images` count |
54
+ | `cf_url` | `cfUrl` | string/null | read-only | known detail field may be preserved in a snapshot but is never sent | `cfUrl` |
55
+ | `logo` | `logo` | string/object/null | read-only | server-managed detail field; create uses `logo_base64` instead | `logo` |
56
+ | `status` | `status` | integer/null | read-only | server-managed detail field | `status` |
57
+ | `project_state` | none | object | required and must be `complete` | wraps `choose_game -> basic_info -> license`; unknown nested fields are rejected | local resumable snapshot |
58
+
59
+ Create-only, edit-only, and read-only fields may be preserved in a resumable
60
+ snapshot, but `_project_wire` sends only fields supported by the selected API
61
+ operation. The state machine rejects unknown `basic_info` and `license` fields
62
+ before changing state.
30
63
  `publish_platforms` is a non-empty array containing `modus`,
31
64
  `bigfoot`, or both; the two values are not mutually exclusive. Creator derives
32
65
  wire `synchronizationType` as 1 for ModUs, 2 for BigFoot, and 3 for both.
@@ -64,7 +97,10 @@ Deletion requires `confirm: "DELETE"`.
64
97
  `transaction_log` optionally selects the redacted transaction record path.
65
98
  Successful records include the completed `file_id`, metadata, signature, and
66
99
  binary upload stages that ran; failed records include `failed_stage`, a redacted
67
- error, and whether the ZIP was retained. The provider parses every ZIP `.toc`
100
+ error, and whether the ZIP was retained. The transaction record is established
101
+ before file-ID allocation and ZIP preflight, so those early failures are also
102
+ durable. Binary-upload errors expose only a query-free endpoint identifier,
103
+ status when available, and a bounded redacted response summary. The provider parses every ZIP `.toc`
68
104
  `Interface` value to derive
69
105
  `toc_version` and `supported_game_versions`; explicit caller values must match.
70
106
  The provider validates the ZIP, computes `md5`, `zip_size`, and `unzip_size`,
@@ -1,3 +1,3 @@
1
1
  """Fupload Python CLI."""
2
2
 
3
- __version__ = "0.0.10"
3
+ __version__ = "0.0.11"
@@ -633,12 +633,21 @@ class ModUs:
633
633
  if size > MAX_PACKAGE_BYTES:
634
634
  raise ValidationError("release ZIP exceeds the 200 MB upload limit", path=file_path)
635
635
  parsed = urllib.parse.urlsplit(signed_url)
636
+ if parsed.scheme not in ("http", "https") or not parsed.hostname:
637
+ raise ValidationError("release upload URL must be HTTP(S)", path="signed_url")
638
+ # The query carries the object-store signature. Keep a useful endpoint
639
+ # in errors without ever persisting that credential material.
640
+ endpoint_host = parsed.hostname
641
+ if parsed.port is not None:
642
+ endpoint_host = "%s:%d" % (endpoint_host, parsed.port)
643
+ endpoint = urllib.parse.urlunsplit((parsed.scheme, endpoint_host, parsed.path or "/", "", ""))
636
644
  conn_cls = http.client.HTTPSConnection if parsed.scheme == "https" else http.client.HTTPConnection
637
- if parsed.scheme == "https":
638
- conn = conn_cls(parsed.hostname, parsed.port, timeout=max(self.timeout, 600), context=ssl.create_default_context())
639
- else:
640
- conn = conn_cls(parsed.hostname, parsed.port, timeout=max(self.timeout, 600))
645
+ conn = None
641
646
  try:
647
+ if parsed.scheme == "https":
648
+ conn = conn_cls(parsed.hostname, parsed.port, timeout=max(self.timeout, 600), context=ssl.create_default_context())
649
+ else:
650
+ conn = conn_cls(parsed.hostname, parsed.port, timeout=max(self.timeout, 600))
642
651
  conn.putrequest("PUT", urllib.parse.urlunsplit(("", "", parsed.path or "/", parsed.query, "")))
643
652
  conn.putheader("Content-Type", "application/zip")
644
653
  conn.putheader("Content-Length", str(size))
@@ -650,15 +659,33 @@ class ModUs:
650
659
  break
651
660
  conn.send(chunk)
652
661
  response = conn.getresponse()
653
- response.read()
662
+ raw = response.read(4096)
654
663
  if response.status < 200 or response.status >= 300:
655
- raise FuploadError("ModUs binary upload returned HTTP %d" % response.status, http_status=response.status, verification_required=True, stage="binary_upload")
664
+ summary = redact(raw.decode("utf-8", errors="replace")) if raw else "<empty>"
665
+ raise FuploadError(
666
+ "ModUs binary upload returned HTTP %d" % response.status,
667
+ endpoint=endpoint,
668
+ http_status=response.status,
669
+ verification_required=True,
670
+ stage="binary_upload",
671
+ details={"response_summary": summary},
672
+ )
656
673
  except FuploadError:
657
674
  raise
658
- except OSError as exc:
659
- raise FuploadError("ModUs binary upload failed: %s" % exc, verification_required=True, stage="binary_upload") from exc
675
+ except (OSError, http.client.HTTPException) as exc:
676
+ raise FuploadError(
677
+ "ModUs binary upload failed",
678
+ endpoint=endpoint,
679
+ verification_required=True,
680
+ stage="binary_upload",
681
+ details={"response_summary": redact(str(exc)) or exc.__class__.__name__},
682
+ ) from exc
660
683
  finally:
661
- conn.close()
684
+ if conn is not None:
685
+ try:
686
+ conn.close()
687
+ except OSError:
688
+ pass
662
689
  return {"status": "uploaded", "archive": path.name, "bytes": size}
663
690
 
664
691
  def release_delete(self, project_id: int, file_id: int) -> Any:
@@ -666,70 +693,108 @@ class ModUs:
666
693
 
667
694
  def publish(self, doc: Mapping[str, Any], *, update: bool = False) -> Dict[str, Any]:
668
695
  project_id = _positive_id(doc["project_id"], field="project_id")
669
- allocated_file_id = not bool(doc.get("file_id"))
670
- file_id = _positive_id(doc.get("file_id") or self.release_file_id(project_id), field="file_id")
671
- metadata = {k: doc[k] for k in ("project_id", "version", "type", "supported_game_versions", "toc_version", "changelog", "path") if k in doc}
672
- metadata["file_id"] = file_id
673
696
  file_path = doc.get("file")
674
- if file_path is None and not update:
675
- raise ValidationError("release ZIP file is required", path="file")
676
697
  archive = Path(str(file_path)) if file_path is not None else None
677
- if archive is not None:
678
- if not archive.is_file():
679
- raise ValidationError("release ZIP file does not exist", path=str(file_path))
680
- size = archive.stat().st_size
681
- if size > MAX_PACKAGE_BYTES:
682
- raise ValidationError("release ZIP exceeds the 200 MB upload limit", path=str(file_path))
683
- md5 = hashlib.md5(archive.read_bytes()).hexdigest()
684
- try:
685
- unzip_size = sum(info.file_size for info in zipfile.ZipFile(str(archive)).infolist())
686
- except (OSError, zipfile.BadZipFile) as exc:
687
- raise ValidationError("release file is not a valid ZIP", path=str(file_path)) from exc
688
- derived = parse_modus_zip(archive)
689
- supplied_toc = doc.get("toc_version")
690
- supplied_games = doc.get("supported_game_versions")
691
- if supplied_toc is not None and str(supplied_toc) != derived["toc_version"]:
692
- raise ValidationError("toc_version does not match the ZIP Interface field", path="$.toc_version")
693
- if supplied_games is not None and _supported_game_versions(supplied_games) != derived["supported_game_versions"]:
694
- raise ValidationError("supported_game_versions does not match the ZIP Interface field", path="$.supported_game_versions")
695
- metadata.update({
696
- "md5": md5,
697
- "zip_size": size,
698
- "unzip_size": unzip_size,
699
- "toc_version": derived["toc_version"],
700
- "supported_game_versions": derived["supported_game_versions"],
701
- "path": doc.get("path") or archive.name,
702
- })
698
+ supplied_file_id = doc.get("file_id")
703
699
  transaction: Dict[str, Any] = {
704
700
  "schema": "fupload.v1.modus.upload-transaction",
705
701
  "created_at": int(time.time()),
706
702
  "project_id": project_id,
707
- "file_id": file_id,
703
+ "file_id": None,
708
704
  "update": bool(update),
709
705
  "archive": str(archive) if archive is not None else None,
710
- "stages": ["file_id"] if allocated_file_id else [],
706
+ "stages": [],
711
707
  }
712
708
  transaction_path = Path(str(doc.get("transaction_log") or ((str(archive) + ".modus-transaction.json") if archive else "modus-transaction.json")))
713
709
 
714
710
  def save_transaction() -> None:
715
711
  transaction_path.write_text(json.dumps(transaction, ensure_ascii=False, indent=2), encoding="utf-8")
716
712
 
713
+ # Establish the audit record before reserving a file ID or validating
714
+ # the archive so every publish failure has a durable transaction.
717
715
  try:
718
- transaction["stages"].append("release_metadata_update" if update else "release_metadata")
716
+ save_transaction()
717
+ except OSError as exc:
718
+ raise FuploadError(
719
+ "ModUs upload transaction could not be written",
720
+ stage="transaction_log",
721
+ details={"path": str(transaction_path)},
722
+ ) from exc
723
+
724
+ current_stage = "prepare"
725
+ try:
726
+ allocated_file_id = not bool(supplied_file_id)
727
+ current_stage = "file_id"
728
+ transaction["active_stage"] = current_stage
729
+ save_transaction()
730
+ if allocated_file_id:
731
+ transaction["stages"].append("file_id")
732
+ file_id = _positive_id(self.release_file_id(project_id), field="file_id")
733
+ else:
734
+ file_id = _positive_id(supplied_file_id, field="file_id")
735
+ transaction["file_id"] = file_id
736
+
737
+ metadata = {k: doc[k] for k in ("project_id", "version", "type", "supported_game_versions", "toc_version", "changelog", "path") if k in doc}
738
+ metadata["file_id"] = file_id
739
+ current_stage = "zip_preflight"
740
+ transaction["active_stage"] = current_stage
741
+ save_transaction()
742
+ if archive is None and not update:
743
+ raise ValidationError("release ZIP file is required", path="file")
744
+ if archive is not None:
745
+ if not archive.is_file():
746
+ raise ValidationError("release ZIP file does not exist", path=str(file_path))
747
+ size = archive.stat().st_size
748
+ if size > MAX_PACKAGE_BYTES:
749
+ raise ValidationError("release ZIP exceeds the 200 MB upload limit", path=str(file_path))
750
+ md5 = hashlib.md5(archive.read_bytes()).hexdigest()
751
+ try:
752
+ unzip_size = sum(info.file_size for info in zipfile.ZipFile(str(archive)).infolist())
753
+ except (OSError, zipfile.BadZipFile) as exc:
754
+ raise ValidationError("release file is not a valid ZIP", path=str(file_path)) from exc
755
+ derived = parse_modus_zip(archive)
756
+ supplied_toc = doc.get("toc_version")
757
+ supplied_games = doc.get("supported_game_versions")
758
+ if supplied_toc is not None and str(supplied_toc) != derived["toc_version"]:
759
+ raise ValidationError("toc_version does not match the ZIP Interface field", path="$.toc_version")
760
+ if supplied_games is not None and _supported_game_versions(supplied_games) != derived["supported_game_versions"]:
761
+ raise ValidationError("supported_game_versions does not match the ZIP Interface field", path="$.supported_game_versions")
762
+ metadata.update({
763
+ "md5": md5,
764
+ "zip_size": size,
765
+ "unzip_size": unzip_size,
766
+ "toc_version": derived["toc_version"],
767
+ "supported_game_versions": derived["supported_game_versions"],
768
+ "path": doc.get("path") or archive.name,
769
+ })
770
+ current_stage = "release_metadata_update" if update else "release_metadata"
771
+ transaction["stages"].append(current_stage)
772
+ transaction["active_stage"] = current_stage
773
+ save_transaction()
719
774
  result = self.release_metadata(metadata, update=update)
720
775
  if archive is None:
776
+ transaction.pop("active_stage", None)
721
777
  transaction.update({"completed": True, "upload": None})
722
778
  save_transaction()
723
779
  return {"project_id": project_id, "file_id": file_id, "metadata": result, "upload": None, "transaction": _safe(transaction)}
724
- transaction["stages"].append("signature")
780
+ current_stage = "signature"
781
+ transaction["stages"].append(current_stage)
782
+ transaction["active_stage"] = current_stage
783
+ save_transaction()
725
784
  signed = self.release_signature(project_id, file_id)
726
- transaction["stages"].append("binary_upload")
785
+ current_stage = "binary_upload"
786
+ transaction["stages"].append(current_stage)
787
+ transaction["active_stage"] = current_stage
788
+ save_transaction()
727
789
  uploaded = self.upload_zip(signed, str(archive))
790
+ transaction.pop("active_stage", None)
728
791
  transaction.update({"completed": True, "upload": uploaded})
729
792
  save_transaction()
730
793
  return {"project_id": project_id, "file_id": file_id, "metadata": result, "upload": uploaded, "transaction": _safe(transaction)}
731
794
  except FuploadError as exc:
732
- transaction["failed_stage"] = exc.stage or transaction["stages"][-1] if transaction["stages"] else "prepare"
795
+ if exc.stage in (None, "dependency_get"):
796
+ exc.stage = current_stage
797
+ transaction["failed_stage"] = exc.stage or current_stage
733
798
  transaction["error"] = exc.as_dict()
734
799
  transaction["retained_archive"] = bool(archive and archive.is_file())
735
800
  try:
@@ -737,6 +802,20 @@ class ModUs:
737
802
  except OSError:
738
803
  pass
739
804
  raise
805
+ except OSError as exc:
806
+ wrapped = FuploadError(
807
+ "ModUs upload preparation failed",
808
+ stage=current_stage,
809
+ details={"response_summary": redact(str(exc)) or exc.__class__.__name__},
810
+ )
811
+ transaction["failed_stage"] = current_stage
812
+ transaction["error"] = wrapped.as_dict()
813
+ transaction["retained_archive"] = bool(archive and archive.is_file())
814
+ try:
815
+ save_transaction()
816
+ except OSError:
817
+ pass
818
+ raise wrapped from exc
740
819
 
741
820
  def execute_read(self, resource: str, action: str, args: Any = None) -> Any:
742
821
  if resource == "session" and action == "doctor": return self.doctor()
@@ -41,6 +41,17 @@ COMPLETE = ProjectStep.COMPLETE.value
41
41
  _SCHEMA = "fupload.v1.modus.project-state"
42
42
  _PLATFORMS = ("modus", "bigfoot")
43
43
  _BIGFOOT_EXCLUSIVE_CATEGORY_ID = 998
44
+ _BASIC_INFO_KEYS = {
45
+ # Shared create/edit form fields.
46
+ "schema", "name", "project_name", "alt_name", "summary", "categories",
47
+ "synchronization_type", "publish_platforms", "publishPlatforms",
48
+ "required_tier_id", "requiredTierId", "repo_url",
49
+ # Create-only image fields and edit-only detail/image fields.
50
+ "logo_base64", "screenshot_base64s", "description",
51
+ "required_dependencies", "images", "image_ops",
52
+ # Known project-detail readback fields preserved in resumable snapshots.
53
+ "cf_url", "logo", "status",
54
+ }
44
55
  _LICENSE_KEYS = {
45
56
  "type", "template", "license_template", "licenseTemplate",
46
57
  "holder", "copyright_holder", "copyrightHolder",
@@ -49,6 +60,15 @@ _LICENSE_KEYS = {
49
60
  }
50
61
 
51
62
 
63
+ def _basic_info_fields(value: Mapping[str, Any]) -> None:
64
+ unknown = sorted(set(value) - _BASIC_INFO_KEYS)
65
+ if unknown:
66
+ raise ValidationError(
67
+ "unknown basic_info field(s): %s" % ", ".join(unknown),
68
+ path="$.basic_info.%s" % unknown[0],
69
+ )
70
+
71
+
52
72
  def _nonempty_text(value: Any, *, path: str) -> str:
53
73
  if not isinstance(value, str) or not value.strip():
54
74
  raise ValidationError("value must be a non-empty string", path=path)
@@ -194,6 +214,7 @@ class ProjectStateMachine:
194
214
  self._require(ProjectStep.BASIC_INFO)
195
215
  value: Dict[str, Any] = dict(info or {})
196
216
  value.update(fields)
217
+ _basic_info_fields(value)
197
218
  name = value.get("name", value.get("project_name"))
198
219
  summary = value.get("summary")
199
220
  _nonempty_text(name, path="$.basic_info.name")
@@ -288,26 +309,33 @@ class ProjectStateMachine:
288
309
  game = snapshot.get("game")
289
310
  basic = snapshot.get("basic_info") or {}
290
311
  lic = snapshot.get("license") or {}
312
+ restored_game: Any = None
313
+ restored_basic: Dict[str, Any] = {}
314
+ restored_license: Dict[str, Any] = {}
291
315
  if state != CHOOSE_GAME:
292
- self._game = _game(game)
316
+ restored_game = _game(game)
293
317
  if state in {LICENSE, COMPLETE}:
294
318
  if not isinstance(basic, Mapping):
295
319
  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"]:
320
+ restored_basic = dict(basic)
321
+ _basic_info_fields(restored_basic)
322
+ _nonempty_text(restored_basic.get("name"), path="$.basic_info.name")
323
+ _nonempty_text(restored_basic.get("summary"), path="$.basic_info.summary")
324
+ restored_basic["publish_platforms"] = _platforms(restored_basic.get("publish_platforms"))
325
+ restored_basic["categories"] = _categories(restored_basic.get("categories"))
326
+ restored_basic["required_tier_id"] = _required_tier(restored_basic.get("required_tier_id"))
327
+ if _BIGFOOT_EXCLUSIVE_CATEGORY_ID in restored_basic["categories"] and restored_basic["publish_platforms"] != ["bigfoot"]:
303
328
  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:
329
+ if "bigfoot" in restored_basic["publish_platforms"] and restored_basic["required_tier_id"] is not None:
305
330
  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:
331
+ derived_sync_type = (1 if "modus" in restored_basic["publish_platforms"] else 0) | (2 if "bigfoot" in restored_basic["publish_platforms"] else 0)
332
+ if restored_basic.get("synchronization_type") != derived_sync_type:
308
333
  raise ValidationError("synchronization_type must match publish_platforms", path="$.basic_info.synchronization_type")
309
334
  if state == COMPLETE:
310
- self._license = _license(lic)
335
+ restored_license = _license(lic)
336
+ self._game = restored_game
337
+ self._basic_info = restored_basic
338
+ self._license = restored_license
311
339
  self._step = str(state)
312
340
 
313
341
  def save(self, path: Union[str, os.PathLike[str]]) -> Path:
@@ -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.10",
5
- "skill_version": "0.0.10",
6
- "tree_sha256": "38621aa4709de68a1cc298c8183fe0a5647c8291b870defdbbb5f54d184fa94a",
4
+ "package_version": "0.0.11",
5
+ "skill_version": "0.0.11",
6
+ "tree_sha256": "a0d589cb5d67c7bfbd34862ee4c3109e88068662ae3b7430c0635e71ace9a8d9",
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": 4492,
96
- "sha256": "0b9cf6c4b3bc7aa37a3649ec6ccb6c8b8aa3d077c8febc2635548546150df356"
95
+ "bytes": 8705,
96
+ "sha256": "10c2db10ade101c6738d9007865db68da54892da59fc1cc0fc1a422296a4bf07"
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": "3f1afb95137eaedc622bb437c9c8a01bdaa729b911f3749e400a429d24abaaa2"
116
+ "sha256": "c7d5308bddd21b0e8042689cdeaba91ba45170a998f42844d42f88b7248fc057"
117
117
  },
118
118
  {
119
119
  "path": "scripts/fupload_cli/blackbox_web.py",
@@ -167,8 +167,8 @@
167
167
  },
168
168
  {
169
169
  "path": "scripts/fupload_cli/modus.py",
170
- "bytes": 38821,
171
- "sha256": "f5edb6898ce515a7e34f0fcab3dc022c2c5185f548644eabf5291e38b22dae22"
170
+ "bytes": 42241,
171
+ "sha256": "ee298b064f4b3c4a25081f76e93d48b18f1b810f647437e78d78a4a20ab5bd5c"
172
172
  },
173
173
  {
174
174
  "path": "scripts/fupload_cli/newbee_auth.py",
@@ -187,8 +187,8 @@
187
187
  },
188
188
  {
189
189
  "path": "scripts/fupload_cli/state_machine.py",
190
- "bytes": 15325,
191
- "sha256": "02f37b745bbb70860c09563b6778014bd1c69ca06ba28777415ad7ce84d02610"
190
+ "bytes": 16450,
191
+ "sha256": "da26b63be4e085b9b39aea4d13a2c58206836d98fa46400b243113ce4b1ff4a9"
192
192
  },
193
193
  {
194
194
  "path": "scripts/fupload_cli/transport.py",
@@ -208,7 +208,7 @@
208
208
  {
209
209
  "path": "SKILL.md",
210
210
  "bytes": 25720,
211
- "sha256": "2e20b91ded57f4d91dbb658abf6fedc636785cc1b3ada23f985c9241c53cd3e6"
211
+ "sha256": "cfc3ce770c3594c5a787b25da03cb2e92e0f6ea6fbd162f26f4b6b3c44295a89"
212
212
  }
213
213
  ]
214
214
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@follenfang/fupload",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "Install and run the Fuploader Agent Skill and Python CLI.",
5
5
  "type": "module",
6
6
  "bin": {