@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.
Files changed (43) hide show
  1. package/README.md +208 -3
  2. package/fupload/SKILL.md +129 -0
  3. package/fupload/agents/openai.yaml +4 -0
  4. package/fupload/examples/dd-config-delete.json +5 -0
  5. package/fupload/examples/dd-config-update.json +25 -0
  6. package/fupload/examples/dd-plugin-delete.json +5 -0
  7. package/fupload/examples/dd-plugin-update.json +9 -0
  8. package/fupload/examples/dd-wa-delete.json +5 -0
  9. package/fupload/examples/dd-wa-edit.json +9 -0
  10. package/fupload/examples/newbee-config-delete.json +5 -0
  11. package/fupload/examples/newbee-config-update.json +10 -0
  12. package/fupload/examples/newbee-plugin-create.json +14 -0
  13. package/fupload/examples/newbee-plugin-delete.json +5 -0
  14. package/fupload/examples/newbee-wa-delete.json +5 -0
  15. package/fupload/examples/newbee-wa-update.json +8 -0
  16. package/fupload/references/dd.md +105 -0
  17. package/fupload/references/newbee-official-cli.md +288 -0
  18. package/fupload/references/newbee.md +80 -0
  19. package/fupload/references/workflow.md +67 -0
  20. package/fupload/scripts/fupload.py +17 -0
  21. package/fupload/scripts/fupload_cli/__init__.py +3 -0
  22. package/fupload/scripts/fupload_cli/cli.py +266 -0
  23. package/fupload/scripts/fupload_cli/dd.py +2406 -0
  24. package/fupload/scripts/fupload_cli/dd_broker.py +634 -0
  25. package/fupload/scripts/fupload_cli/dd_sidecar.py +860 -0
  26. package/fupload/scripts/fupload_cli/errors.py +94 -0
  27. package/fupload/scripts/fupload_cli/io.py +125 -0
  28. package/fupload/scripts/fupload_cli/newbee.py +1412 -0
  29. package/fupload/scripts/fupload_cli/newbee_auth.py +135 -0
  30. package/fupload/scripts/fupload_cli/schema.py +539 -0
  31. package/fupload/scripts/fupload_cli/transport.py +125 -0
  32. package/fupload/scripts/fupload_cli/trust.py +207 -0
  33. package/npm/bin/fupload.mjs +90 -0
  34. package/npm/lib/managed-install.mjs +86 -0
  35. package/npm/lib/options.mjs +38 -0
  36. package/npm/lib/python.mjs +45 -0
  37. package/npm/lib/skill-installer.mjs +228 -0
  38. package/npm/lib/uninstall.mjs +211 -0
  39. package/npm/lib/update.mjs +99 -0
  40. package/npm/lib/versions.mjs +63 -0
  41. package/npm/postinstall.mjs +18 -0
  42. package/npm/skill-manifest.json +164 -0
  43. package/package.json +50 -6
package/README.md CHANGED
@@ -1,4 +1,209 @@
1
- # Fuploader bootstrap
1
+ # Fuploader
2
2
 
