@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,266 @@
|
|
|
1
|
+
"""Argparse command tree for Fupload."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Callable, Dict, Optional, Sequence, Tuple
|
|
10
|
+
|
|
11
|
+
from . import __version__
|
|
12
|
+
from .dd import DD
|
|
13
|
+
from .errors import FuploadError, ValidationError
|
|
14
|
+
from .io import read_json, write_error, write_output
|
|
15
|
+
from .newbee import NewBee
|
|
16
|
+
from .schema import get_schema, schema_help
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
WRITE_HELP = """This command performs one remote business action from a versioned JSON document.
|
|
20
|
+
|
|
21
|
+
It is non-interactive. Unknown fields and fields belonging to another action are rejected.
|
|
22
|
+
On edit/update, an omitted field preserves the remote value; an explicit empty value clears it
|
|
23
|
+
only when the field contract permits. The provider GETs current detail and dynamic options before
|
|
24
|
+
building an allowlisted wire payload, then reads the result back after the write.
|
|
25
|
+
|
|
26
|
+
Public/review changes are never implicit. Set public and submit_for_review explicitly where the
|
|
27
|
+
schema exposes them. The calling Skill must show the complete plan and obtain confirmation first.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _parser(**kwargs: Any) -> argparse.ArgumentParser:
|
|
32
|
+
return argparse.ArgumentParser(
|
|
33
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
34
|
+
**kwargs,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _positive(value: str) -> int:
|
|
39
|
+
number = int(value)
|
|
40
|
+
if number <= 0:
|
|
41
|
+
raise argparse.ArgumentTypeError("must be greater than zero")
|
|
42
|
+
return number
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _page_flags(parser: argparse.ArgumentParser, *, offset: bool = False) -> None:
|
|
46
|
+
if offset:
|
|
47
|
+
parser.add_argument("--offset", type=int, default=0, help="Zero-based result offset (technical default: 0).")
|
|
48
|
+
else:
|
|
49
|
+
parser.add_argument("--page", type=_positive, default=1, help="One-based page number (technical default: 1).")
|
|
50
|
+
parser.add_argument("--page-size", type=_positive, default=50, help="Page size, capped by the platform (technical default: 50).")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _list_flags(parser: argparse.ArgumentParser, *, offset: bool = False, game_type: bool = False) -> None:
|
|
54
|
+
parser.add_argument("--keyword", default="", help="Optional name/title filter; empty means all current-author records.")
|
|
55
|
+
_page_flags(parser, offset=offset)
|
|
56
|
+
if game_type:
|
|
57
|
+
parser.add_argument("--game-type", type=_positive, required=True, help="DD game type selected from `dd options game-types`.")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _write_leaf(parent: argparse._SubParsersAction, platform: str, resource: str, action: str, summary: str) -> None:
|
|
61
|
+
leaf = parent.add_parser(
|
|
62
|
+
action, help=summary, description=summary + "\n\n" + WRITE_HELP,
|
|
63
|
+
epilog=schema_help(platform, resource, action),
|
|
64
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
65
|
+
)
|
|
66
|
+
leaf.add_argument("--input", required=True, metavar="PATH|-", help="Versioned JSON file, or - to read one JSON object from stdin.")
|
|
67
|
+
leaf.add_argument("--dry-run", action="store_true", help="Validate schema and local files only; do not authenticate, upload, or write remotely.")
|
|
68
|
+
if platform == "dd":
|
|
69
|
+
leaf.add_argument("--session", help="Opaque task session ID returned by `dd session start`; required for a live DD operation.")
|
|
70
|
+
leaf.set_defaults(handler="write", platform=platform, resource=resource, action=action)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _read_leaf(parent: argparse._SubParsersAction, name: str, summary: str, **defaults: Any) -> argparse.ArgumentParser:
|
|
74
|
+
leaf = parent.add_parser(name, help=summary, description=summary + "\n\nThis is a read-only command and emits stable JSON.")
|
|
75
|
+
leaf.set_defaults(handler="read", **defaults)
|
|
76
|
+
if defaults.get("platform") == "dd" and defaults.get("resource") != "session":
|
|
77
|
+
leaf.add_argument("--session", help="Opaque task session ID returned by `dd session start`; required for this live DD read.")
|
|
78
|
+
return leaf
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _newbee_relationship_tree(parent: argparse.ArgumentParser, resource: str, label: str) -> None:
|
|
82
|
+
authors = parent.add_parser("co-author", help="Search, list, or replace %s co-authors" % label).add_subparsers(dest="author_action", required=True)
|
|
83
|
+
leaf = _read_leaf(authors, "search", "Search current co-author candidates.", platform="newbee", resource=resource, action="co-author-search"); leaf.add_argument("--keyword", required=True)
|
|
84
|
+
leaf = _read_leaf(authors, "list", "List current co-authors for one %s." % label, platform="newbee", resource=resource, action="co-author-list"); leaf.add_argument("--id", type=_positive, required=True)
|
|
85
|
+
_write_leaf(authors, "newbee", resource + "-co-author", "set", "Replace the complete %s co-author list; an empty array clears it." % label)
|
|
86
|
+
references = parent.add_parser("reference", help="Search, list, or replace %s content references" % label).add_subparsers(dest="reference_action", required=True)
|
|
87
|
+
leaf = _read_leaf(references, "search", "Search content that can be referenced by this %s." % label, platform="newbee", resource=resource, action="reference-search"); leaf.add_argument("--keyword", required=True)
|
|
88
|
+
leaf = _read_leaf(references, "list", "List current references for one %s." % label, platform="newbee", resource=resource, action="reference-list"); leaf.add_argument("--id", type=_positive, required=True)
|
|
89
|
+
_write_leaf(references, "newbee", resource + "-reference", "set", "Replace the complete %s reference list; an empty array clears it." % label)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _newbee_tree(platforms: argparse._SubParsersAction) -> None:
|
|
93
|
+
root = platforms.add_parser("newbee", help="NewBeeBox Creator operations", description="Reuse the signed-in NewBeeBox desktop auth-store; no token input is accepted.")
|
|
94
|
+
groups = root.add_subparsers(dest="resource_command", required=True)
|
|
95
|
+
|
|
96
|
+
session = groups.add_parser("session", help="Authentication diagnostics").add_subparsers(dest="action_command", required=True)
|
|
97
|
+
_read_leaf(session, "doctor", "Verify Windows Known Folder credentials, fixed official origins, and the Creator token exchange.", platform="newbee", resource="session", action="doctor")
|
|
98
|
+
options = groups.add_parser("options", help="Read dynamic business choices before writing").add_subparsers(dest="option_action", required=True)
|
|
99
|
+
for action, text in (
|
|
100
|
+
("content-origins", "List current content-origin values."),
|
|
101
|
+
("subscribe-plans", "List current author subscription plan levels."),
|
|
102
|
+
("time-ranges", "List current one-time purchase durations."),
|
|
103
|
+
):
|
|
104
|
+
_read_leaf(options, action, text, platform="newbee", resource="options", action=action)
|
|
105
|
+
|
|
106
|
+
plugin = groups.add_parser("plugin", help="Plugin create, version update, metadata edit, and reads").add_subparsers(dest="action_command", required=True)
|
|
107
|
+
for action, text in (("create", "Create a private plugin record; public review is applied only after a version exists."), ("update", "Upload one immutable plugin version."), ("edit", "Edit plugin metadata or explicit public/review state."), ("delete", "Delete one explicitly confirmed plugin record.")):
|
|
108
|
+
_write_leaf(plugin, "newbee", "plugin", action, text)
|
|
109
|
+
leaf = _read_leaf(plugin, "list", "List plugins owned by the current author.", platform="newbee", resource="plugin", action="list"); _list_flags(leaf)
|
|
110
|
+
leaf = _read_leaf(plugin, "get", "Read one plugin detail by numeric Creator ID.", platform="newbee", resource="plugin", action="get"); leaf.add_argument("--id", type=_positive, required=True)
|
|
111
|
+
_read_leaf(plugin, "categories", "List current plugin categories and IDs.", platform="newbee", resource="plugin", action="categories")
|
|
112
|
+
_read_leaf(plugin, "game-versions", "List current game branches/build IDs, including retail and classic variants.", platform="newbee", resource="plugin", action="game-versions")
|
|
113
|
+
leaf = _read_leaf(plugin, "versions", "List uploaded versions for one plugin.", platform="newbee", resource="plugin", action="versions"); leaf.add_argument("--id", type=_positive, required=True); _page_flags(leaf)
|
|
114
|
+
changelog = plugin.add_parser("changelog", help="Read or edit plugin version logs").add_subparsers(dest="changelog_action", required=True)
|
|
115
|
+
leaf = _read_leaf(changelog, "list", "List version log records for one plugin.", platform="newbee", resource="plugin", action="changelog-list"); leaf.add_argument("--id", type=_positive, required=True); _page_flags(leaf)
|
|
116
|
+
leaf = _read_leaf(changelog, "get", "Read one plugin version log by file ID.", platform="newbee", resource="plugin", action="changelog-get"); leaf.add_argument("--id", type=_positive, required=True)
|
|
117
|
+
_write_leaf(changelog, "newbee", "plugin-changelog", "edit", "Edit or explicitly clear one existing plugin version log.")
|
|
118
|
+
_newbee_relationship_tree(plugin, "plugin", "plugin")
|
|
119
|
+
|
|
120
|
+
config = groups.add_parser("config", help="Configuration share create, backup update, metadata edit, and reads").add_subparsers(dest="action_command", required=True)
|
|
121
|
+
for action, text in (("create", "Create a configuration share from an existing desktop cloud backup."), ("update", "Replace cloud-backup content selections without changing metadata."), ("edit", "Edit configuration metadata, business settings, channel, or review state."), ("delete", "Delete one explicitly confirmed configuration record.")):
|
|
122
|
+
_write_leaf(config, "newbee", "config", action, text)
|
|
123
|
+
leaf = _read_leaf(config, "list", "List configuration shares owned by the current author.", platform="newbee", resource="config", action="list"); _list_flags(leaf, offset=True)
|
|
124
|
+
leaf = _read_leaf(config, "get", "Read a safe configuration-share detail without raw backup paths.", platform="newbee", resource="config", action="get"); leaf.add_argument("--id", type=_positive, required=True)
|
|
125
|
+
_read_leaf(config, "backups", "List cloud backups already uploaded by the NewBeeBox desktop client.", platform="newbee", resource="config", action="backups")
|
|
126
|
+
leaf = _read_leaf(config, "backup-get", "Read selectable plugins, ignored items, fonts, materials, and roles from one cloud backup.", platform="newbee", resource="config", action="backup-get"); leaf.add_argument("--id", type=_positive, required=True, help="Cloud backup ID.")
|
|
127
|
+
_newbee_relationship_tree(config, "config", "configuration share")
|
|
128
|
+
|
|
129
|
+
wa = groups.add_parser("wa", help="WA/string create, version update, metadata edit, and attached operations").add_subparsers(dest="action_command", required=True)
|
|
130
|
+
for action, text in (("create", "Create a WA/string record and first string version."), ("update", "Publish one new immutable WA/string version."), ("edit", "Edit WA metadata, media, categories, attachments, business settings, or review state."), ("delete", "Delete one explicitly confirmed WA/string record.")):
|
|
131
|
+
_write_leaf(wa, "newbee", "wa", action, text)
|
|
132
|
+
leaf = _read_leaf(wa, "list", "List WA/string records owned by the current author; raw strings are redacted.", platform="newbee", resource="wa", action="list"); _list_flags(leaf, offset=True)
|
|
133
|
+
leaf = _read_leaf(wa, "get", "Read one WA metadata detail; raw strings are replaced with length and SHA-256.", platform="newbee", resource="wa", action="get"); leaf.add_argument("--id", type=_positive, required=True)
|
|
134
|
+
leaf = _read_leaf(wa, "categories", "List WA categories for a selected game version.", platform="newbee", resource="wa", action="categories"); leaf.add_argument("--game-version-id", type=_positive, required=True)
|
|
135
|
+
_read_leaf(wa, "attachment-paths", "List platform-provided attachment install types and paths.", platform="newbee", resource="wa", action="attachment-paths")
|
|
136
|
+
media = wa.add_parser("media", help="Upload one WA image or verified attachment material").add_subparsers(dest="media_action", required=True)
|
|
137
|
+
_write_leaf(media, "newbee", "wa-media", "upload", "Upload one WA media file and return its reusable platform reference.")
|
|
138
|
+
logs = wa.add_parser("changelog", help="Read or edit WA version logs").add_subparsers(dest="log_action", required=True)
|
|
139
|
+
leaf = _read_leaf(logs, "latest", "Read the latest WA version summary.", platform="newbee", resource="wa", action="changelog-latest"); leaf.add_argument("--id", type=_positive, required=True)
|
|
140
|
+
leaf = _read_leaf(logs, "list", "List WA version log records.", platform="newbee", resource="wa", action="changelog-list"); leaf.add_argument("--id", type=_positive, required=True); _page_flags(leaf)
|
|
141
|
+
_write_leaf(logs, "newbee", "wa-changelog", "edit", "Edit or explicitly clear one WA version log.")
|
|
142
|
+
_newbee_relationship_tree(wa, "wa", "WA")
|
|
143
|
+
share_code = wa.add_parser("share-code", help="Set or refresh the NewBeeBox WA share code").add_subparsers(dest="share_code_action", required=True)
|
|
144
|
+
_write_leaf(share_code, "newbee", "wa-share-code", "set", "Set or refresh the share code for one WA module.")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _dd_tree(platforms: argparse._SubParsersAction) -> None:
|
|
148
|
+
root = platforms.add_parser("dd", help="NetEase DD author operations", description="Use DD's official netease_dd.exe, credentials, native login, and NEP signer. No token input is accepted.")
|
|
149
|
+
groups = root.add_subparsers(dest="resource_command", required=True)
|
|
150
|
+
session = groups.add_parser("session", help="Installation and task-session lifecycle").add_subparsers(dest="action_command", required=True)
|
|
151
|
+
_read_leaf(session, "doctor", "Discover DD, verify its official Authenticode publisher, and diagnose GUI/broker state without logging in.", platform="dd", resource="session", action="doctor")
|
|
152
|
+
leaf = _read_leaf(session, "start", "Close confirmed official DD GUI instances, then start one task-scoped native login session.", platform="dd", resource="session", action="start")
|
|
153
|
+
leaf.add_argument("--confirm-close-gui", action="store_true", help="Required only when doctor reports a running official DD GUI; the Skill obtains user consent before using it.")
|
|
154
|
+
leaf = _read_leaf(session, "status", "Read the local task-broker status without creating a login.", platform="dd", resource="session", action="status")
|
|
155
|
+
leaf.add_argument("--session", help="Optional opaque session ID; omitted selects the single active local session.")
|
|
156
|
+
leaf = _read_leaf(session, "stop", "Log out and stop one DD task session.", platform="dd", resource="session", action="stop")
|
|
157
|
+
leaf.add_argument("--session", required=True, help="Opaque session ID returned by `dd session start`.")
|
|
158
|
+
options = groups.add_parser("options", help="Read dynamic business choices before writing").add_subparsers(dest="option_action", required=True)
|
|
159
|
+
for action, text in (("game-types", "List DD game types."), ("channels", "List selectable DD rooms/channels for room association."), ("life-types", "List share-code and purchase life types."), ("vip-levels", "List available anchor VIP levels."), ("associated-acts", "List current-author content eligible for association.")):
|
|
160
|
+
leaf = _read_leaf(options, action, text, platform="dd", resource="options", action=action)
|
|
161
|
+
if action == "associated-acts":
|
|
162
|
+
leaf.add_argument("--game-type", type=_positive, required=True)
|
|
163
|
+
|
|
164
|
+
plugin = groups.add_parser("plugin", help="DD plugin create, version update, metadata edit, and reads").add_subparsers(dest="action_command", required=True)
|
|
165
|
+
for action, text in (("create", "Create a DD plugin with its first selected version."), ("update", "Publish a DD plugin version while preserving first-publication metadata."), ("edit", "Edit DD plugin commercial, association, room/channel, and creation-statement settings."), ("delete", "Delete one explicitly confirmed DD plugin record.")):
|
|
166
|
+
_write_leaf(plugin, "dd", "plugin", action, text)
|
|
167
|
+
leaf = _read_leaf(plugin, "list", "List plugins owned by the current DD author account.", platform="dd", resource="plugin", action="list"); _list_flags(leaf, game_type=True)
|
|
168
|
+
leaf = _read_leaf(plugin, "get", "Read one DD plugin detail by share SN.", platform="dd", resource="plugin", action="get"); leaf.add_argument("--sn", required=True)
|
|
169
|
+
_read_leaf(plugin, "categories", "List DD plugin category choices.", platform="dd", resource="plugin", action="categories")
|
|
170
|
+
leaf = _read_leaf(plugin, "game-versions", "List build choices for one DD game type.", platform="dd", resource="plugin", action="game-versions"); leaf.add_argument("--game-type", type=_positive, required=True)
|
|
171
|
+
leaf = _read_leaf(plugin, "versions", "List versions for one DD plugin.", platform="dd", resource="plugin", action="versions"); leaf.add_argument("--sn", required=True); leaf.add_argument("--game-type", type=_positive, required=True); leaf.add_argument("--page", type=_positive, default=1)
|
|
172
|
+
|
|
173
|
+
config = groups.add_parser("config", help="DD configuration create, backup-content update, metadata edit, and reads").add_subparsers(dest="action_command", required=True)
|
|
174
|
+
for action, text in (("create", "Create a DD configuration share from an existing DD cloud backup."), ("update", "Update selected backup content and inner versions."), ("edit", "Edit DD configuration metadata and commercial/association settings."), ("delete", "Delete one explicitly confirmed DD configuration record.")):
|
|
175
|
+
_write_leaf(config, "dd", "config", action, text)
|
|
176
|
+
leaf = _read_leaf(config, "list", "List configuration shares owned by the current DD author.", platform="dd", resource="config", action="list"); _list_flags(leaf, game_type=True)
|
|
177
|
+
leaf = _read_leaf(config, "get", "Read one DD configuration detail by share SN.", platform="dd", resource="config", action="get"); leaf.add_argument("--sn", required=True)
|
|
178
|
+
_read_leaf(config, "backups", "List DD cloud backups available to the current account.", platform="dd", resource="config", action="backups")
|
|
179
|
+
leaf = _read_leaf(config, "backup-get", "Read one DD backup's complete selectable content.", platform="dd", resource="config", action="backup-get"); leaf.add_argument("--sn", required=True)
|
|
180
|
+
|
|
181
|
+
wa = groups.add_parser("wa", help="DD WA/string create, content update, metadata edit, and reads").add_subparsers(dest="action_command", required=True)
|
|
182
|
+
for action, text in (("create", "Create a DD WA/string record."), ("update", "Publish updated DD WA content/version/material while preserving metadata."), ("edit", "Edit DD WA metadata and commercial/association settings."), ("delete", "Delete one explicitly confirmed DD WA/string record.")):
|
|
183
|
+
_write_leaf(wa, "dd", "wa", action, text)
|
|
184
|
+
leaf = _read_leaf(wa, "list", "List WA/string records owned by the current DD author.", platform="dd", resource="wa", action="list"); _list_flags(leaf, game_type=True)
|
|
185
|
+
leaf = _read_leaf(wa, "get", "Read one DD WA detail by share SN.", platform="dd", resource="wa", action="get"); leaf.add_argument("--sn", required=True)
|
|
186
|
+
leaf = _read_leaf(wa, "categories", "List DD WA category choices for a game type.", platform="dd", resource="wa", action="categories"); leaf.add_argument("--game-type", type=_positive, required=True)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
190
|
+
parser = _parser(
|
|
191
|
+
prog="fupload",
|
|
192
|
+
description="Atomic World of Warcraft author publishing CLI for NewBeeBox and NetEase DD.",
|
|
193
|
+
epilog="All output is JSON. Write commands require versioned JSON through --input and never prompt.",
|
|
194
|
+
)
|
|
195
|
+
parser.add_argument("--version", action="version", version="%(prog)s " + __version__)
|
|
196
|
+
platforms = parser.add_subparsers(dest="platform_command", required=True)
|
|
197
|
+
_newbee_tree(platforms)
|
|
198
|
+
_dd_tree(platforms)
|
|
199
|
+
return parser
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _validate_nested_files(doc: Dict[str, Any]) -> None:
|
|
203
|
+
for name in ("screenshot_files", "picture_files", "image_files", "detail_img_files", "display_img_files"):
|
|
204
|
+
if name not in doc:
|
|
205
|
+
continue
|
|
206
|
+
if not isinstance(doc[name], list):
|
|
207
|
+
raise ValidationError("expected array", path="$.%s" % name)
|
|
208
|
+
for index, value in enumerate(doc[name]):
|
|
209
|
+
if not isinstance(value, str) or not os.path.isfile(value):
|
|
210
|
+
raise ValidationError("file does not exist or is not a regular file", path="$.%s[%d]" % (name, index))
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _dry_run_data(doc: Dict[str, Any], schema_name: str) -> Dict[str, Any]:
|
|
214
|
+
files = {}
|
|
215
|
+
for name, value in doc.items():
|
|
216
|
+
if name == "file" or name.endswith("_file"):
|
|
217
|
+
if isinstance(value, str) and value:
|
|
218
|
+
files[name] = {"name": Path(value).name, "size": Path(value).stat().st_size}
|
|
219
|
+
elif name.endswith("_files") and isinstance(value, list):
|
|
220
|
+
files[name] = [{"name": Path(path).name, "size": Path(path).stat().st_size} for path in value]
|
|
221
|
+
return {
|
|
222
|
+
"schema_valid": True,
|
|
223
|
+
"input_schema": schema_name,
|
|
224
|
+
"present_fields": sorted(set(doc) - {"schema"}),
|
|
225
|
+
"local_files": files,
|
|
226
|
+
"remote_validation_performed": False,
|
|
227
|
+
"note": "Remote IDs, permissions, current state, and dynamic choices are checked only during execution.",
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
232
|
+
parser = build_parser()
|
|
233
|
+
args = parser.parse_args(argv)
|
|
234
|
+
platform = getattr(args, "platform", getattr(args, "platform_command", "unknown"))
|
|
235
|
+
resource = getattr(args, "resource", "unknown")
|
|
236
|
+
action = getattr(args, "action", "unknown")
|
|
237
|
+
operation = "%s.%s" % (resource, action)
|
|
238
|
+
try:
|
|
239
|
+
if args.handler == "write":
|
|
240
|
+
schema = get_schema(platform, resource, action)
|
|
241
|
+
doc = schema.validate(read_json(args.input))
|
|
242
|
+
_validate_nested_files(doc)
|
|
243
|
+
if args.dry_run:
|
|
244
|
+
write_output(platform, operation, _dry_run_data(doc, schema.name), dry_run=True)
|
|
245
|
+
return 0
|
|
246
|
+
provider = NewBee() if platform == "newbee" else DD()
|
|
247
|
+
if platform == "dd":
|
|
248
|
+
data = provider.execute_write(resource, action, doc, getattr(args, "session", None))
|
|
249
|
+
else:
|
|
250
|
+
data = provider.execute_write(resource, action, doc)
|
|
251
|
+
write_output(platform, operation, data)
|
|
252
|
+
return 0
|
|
253
|
+
provider = NewBee() if platform == "newbee" else DD()
|
|
254
|
+
if platform == "dd":
|
|
255
|
+
data = provider.execute_read(resource, action, args, getattr(args, "session", None))
|
|
256
|
+
else:
|
|
257
|
+
data = provider.execute_read(resource, action, args)
|
|
258
|
+
write_output(platform, operation, data)
|
|
259
|
+
return 0
|
|
260
|
+
except (FuploadError, OSError, ValueError) as exc:
|
|
261
|
+
write_error(platform, operation, exc)
|
|
262
|
+
return 2
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
if __name__ == "__main__":
|
|
266
|
+
raise SystemExit(main())
|