3
- This prerelease only establishes the npm package identity. Install the `latest`
4
- release for the Fuploader CLI and Agent Skill.
3
+ Fuploader 是一个面向 Agent 的《魔兽世界》作者发布 Skill 和 CLI。npm 安装后统一使用 `fupload` 命令;命令内部运行随 Skill 分发的纯 Python 实现。新手盒子(NewBeeBox)默认优先使用官方 `ncc` CLI,用户显式要求第三方管理工具时才使用 Fuploader 通道;网易 DD 使用 Fuploader 调用官方无头客户端。
4
+
5
+ 项目强调显式调用、完整字段收集、写入前确认和写入后读回验证。CLI 只负责单次原子读写,业务选择、执行计划和异常恢复由 Agent 在对话中完成。
6
+
7
+ ## 功能
8
+
9
+ - 支持新手盒子官方 `ncc`、第三方 Python 管理通道和网易 DD。
10
+ - 支持插件、配置分享、WA/字符串的创建、内容更新与元数据编辑。
11
+ - 官方通道严格采用已安装 `ncc` 暴露的能力;第三方 Python 通道覆盖版本、游戏分支、分类、媒体、可见性、审核、商业设置、频道、关联内容和配置备份选择等页面字段。
12
+ - 第三方 Python 以 Creator Center 网页的请求和表单状态为基准,编辑和更新采用 `GET -> 动态选项查询 -> presence-aware patch -> 写入 -> 读回`;关联作者/内容在主记录读回后替换并再次读回。官方通道采用 `ncc` 文档规定的只读查询、写入和读回命令。
13
+ - 第三方 Python 写入使用严格 JSON Schema:未知字段、重复键、`NaN` 和 `Infinity` 均会被拒绝;官方 `ncc` 使用参数和 `-o json` 结构化输出。
14
+ - 新手盒子官方通道复用本机 `ncc login` 或预先注入的 `NCC_TOKEN`,第三方通道复用桌面客户端登录状态;不接收或输出 token、cookie、JWT、签名 URL、DD `clientNo`、原始 WA 字符串或原始配置内容。
15
+ - DD 通过已安装官方客户端的无头运行环境完成原生登录、签名和官方 `WowUIInterface.parseWa` WA2 解析;一次发布任务只建立一个串行会话,任务结束立即退出。
16
+ - DD 写入前按官方网页的详情投影和表单校验重建完整请求;可选 VIP/频道依赖只在启用时查询,插件版本历史遍历分页,WA 新版本只接受纯数字。4xx/业务错误会在 DD 版本目录的 `Fupload/logs` 保存经过递归脱敏且按 UTF-8 字节限长的请求与响应记录。
17
+ - DD 回归以 `resource × action × field × state` 表驱动矩阵覆盖 195 个 create/update/edit/delete 输入字段;每个字段校验正常值、替代值、遗漏、null、非法类型及适用的 false/0/空值/边界,并捕获 JSON 序列化后的最终 endpoint、请求体、上传和 mutation 次数。
18
+
19
+ ## 目录
20
+
21
+ ```text
22
+ npm/ # npm 启动器、Skill 安装/卸载及发布检查
23
+ fupload/
24
+ |- SKILL.md # Agent Skill 主入口
25
+ |- agents/openai.yaml # Skill 元数据
26
+ |- references/ # 工作流和双平台完整字段契约
27
+ |- examples/ # 双平台 JSON 输入示例
28
+ `- scripts/
29
+ |- fupload.py # CLI 入口
30
+ |- fupload_cli/ # Python 实现
31
+ `- tests/ # 回归测试
32
+ ```
33
+
34
+ `analyze/` 用于本地探索、真实验证中间文件和报告,已被 Git 忽略。
35
+
36
+ ## 环境要求
37
+
38
+ - Fuploader npm CLI:Node.js >= 18.18、Python >= 3.9
39
+ - 新手盒子官方通道:Windows、macOS 或 Linux,Node.js >= 18、官方 `ncc` CLI 与本机登录态
40
+ - 新手盒子第三方 Python 通道:Windows、Python 3、已安装并登录新手盒子桌面客户端
41
+ - 网易 DD:Windows、Python 3、已安装并登录官方客户端
42
+
43
+ 官方 `ncc` 的安装命令为 `npm i -g @newbeebox/newbeebox-creator-center-cli@latest`。Fuploader 不读取其凭据文件;用户在自己的终端完成 `ncc login`,Agent 仅用 `ncc whoami -o json` 验证。自动化可在启动 Agent 前注入 `NCC_TOKEN`,令牌不得写入命令参数、项目文件或 Git。
44
+
45
+ 第三方 Python 通道的认证目录由 Windows Known Folder API 定位到用户 Roaming AppData 下的 `NewBeeBox/auth-store`。NewBee API、认证、元数据和上传 origin 固定为官方 HTTPS 地址,环境变量不能重定向凭据或文件。DD 会自动查找安装目录,验证 `netease_dd.exe` 的 Authenticode 官方发布者后再启动,并把稳定的 sidecar 设备状态保存在 Roaming AppData 下的 `CCVoiceHub/Fupload/sidecar-device.json`。
46
+
47
+ ## 安装
48
+
49
+ ```powershell
50
+ npm install -g @follenfang/fupload
51
+ fupload --version
52
+ fupload --help
53
+ ```
54
+
55
+ 全局安装会同时创建 `fupload` 命令,并把版本匹配的 Skill 原子安装到 `~/.agents/skills/fupload/`。可用 `FUPLOAD_AGENT_HOME` 把默认位置改为 `<home>/skills/fupload`,或在单次命令中使用 `--skill-dir <完整路径>`;所有成功管理过的 Skill 路径都会登记,供完整卸载使用。
56
+
57
+ ## 作为 Skill 使用
58
+
59
+ 安装后显式调用:
60
+
61
+ ```text
62
+ $fupload
63
+ ```
64
+
65
+ 该 Skill 不会因普通提及“发布”“新手盒子”或“DD”而自动触发。Agent 会先询问平台、资源和动作。新手盒子会自动检测 `ncc`:已安装即默认使用官方通道;未安装时先询问是否安装;只有用户显式选择第三方 Python 管理工具才进入项目内 CLI。Agent 在被发布项目中创建 `publish/<时间>-<平台>-<资源>-<动作>/`,同一次发布的脱敏 JSON 按原子步骤保存为 `01-<动作>.json`、`02-<动作>.json`。展示完整写入计划并得到确认后才会真实写入。
66
+
67
+ ## CLI 使用
68
+
69
+ 新手盒子官方 CLI:
70
+
71
+ ```powershell
72
+ npm i -g @newbeebox/newbeebox-creator-center-cli@latest
73
+ ncc -V
74
+ ncc docs
75
+ ncc whoami -o json
76
+ ```
77
+
78
+ 创建令牌和本机登录说明见[官方 CLI 文档](https://creator.newbeebox.com/cli-docs)。不要把令牌粘贴到 Agent 对话;在自己的终端完成 `ncc login`。
79
+
80
+ 以下为 Fuploader 第三方 NewBeeBox 与 DD 执行层。
81
+
82
+ 查看总帮助:
83
+
84
+ ```powershell
85
+ fupload --help
86
+ ```
87
+
88
+ 查看具体操作的可执行字段契约:
89
+
90
+ ```powershell
91
+ fupload newbee plugin create --help
92
+ fupload dd config update --help
93
+ ```
94
+
95
+ 检查桌面登录与安装状态:
96
+
97
+ ```powershell
98
+ fupload newbee session doctor
99
+ fupload dd session doctor
100
+ ```
101
+
102
+ DD doctor 不会登录。若输出 `gui_running=true`,需先取得用户同意,随后关闭已验证的官方 GUI 并建立一个任务会话:
103
+
104
+ ```powershell
105
+ fupload dd session start --confirm-close-gui
106
+ fupload dd options game-types --session <session-id>
107
+ fupload dd session stop --session <session-id>
108
+ ```
109
+
110
+ GUI 未运行时,`session start` 不加 `--confirm-close-gui`。后续 DD 的动态查询、写入和读回都传同一个 `--session`,并在 `finally` 中 stop。
111
+
112
+ 读取当前游戏分支:
113
+
114
+ ```powershell
115
+ fupload newbee plugin game-versions
116
+ fupload dd options game-types
117
+ ```
118
+
119
+ 写操作只接受 JSON 输入。建议先 dry-run:
120
+
121
+ ```powershell
122
+ fupload newbee plugin create --input fupload\examples\newbee-plugin-create.json --dry-run
123
+ fupload dd plugin update --input fupload\examples\dd-plugin-update.json --dry-run
124
+ ```
125
+
126
+ 所有命令输出一个 `schema=fupload.output.v1` 的 JSON 对象。退出码 `0` 表示成功,退出码 `2` 表示校验、会话、平台或写后验证错误。
127
+
128
+ ## 升级与完整卸载
129
+
130
+ ```powershell
131
+ fupload update
132
+ fupload uninstall
133
+ ```
134
+
135
+ `fupload update` 固定安装 `@follenfang/fupload@latest`,随后把默认位置和所有登记有效的自定义 Skill 同步到同一版本。未知目录不会被覆盖。
136
+
137
+ `fupload uninstall` 先删除所有仍有有效 npm 管理标记的 Fuploader Skill,再调用 npm 删除 `@follenfang/fupload` 和 `fupload` 命令。Skill 清理失败时 npm 包保持可用,处理占用或权限问题后可重试。项目中的 `publish/`、新手盒子/DD 登录数据和 DD 日志始终保留。
138
+
139
+ npm 7 及以上不执行卸载 lifecycle,因此直接运行 `npm uninstall -g @follenfang/fupload` 只删除 npm 包和 CLI,可能留下 Skill;它不属于完整卸载流程。
140
+
141
+ ## 工作流约束
142
+
143
+ 1. 写入前先运行目标 leaf command 的 `--help`,以运行时 Schema 为准。
144
+ 2. 使用只读命令获取账号记录、分类、版本、备份、频道和关联候选,不猜测 ID;DD 的全部命令复用一个任务 session。
145
+ 3. DD 配置按 `backup_sn -> backup detail -> WTF 账号/服务器/角色 -> 该账号的 WA` 逐层选择,依赖闭合后只生成一次最终 JSON。
146
+ 4. 每次独立发布都在目标项目的 `publish/` 下创建新目录,不把 JSON 写入 Skill 目录,也不自动删除发布记录。
147
+ 5. 第三方 Python 写入先执行 `--dry-run`;官方 `ncc` 只对文档声明支持的叶子执行 dry-run,例如 `addons push --dry-run`。
148
+ 6. 得到明确确认后串行写入,每步成功后立即读回验证。
149
+ 7. 遇到 `verification_required` 时先读取远端状态,再决定是否重试。
150
+
151
+ 完整契约见:
152
+
153
+ - [工作流与 CLI 契约](fupload/references/workflow.md)
154
+ - [新手盒子官方 CLI 完整参考](fupload/references/newbee-official-cli.md)
155
+ - [新手盒子第三方 Python 字段参考](fupload/references/newbee.md)
156
+ - [网易 DD 字段参考](fupload/references/dd.md)
157
+
158
+ ## 测试
159
+
160
+ ```powershell
161
+ npm test
162
+ npm run check:manifest
163
+ npm run test:pack
164
+ npm run test:install
165
+ python -m unittest discover -s fupload\scripts\tests
166
+ python -m compileall -q fupload\scripts
167
+ ```
168
+
169
+ 当前回归测试覆盖 CLI 路由、严格 JSON、字段 Schema、双平台 builder、动态选项校验、字段保留与清空语义,以及所有内置示例的 dry-run。
170
+
171
+ DD 逐字段 wire 矩阵可单独运行并生成本地审计报告:
172
+
173
+ ```powershell
174
+ python -m unittest discover -s fupload\scripts\tests -p test_dd_wire_matrix.py -v
175
+ python fupload\scripts\tests\generate_dd_wire_matrix_report.py > analyze\dd-field-by-field-wire-regression-20260801.md
176
+ ```
177
+
178
+ ## 版本与发布
179
+
180
+ 正式版本使用 SemVer。Git tag 固定为 `vX.Y.Z`,去掉 `v` 后必须与以下位置完全一致:
181
+
182
+ - `package.json` 和 `package-lock.json`
183
+ - `fupload/SKILL.md` 的 `metadata.version`
184
+ - `fupload/scripts/fupload_cli/__init__.py` 的 `__version__`
185
+ - `npm/skill-manifest.json` 的包版本与 Skill 版本
186
+
187
+ 当前初版为 `0.0.1`,对应 tag `v0.0.1`。`.github/workflows/publish-npm.yml` 在 pull request、`main` push 和 `v*` tag 上运行 Windows/Linux CI;只有 tag CI 全部通过时才通过 npm Trusted Publishing/OIDC 发布,不使用长期 `NPM_TOKEN`。
188
+
189
+ 发布前在 npm 包设置中绑定 Trusted Publisher:
190
+
191
+ - npm package:`@follenfang/fupload`
192
+ - GitHub organization/user:`Follen`
193
+ - repository:`Fuploader`
194
+ - workflow:`publish-npm.yml`
195
+
196
+ 然后从已推送且干净的 `main` 创建 annotated tag:
197
+
198
+ ```powershell
199
+ npm run check:versions
200
+ npm run check:release -- v0.0.1
201
+ git tag -a v0.0.1 -m "Fuploader 0.0.1"
202
+ git push origin v0.0.1
203
+ ```
204
+
205
+ npm 对首次创建的包可能要求先由账号所有者完成一次 bootstrap 发布;该步骤只用于建立包身份,正式 `latest` 仍由 `v0.0.1` workflow 发布。需要人工处理时,不要把 npm token 写入仓库或 GitHub Secret。
206
+
207
+ ## 说明
208
+
209
+ 官方 `ncc` 通道以已安装版本的 `ncc docs` 和叶子 `--help` 为执行契约;仓库内官方文档是获取时的完整快照。第三方 Python 与 DD 通道依赖桌面客户端和线上接口的当前行为,客户端版本或接口变化后应先运行 session doctor 和只读命令。Fuploader 项目本身不是新手盒子或网易 DD 的官方项目。
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: fupload
3
+ description: Explicit author-publishing workflow for World of Warcraft plugins, configuration shares, and WA/strings on NewBeeBox and NetEase DD. 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, plugins, configurations, or WA.
4
+ metadata:
5
+ version: "0.0.1"
6
+ ---
7
+
8
+ # Fupload
9
+
10
+ Act as the publishing operator. For NewBeeBox, prefer the official `ncc` CLI whenever it is installed unless the user explicitly requests the third-party Python management tool. For DD, and for an explicitly selected third-party NewBeeBox workflow, use the bundled Python CLI. Keep investigation, choices, planning, confirmation, and recovery in the conversation.
11
+
12
+ When installed from npm, interpret `<fupload-cli>` as `fupload`. Only while maintaining the source repository, directly installing this Skill, or when `fupload` is absent from PATH, use `python <skill-root>/scripts/fupload.py`. Keep the user's current project as the working directory in either mode.
13
+
14
+ ## Establish intent
15
+
16
+ When the user has not already stated both dimensions, ask in one short message:
17
+
18
+ 1. Platform: NewBeeBox or DD.
19
+ 2. Resource and action: plugin/configuration/WA, then create/content update/metadata edit/delete.
20
+
21
+ Map natural language consistently:
22
+
23
+ - `create`: create the main record. If first content is a separate platform action, plan it as a following atomic step.
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 is existing-record commercial, association, room/channel, and creation-statement settings; first-publication metadata, categories, media, and version fields are create/update-only.
26
+ - `delete`: delete one main record only. It never deletes an individual version, media object, or associated record.
27
+
28
+ Never offer bulk delete, version-file delete, drafts, guides, messages, or GUI control automation. The DD task-session command may close verified official GUI processes only after the consent flow below.
29
+
30
+ ## Select the execution channel
31
+
32
+ For DD, use the bundled Python CLI.
33
+
34
+ For NewBeeBox, apply this order exactly:
35
+
36
+ 1. If the user explicitly requests the third-party Python management tool, use the bundled Python CLI even when `ncc` is installed.
37
+ 2. Otherwise detect `ncc` locally. On PowerShell use `Get-Command ncc -ErrorAction SilentlyContinue`; on POSIX shells use `command -v ncc`. Do not ask the user to report a fact the environment can show.
38
+ 3. If installed, select official `ncc` by default. Run `ncc -V`, `ncc --help`, `ncc docs`, and `ncc whoami -o json`. Treat the installed `ncc docs` and target leaf `--help` as the execution contract. From `whoami`, retain or report only the creator identity and module permissions needed for the task; redact token names, prefixes, timestamps, and other credential metadata.
39
+ 4. If not installed, ask whether the user wants the official CLI. If yes, check `node -v` is at least 18, help install Node.js when needed, then run `npm i -g @newbeebox/newbeebox-creator-center-cli@latest`. Verify `ncc -V` and `ncc --help`; installation success is not authentication success.
40
+ 5. After installation, direct the user to `https://creator.newbeebox.com/creator-center/cli-token` to create a token and have them run `ncc login` in their own terminal. Resume only after `ncc whoami -o json` verifies the expected creator identity. For automation, accept an `NCC_TOKEN` already present in the Agent process environment without printing it.
41
+ 6. If the user declines official installation, or official docs do not support the target action, explain the exact boundary. Use the Python channel only after the user explicitly chooses the third-party Python management tool.
42
+
43
+ Never switch channels silently. Before changing channels, re-read remote state and present a new plan so the same create, version, or edit cannot be submitted twice.
44
+
45
+ ## Protect credentials
46
+
47
+ Prefer an existing official `ncc login`, then a caller-provided `NCC_TOKEN`, then user-performed local login. Never ask the user to paste a token into the conversation. Never place a real token in `--token`, shell history, command output, JSON, `.env`, `publish/`, `analyze/`, tests, Skill files, references, or Git. Do not inspect or copy the official CLI credential store.
48
+
49
+ If a token is pasted into the conversation, do not repeat or use it. Tell the user to revoke it from the official CLI token page and create a replacement through local login or pre-launch environment injection. Check only whether `NCC_TOKEN` is present, never its length, prefix, hash, or value.
50
+
51
+ ## Locate the Fuploader CLI
52
+
53
+ Run `<fupload-cli> --help` from the user's project. The npm launcher resolves and verifies the installed Skill relative to the package, then invokes its bundled Python implementation; do not change the working directory to the Skill.
54
+
55
+ When the npm command is absent and the source/direct-Skill fallback is required, resolve `<skill-root>` from this `SKILL.md` and use `python <skill-root>/scripts/fupload.py`. On Windows, prefer `py -3` only if `python` is unavailable. Do not use a binary, Go source, repository-relative fallback, browser automation, Computer Use, or direct handcrafted HTTP calls. NewBeeBox's official `ncc` is not this bundled CLI and must not be wrapped as a Python `--input` command.
56
+
57
+ For npm package maintenance, use `fupload update` to update the CLI and every registered managed Skill to `@follenfang/fupload@latest`. For a complete removal, use `fupload uninstall`; it removes every registered Skill that still has a valid Fuploader npm ownership marker, then removes the npm package and command. Do not use direct `npm install -g` or `npm uninstall -g` as the normal Agent-managed update/removal workflow.
58
+
59
+ ## Load only the needed contract
60
+
61
+ - Read [references/workflow.md](references/workflow.md) for every write workflow.
62
+ - For official NewBeeBox, read [references/newbee-official-cli.md](references/newbee-official-cli.md), then run the installed `ncc docs` and exact leaf `--help`. The bundled reference is a complete official snapshot, but runtime docs win when versions differ.
63
+ - Read [references/newbee.md](references/newbee.md) only when the user explicitly selected the third-party Python NewBeeBox channel.
64
+ - Read [references/dd.md](references/dd.md) only for DD.
65
+ - For Python, run the exact leaf command with `--help` before creating its input. Treat help as the executable schema contract.
66
+
67
+ ## Investigate
68
+
69
+ Inspect the user-provided project or current workspace for `.toc`, README, changelog, Git changes, archives, screenshots, logos, and WA material files. Do not modify or package the user's project until that is part of the agreed plan.
70
+
71
+ Use read-only commands from the selected channel to discover the current account's records, target detail, versions, backups, categories, game branches/builds, installation paths, life types, VIP levels, channels, and association candidates. Never ask the user to copy an ID that the selected CLI can query.
72
+
73
+ For official NewBeeBox, use the documented `ncc wow addons list|info|categories|versions`, `wa list|info|categories`, `uipack list|info`, `cloudbackup list|info`, and related commands needed by the action. Use the exact options returned by the installed help. `addons push` compatibility uses explicit build strings or documented `auto`; never substitute a parent branch ID. An empty required option list blocks the write.
74
+
75
+ For third-party Python NewBeeBox, use Creator Center webpage requests and form behavior as the business baseline. Always read `newbee options content-origins`, `subscribe-plans`, and `time-ranges` when the corresponding field is present. Also read plugin categories/builds, WA categories, attachment paths, and each resource's `co-author`/`reference` candidates for those fields. Empty option output blocks the write. Plugin compatibility uses build strings from `game-versions.items[].versions`, never the parent branch `id`. Preserve omitted edit fields; an explicit empty `co_authors` or `references` array replaces and clears that complete relation only after the main record readback succeeds.
76
+
77
+ For DD, use the one active task session to read game types/builds and the resource-specific category tree. Resolve parent/child choices in dependency order, display only children returned for the selected parent, and invalidate all descendants when a parent changes. A plugin primary category is a top-level item and secondary IDs are children of that item. Validate associations as `(act_type,sn)`. A room-only selection has `room_id` with empty `channel_id` and `channel_type`; a channel selection requires both channel fields. Query VIP levels only when anchor VIP is enabled and query room/channel data only when room linkage is enabled; disabled optional features do not depend on those endpoints.
78
+
79
+ For every existing DD update or edit, GET the detail first and rebuild the official full form before preparing the write. If a legacy record lacks a field now required by the official submit validator, such as `creation_statement`, stop before upload and collect that value. When the missing field belongs to a different action allowlist, plan and confirm the prerequisite edit first, read it back, then run the content update. Do not send `null`, create defaults, or guessed values to bypass the missing field.
80
+
81
+ For DD WA create, collect user choices before generating JSON, then let the Python CLI supply only the official create defaults for omitted form values: seven-day share and purchase lifetimes, `need_buy=false`, category `ui_original`, `Interface/Addons`, empty VIP levels, and version `0`. Submit category IDs as strings even when a discovery response represents them numerically. WA create/update versions contain digits only; update must be numerically greater than the current remote value. Every `!WA:2!` create/update/edit is reparsed by the installed official `WowUIInterface.parseWa` chain, including unchanged edit content. Do not carry create defaults into WA update/edit; omitted existing fields preserve their remote value.
82
+
83
+ For official NewBeeBox, use only `ncc whoami -o json` to verify authentication; do not inspect its credential files. For third-party Python NewBeeBox, run `newbee session doctor`; credentials must come from the Windows Known Folder auth-store and all authenticated requests must use fixed official HTTPS origins. For DD, run `dd session doctor`; discovery must accept only an Authenticode-valid executable from an allowed official NetEase publisher. Doctor is local-only and must not start a native login. Do not set endpoint, credential-directory, or DD executable-path environment overrides.
84
+
85
+ ## Own one DD task session
86
+
87
+ Before any DD live GET or write, run `dd session doctor`. If it reports `gui_running=true`, tell the user that continuing will close the listed official DD GUI instances and ask for explicit consent. Without consent, do not run start, do not close a process, and do not issue a native login. After consent, run `dd session start --confirm-close-gui`; when no GUI is running, run `dd session start` without that flag.
88
+
89
+ Keep the returned opaque `session_id` only in task memory and pass it as `--session <id>` to every DD GET, write, readback, status, and delete. Reuse this one session for the complete task, serialize all commands, stop on the first failure, and never start one session per item in a batch. In a `finally` path, always run `dd session stop --session <id>` and require `cleanup_complete=true`; the ten-minute idle timeout is only an abnormal-exit fallback.
90
+
91
+ For every DD configuration, first list backups, select `backup_sn`, then run `dd config backup-get --sn <backup>`. Select a WTF account/server/role before presenting that account's known/unknown WA choices. For a retail configuration, present the safe edit-mode and cooldown selector metadata. Send only stable IDs and returned selectors; never request, display, store, or reconstruct raw backup objects or retail import strings.
92
+
93
+ For a configuration, require a cloud backup already uploaded by the matching desktop client. If none exists, ask the user to upload one in the client and stop before writing.
94
+
95
+ ## Collect every business choice
96
+
97
+ Expose every business field writable through the selected channel. Do not silently accept a webpage preselection or invent a business default. This includes applicable game type/build, categories, origin, format, visibility, review submission, payment, lifetime, price, room/channel, synchronization, membership, associations, backup content, WTF roles, incremental selections, retail UI data, WA material mode, and install path. When official `ncc` does not expose a webpage field or action, state that capability boundary; do not guess a hidden flag or silently switch to Python.
98
+
99
+ Use existing remote values only for omitted fields in `edit` or `update`, where omission means preserve. Show candidates by human name plus ID/SN and relevant status. Ask only choices that cannot be determined from the user's explicit request, local artifacts, or remote reads. For DD, do not draft the executable JSON incrementally: close the full dependency graph first, then generate one final JSON containing parent fields and stable child IDs/selectors. Python repeats the live GETs before upload or mutation and rejects missing or cross-parent selections; detail is authoritative when DD list and detail timestamps differ. When an existing plugin or WA has `assign_user_sn`, only public scope is selectable and the final rebuilt form must remain public.
100
+
101
+ ## Prepare and confirm
102
+
103
+ Create a durable release directory under the target project's root, never under or beside the installed Skill. Use `publish/<YYYYMMDD-HHmmss>-<platform>-<resource>-<action>/`; if that name already exists, append `-2`, `-3`, and so on instead of reusing it. Put every atomic step in a versioned JSON file ordered as `01-<action>.json`, `02-<action>.json`, and so on. A retry or readback for the same plan reuses its directory; a new independent publishing plan gets a new directory. Use JSON, not YAML. Keep the directory after execution as the target project's publishing record, and do not change its ignore rules unless the user asks.
104
+
105
+ For Python, these are executable `--input` documents using the leaf schema. For official `ncc`, these are redacted plan records containing the channel, working directory, argument vector, non-secret business inputs, local file references, and expected readback; `ncc` does not consume them. Never store a token, raw WA string, raw configuration content, or signed URL in a plan record. Use `@file` or local path references for content. Run `--dry-run` where the selected leaf documents it, especially `ncc wow addons push --dry-run`; do not invent dry-run support for other official commands.
106
+
107
+ Before the first write, present one complete human-readable plan containing:
108
+
109
+ - platform, account-visible target name and ID/SN;
110
+ - ordered atomic commands;
111
+ - every changed, preserved, and explicitly cleared field;
112
+ - archive/media/material files and versions;
113
+ - backup and all content selections;
114
+ - commercial, channel, association, visibility, and review effects;
115
+ - possible retained object or uploaded media if a later step fails.
116
+
117
+ Obtain one explicit confirmation for that exact plan. If the plan changes materially, confirm the changed plan once.
118
+
119
+ For a Python delete, first run the resource `get` command, show the exact name and ID/SN, and obtain confirmation for that single record. NewBeeBox delete input contains only the schema, `id`, and `confirm: "DELETE"`; DD delete input contains only the schema, `sn`, and `confirm_delete: true`. Do not reuse confirmation for another record and do not retry an uncertain delete. Official `ncc` currently documents plugin, WA, and configuration main-record deletion as web-only; state that boundary and wait for an explicit third-party selection before offering the Python delete command.
120
+
121
+ ## Execute and verify
122
+
123
+ Run writes serially. For official `ncc`, always request `-o json`, parse stdout as JSON, and treat stderr only as progress diagnostics. For Python, parse its stable JSON output. Never scrape human text. After each successful step, immediately run the corresponding info/get/list/versions/history command and compare the intended fields in the same DD session when applicable. DD performs a bounded GET-only readback poll and never resends a mutation during verification. For DD plugin update, use a nonempty `/addon/addon_versions` result only as a pre-upload duplicate guard; the matching author-list item's `latest_version` is the primary success confirmation and `detail_v2` is supplementary. Plugin edit also uses the same-SN author projection when detail remains stale. An empty history is diagnostic-only. Treat “submitted for review” and “under review” as distinct from “approved” or “publicly visible.”
124
+
125
+ In the third-party Python NewBeeBox channel, public plugin publication is three atomic writes: create privately, upload and verify the first version, then edit to public with explicit review intent. Never send a public `share_state` during Python create. In the official channel, follow the installed `ncc docs` sequence for create, init, push, and visibility instead of applying Python wire rules.
126
+
127
+ Stop on the first failure. Report completed steps, retained IDs/SNs or media references, the redacted failure stage, whether verification is required, and the smallest safe retry. Explicit DD HTTP/business rejection has `verification_required=false`; interrupted PUT/mutation or failed readback after an accepted write has `verification_required=true`. Native DD failures may include a bounded message, HTTP status, native business code, field validation hints, and `details.log_path`. When `log_path` is present, read only the referenced Fuploader JSONL record under the DD version directory; do not inspect DD's other logs or any credential files. The Fuploader record contains sanitized request and response JSON/body plus byte counts and truncation flags, with signed URLs, cookies, JWTs, credentials, client identifiers, signatures, and tokens recursively redacted. Read back after an uncertain write before resending it. Official NewBeeBox exit codes are `0` success, `1` business error, `2` authentication failure, and `3` network error. Never loop on `quota_exceeded`.
128
+
129
+ Never print or persist tokens, cookies, JWTs, signed URLs, DD `clientNo`, raw WA strings, or raw configuration backup contents.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Fupload"
3
+ short_description: "整理并发布新手盒子与网易DD的插件、配置分享及WA字符串"
4
+ default_prompt: "使用 $fupload 检查当前项目和远端状态;新手盒子优先检测并使用官方 ncc,显式要求第三方时使用 fupload CLI;整理全部业务选项并在确认后完成发布或更新。"
@@ -0,0 +1,5 @@
1
+ {
2
+ "schema": "fupload.v1.dd.config.delete",
3
+ "sn": "CONFIG_SN",
4
+ "confirm_delete": true
5
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "schema": "fupload.v1.dd.config.update",
3
+ "share_sn": "config-share-sn",
4
+ "backup_sn": "backup-sn",
5
+ "update_desc": "Backup content update",
6
+ "known_addon_ids": [],
7
+ "known_addon_update_ids": [],
8
+ "unknown_addon_ids": [],
9
+ "unknown_addon_update_ids": [],
10
+ "wtf_role_ids": [],
11
+ "material_names": [],
12
+ "material_update_names": [],
13
+ "font_names": [],
14
+ "font_update_names": [],
15
+ "known_wa_ids": [],
16
+ "known_wa_update_ids": [],
17
+ "unknown_wa_ids": [],
18
+ "unknown_wa_update_ids": [],
19
+ "retail_ui_config": {
20
+ "edit_mode_selectors": ["em_from_backup_get"],
21
+ "default_edit_mode_selector": "em_from_backup_get",
22
+ "cool_down_selectors": ["cd_from_backup_get"],
23
+ "enable_dd_setup_wizard": true
24
+ }
25
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "schema": "fupload.v1.dd.plugin.delete",
3
+ "sn": "PLUGIN_SN",
4
+ "confirm_delete": true
5
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema": "fupload.v1.dd.plugin.update",
3
+ "sn": "plugin-share-sn",
4
+ "game_versions": ["100101"],
5
+ "file": "./addon.zip",
6
+ "release_type": 1,
7
+ "version": "2.0.0",
8
+ "update_desc": "Version notes"
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "schema": "fupload.v1.dd.wa.delete",
3
+ "sn": "WA_SN",
4
+ "confirm_delete": true
5
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema": "fupload.v1.dd.wa.edit",
3
+ "sn": "wa-share-sn",
4
+ "brief_desc": "Updated summary",
5
+ "need_buy": false,
6
+ "jump_room": false,
7
+ "with_associate": false,
8
+ "need_anchor_vip": false
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "schema": "fupload.v1.newbee.config.delete",
3
+ "id": 123,
4
+ "confirm": "DELETE"
5
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "schema": "fupload.v1.newbee.config.update",
3
+ "id": 12345,
4
+ "cloud_id": 67890,
5
+ "linked_mods": [],
6
+ "ignored_unknown_mods": [],
7
+ "ignored_materials": [],
8
+ "ignored_fronts": [],
9
+ "roleid": "selected-role-id"
10
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "schema": "fupload.v1.newbee.plugin.create",
3
+ "name": "Example AddOn",
4
+ "mod_categories": [101],
5
+ "content_origin": 1,
6
+ "content_format": 2,
7
+ "intro": "Short introduction",
8
+ "description": "Full description",
9
+ "logo_file": "./logo.png",
10
+ "screenshot_files": ["./screenshot.png"],
11
+ "public": false,
12
+ "subscribe_plan_level": 0,
13
+ "link_to_channel": false
14
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "schema": "fupload.v1.newbee.plugin.delete",
3
+ "id": 123,
4
+ "confirm": "DELETE"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "schema": "fupload.v1.newbee.wa.delete",
3
+ "id": 123,
4
+ "confirm": "DELETE"
5
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "schema": "fupload.v1.newbee.wa.update",
3
+ "id": 12345,
4
+ "wa_str": "<WA string>",
5
+ "wa_str_titles": [],
6
+ "wa_log": "Version notes",
7
+ "link_to_channel": false
8
+ }
@@ -0,0 +1,105 @@
1
+ # NetEase DD field and workflow reference
2
+
3
+ ## Task session
4
+
5
+ Run `dd session doctor` first. Doctor only discovers the installation, verifies the Authenticode publisher, reports official DD processes and reads local broker state; it does not login. When `gui_running=true`, explain that the official DD GUI must close and obtain explicit user consent. Only then run `dd session start --confirm-close-gui`. When `gui_running=false`, run `dd session start` without the confirmation flag.
6
+
7
+ `start` closes only identity- and signature-verified official GUI processes, starts one task broker and returns an opaque `session_id`. Pass that value through `--session` to every DD read and write. All operations are serialized through one native login. Run `dd session status --session <id>` for a local status check and always run `dd session stop --session <id>` in `finally`; successful stop reports `cleanup_complete=true`. The ten-minute idle timeout is only a crash fallback.
8
+
9
+ Do not request or expose token, Cookie, JWT, credential database, signed URL, `clientNo`, raw WA content, or raw backup objects. The device state remains under Windows Known Folder Roaming AppData at `CCVoiceHub/Fupload/sidecar-device.json`.
10
+
11
+ ## GET before one final JSON
12
+
13
+ DD GUI fields are not a flat form. Resolve these dependencies before generating the write JSON:
14
+
15
+ - `game_type` -> `game_versions`, `associated_acts`, WA `category_ids`;
16
+ - `primary_category_id` -> `second_category_ids`;
17
+ - `scope` -> `share_code_life_type`, anchor-VIP availability, `sync_room`;
18
+ - outer free/paid state -> `need_buy`, `need_anchor_vip` -> `price_fen`, `buy_life_type`, `vip_levels`;
19
+ - `jump_room` -> `room_id` -> `channel_id` and `channel_type`;
20
+ - `backup_sn` -> backup detail -> WTF account/server/role -> account-scoped `known_wa_ids` and `unknown_wa_ids`;
21
+ - retail backup -> `retail_ui_config` selectors;
22
+ - `with_file` -> WA `file` and `file_install_path`.
23
+
24
+ Choose a parent, GET and display only that parent's child choices, then continue. Changing a parent invalidates all descendants. Generate one final JSON only after the graph closes. Store parent fields and stable IDs/opaque selectors, never copied backend objects or display names.
25
+
26
+ The Python provider repeats every live GET in the same session before any upload or mutation. A missing, duplicate, or cross-parent selection fails at its exact JSON path with `verification_required=false`. DD detail is authoritative for an existing object's form and ownership; author-list timestamps are not comparable freshness gates.
27
+
28
+ Author-owned plugin/config/WA lists and association candidates traverse the real DD pagination contract instead of assuming the first 100 rows contain every selected SN. Repeated pages or the bounded page limit are platform-data failures and stop the write before upload.
29
+
30
+ The installed author-list response may expose its total as top-level `count`; treat that as the pagination bound. Fetch anchor-VIP levels only when `need_anchor_vip=true`, and fetch CC room/channel data only when `jump_room=true`. Disabled optional features must not fail because an unrelated dependency endpoint is empty or unavailable.
31
+
32
+ ## Shared fields
33
+
34
+ All three create/edit form models use `scope`, `share_code_life_type`, `need_buy`, `price_fen`, `buy_life_type`, `jump_room`, `room_id`, `channel_id`, `channel_type`, `sync_room`, `creation_statement`, `with_associate`, `associated_acts`, `need_anchor_vip`, and `vip_levels`.
35
+
36
+ - `scope` is `public` or `private`. Private requires `share_code_life_type`, clears anchor VIP and room sync. Plugin/WA public force `share_code_life_type=forever`; config public omits it.
37
+ - `need_buy=true` requires `buy_life_type`; `price_fen` may be `0` or `10..20000` fen because the official submit validation explicitly accepts zero. A free create resolves to zero, while an existing paid form may retain a hidden historical price when `need_buy` is turned off but the outer paid mode remains active through anchor VIP.
38
+ - The outer free/paid selector is locked after an SN exists. It is derived from `need_buy || need_anchor_vip` and is not a wire field. Existing paid content may still adjust payment methods and their price/lifetime/VIP children while remaining paid.
39
+ - `jump_room=true` requires `room_id`. `channel_id` and `channel_type` are both empty for a room-only link or both present for one live child channel.
40
+ - `with_associate=true` requires nonempty `associated_acts`; each item is exactly `{sn,act_type}` with `act_type` `addon`, `share`, or `wa`.
41
+ - Remote detail/list projections may enrich associated items with display-only fields such as names, covers, or timestamps. Python validates the live reference, then strips every item back to exactly `{sn,act_type}` before any create/modify mutation.
42
+ - `need_anchor_vip=true` requires public scope. `vip_levels` must contain only live values when supplied, but the official submit validation accepts an empty array. Turning only `need_anchor_vip` off preserves the existing level array; switching to private scope or switching the outer mode to free clears it.
43
+ - `creation_statement` is `original`, `chinesize`, `renovate`, or `second`.
44
+
45
+ Omission on edit preserves the remote value. Explicit false follows the field-specific official behavior: room and association children are cleared, while anchor VIP levels remain unless private scope or the outer free mode clears them.
46
+
47
+ ## Plugin
48
+
49
+ `plugin create` fields: `game_type`, `scope`, `addon_type`, `name`, `description`, `logo`/`logo_file`, `detail_imgs`/`detail_img_files`, `primary_category_id`, `second_category_ids`, `html_desc`, `game_versions`, `detail_url`/`file`, `release_type`, `version`, `update_desc`, and all shared fields. Name, description, and version are at most 80 characters; update description is at most 1000; detail images are at most 8.
50
+
51
+ 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.
52
+
53
+ `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 only accepts the official existing-record commercial and association controls (`scope`, payment/lifetime/VIP fields, room/channel linkage, `creation_statement`, and associated content). First-publication metadata (`addon_type`, `name`, `description`, logo, detail images, categories, and `html_desc`) and version fields are not edit fields; in particular, sending `description` to `/addon/modify` can be accepted while leaving the remote value unchanged, so the CLI rejects it instead of reporting a false success.
54
+
55
+ 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.
56
+
57
+ When rebuilding a plugin update/edit form, preserve field presence from the current DD record. A field absent from both official projections must remain absent; do not synthesize JSON `null`, because the official web `pick -> JSON.stringify` path omits absent properties and DD can reject synthetic nulls with HTTP 422. The official detail dialog is opened from the matching author-list item and takes `detail_url`, `release_type`, and `version` from that item's `latest_version`; `detail_v2` supplies the stable detail fields and top-level `game_versions`. Python therefore uses the same-SN author item only to fill null/missing latest-version placeholders, never to overwrite stable metadata or top-level builds.
58
+
59
+ Before `plugin update`, traverse `/addon/addon_versions` pages as an optional duplicate guard. If it returns version rows, reject any candidate version already present anywhere in that history before upload; if it is empty or unavailable, retain the current-version check and continue. After update, confirm the submitted version fields from the matching item in the author plugin list, whose `latest_version` is the official update projection. `detail_v2` is supplementary. An empty history never marks a successful private-plugin update as failed and never triggers replay.
60
+
61
+ Read `plugin categories`, choose `primary_category_id`, then choose only returned `second_category_ids`. Read `plugin game-versions --game-type <id>` and use its stable values. The package accepts `.zip` only. Authorization always uses `file_type=a19-ui-res`, `business_id=addon`, fixed `file_name=addon.zip`, and `mime_type=application/x-zip-compressed`. Plugin image authorization uses `a19-ui-media/img` with an explicit empty wire file name.
62
+
63
+ ## Configuration share
64
+
65
+ `config create` fields: `backup_sn`, `scope`, `title`, `brief_desc`, `desc`, `update_desc`, `display_imgs`/`display_img_files`, `known_addon_ids`, `unknown_addon_ids`, `wtf_role_ids`, `material_names`, `font_names`, `known_wa_ids`, `unknown_wa_ids`, optional `retail_ui_config`, all incremental arrays, and shared fields. The incremental arrays are `known_addon_update_ids`, `unknown_addon_update_ids`, `material_update_names`, `font_update_names`, `known_wa_update_ids`, and `unknown_wa_update_ids`.
66
+
67
+ Title is at most 40 characters, brief description 50, update description 1000, and display images 8. `wtf_role_ids` contains at most one opaque selector returned by `config backup-get`.
68
+
69
+ Run `config backups`, select `backup_sn`, then run `config backup-get --sn <backup>`. Choose content references only from that response. After choosing one WTF role, filter known/unknown WA by the selected role's account. Switching backup requires complete reselection. Switching the WTF account clears both WA groups. Unknown WA internal IDs are restored by Python from `extra.wa_account_info[account]`; they never appear in input JSON.
70
+
71
+ Every wire content group is rebuilt from the latest backup. Each `inner_version` map covers every source item; a new entry is 1, an existing value is preserved, and only explicitly listed existing update entries increment.
72
+
73
+ An incremental array may be submitted without repeating its unchanged selection array; Python applies it to the current selected items from the fresh detail. Every incremental marker must reference an item that remains selected. A stale or unselected marker is rejected at its exact `*_update_ids` or `*_update_names` path before upload or mutation.
74
+
75
+ Every path in `display_img_files` must exist as a regular local file and is rejected at its indexed JSON path before upload when absent.
76
+
77
+ For retail, `retail_ui_config` accepts `edit_mode_selectors`, `default_edit_mode_selector`, `cool_down_selectors`, and `enable_dd_setup_wizard`. Up to five edit modes are allowed and one selected mode is default. Only one cooldown per `spec_tag` is allowed. Selectors are bound to one backup; raw `import_string` and raw edit/cooldown objects are read-only and never emitted.
78
+
79
+ `config update` requires `share_sn`, `backup_sn`, and `update_desc`; it changes backup content and increments. `config edit` requires `share_sn` and changes metadata, images, and allowed shared fields only. Config images omit `file_name` from upload authorization.
80
+
81
+ ## WA/string
82
+
83
+ `wa create` fields: `game_type`, `scope`, `name`, `game_version`, `brief_desc`, `display_imgs`/`display_img_files`, `category_ids`, `content`, `desc`, `update_desc`, `version`, `with_file`, optional local `file`, `file_install_path`, and all shared fields. Name is at most 40 characters, brief description 50, update description 1000, numeric version length 80, categories 5, and images 8.
84
+
85
+ Read `wa categories --game-type <id>` and use only that game type's category IDs. `game_type` and outer free/paid mode are locked after creation. `wa update` requires `sn`, `content`, `update_desc`, `version`, and `with_file`; new version input contains digits only and must be numerically greater than the current value. Historical current values are compared with the official JavaScript `Number(current)` behavior, so a legacy value such as `1.2` may advance to `2`. `wa edit` requires `sn` and accepts metadata, categories, images, and allowed shared fields. Assigned WA records also validate the final rebuilt scope as public.
86
+
87
+ The official create builder supplies defaults that are required even when the user did not make a business choice: `share_code_life_type="seven_day"`, `need_buy=false`, `buy_life_type="seven_day"`, `category_ids=["ui_original"]`, `file_install_path="Interface/Addons"`, `vip_levels=[]`, and `version="0"`. The CLI applies these only to create, then applies the user's explicit values and normalizes every submitted category ID to its string wire form. Update and edit preserve remote values for omitted fields rather than reapplying create defaults.
88
+
89
+ Every submit whose content begins `!WA:2!` is reparsed by the installed official chain `WowUIInterface.parseWa({"waStr": content}) -> WaParser.parseWa(content)`, including unchanged content on edit. Native `JsResult.toJson()` output may use success code `0` or `200`; IDs come from nested `result.uid` and `result.id`. Internal `parse_wa_uid` and `parse_wa_id` are read-only wire fields and are not accepted in JSON. Non-WA2 clears them.
90
+
91
+ When `with_file=true`, create requires a local `.zip` `file` and nonempty `file_install_path`; update may preserve an existing material when `file` is omitted. Authorization uses `a19-ui-res/wa`, fixed `file_name=wa_materials.zip`, fixed ZIP MIME, and a 50 MiB local limit plus server `maxSize`. `with_file=false` follows the official builder and preserves existing internal `file_path` and install path rather than clearing them. WA images omit upload `file_name`.
92
+
93
+ ## Delete
94
+
95
+ `plugin delete`, `config delete`, and `wa delete` each accept one nonempty `sn` and `confirm_delete`=true. Python GETs the target and ownership state before `/addon/delete`, `/share/delete`, or `/wa/delete`, then verifies absence through list/get readback. An uncertain delete is never automatically retried.
96
+
97
+ ## Errors and readback
98
+
99
+ Stages are `session`, `dependency_get`, `upload_authorize`, `object_put`, `mutation`, `readback`, and `native_parser`. Explicit HTTP/business failures and all pre-mutation validation failures have `verification_required=false`. In particular, HTTP 4xx responses such as 422 are confirmed server rejections, not uncertain writes; the CLI reports `http_status` and a bounded, secret-free validation summary when the native response exposes one. PUT/mutation connection uncertainty and accepted-write readback uncertainty have `verification_required=true`; GET first and do not replay the write. Native failures retain a bounded exception message and the native `code`/`error_code` when present, with signed-URL query credentials, signatures, and tokens redacted before they leave the sidecar.
100
+
101
+ Each DD native/API failure appends one ASCII JSON line to `<DD version directory>/Fupload/logs/dd-errors-YYYYMMDD.jsonl`. The record includes HTTP status, native business code including falsy code `0`, stage, endpoint, request field names, the sanitized request JSON/body, validation hints, and the sanitized response JSON/body. Upload authorization records `/file/upload`; object PUT records `object-store-put` instead of the signed URL. Token, JWT, Cookie, authorization, credential, client identifier, device proof, signature, and signed upload URL fields are redacted recursively, including truncated non-JSON text. Request and response bodies are independently bounded to 1 MiB by UTF-8 bytes and record their original size and truncation state. A log write failure is reported as `log_write_error` without replacing the original DD error.
102
+
103
+ After an accepted mutation, detail readback uses a short bounded GET-only poll and never resends the mutation. Plugin edit/version confirmation also checks the matching author-list projection because the official UI opens the modify dialog from that item while `detail_v2` can remain stale or contain null latest-version placeholders. Configuration readback compares its official integer `need_buy` wire value with the boolean detail projection through a resource-specific conversion.
104
+
105
+ The parent process and native sidecar exchange ASCII-only JSONL, and Fuploader's final JSON output follows the same rule. Non-ASCII request, response, and output text is represented with JSON Unicode escapes so Chinese titles, descriptions, announcements, and URLs do not depend on either Windows process code page. JSON consumers recover original UTF-8 strings through normal parsing.