@leonxin/meetgames 0.1.11 → 0.1.13

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 (179) hide show
  1. package/README.md +9 -9
  2. package/dist/cache.d.ts +44 -0
  3. package/dist/cache.d.ts.map +1 -0
  4. package/dist/cache.js +101 -0
  5. package/dist/cache.js.map +1 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +43 -37
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config/meetSdkDefaultConfig.d.ts +1 -1
  10. package/dist/config/meetSdkDefaultConfig.d.ts.map +1 -1
  11. package/dist/config/meetSdkDefaultConfig.js +4 -3
  12. package/dist/config/meetSdkDefaultConfig.js.map +1 -1
  13. package/dist/config/meetSdkIosConfig.d.ts.map +1 -1
  14. package/dist/config/meetSdkIosConfig.js +3 -1
  15. package/dist/config/meetSdkIosConfig.js.map +1 -1
  16. package/dist/config/meetSdkRemoteConfig.d.ts +1 -0
  17. package/dist/config/meetSdkRemoteConfig.d.ts.map +1 -1
  18. package/dist/config/meetSdkRemoteConfig.js +4 -1
  19. package/dist/config/meetSdkRemoteConfig.js.map +1 -1
  20. package/dist/contracts/types.d.ts +11 -0
  21. package/dist/contracts/types.d.ts.map +1 -1
  22. package/dist/core/doctor.js +7 -7
  23. package/dist/core/doctor.js.map +1 -1
  24. package/dist/core/pipeline.d.ts.map +1 -1
  25. package/dist/core/pipeline.js +3 -0
  26. package/dist/core/pipeline.js.map +1 -1
  27. package/dist/core/previewPatches.d.ts +1 -1
  28. package/dist/core/previewPatches.js +2 -2
  29. package/dist/core/previewPatches.js.map +1 -1
  30. package/dist/core/reporter.d.ts.map +1 -1
  31. package/dist/core/reporter.js +4 -0
  32. package/dist/core/reporter.js.map +1 -1
  33. package/dist/core/workspace.d.ts.map +1 -1
  34. package/dist/core/workspace.js +2 -0
  35. package/dist/core/workspace.js.map +1 -1
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/ios/channelConfig.js +1 -1
  41. package/dist/ios/channelConfig.js.map +1 -1
  42. package/dist/ios/codeUtils.d.ts +1 -0
  43. package/dist/ios/codeUtils.d.ts.map +1 -1
  44. package/dist/ios/codeUtils.js +3 -0
  45. package/dist/ios/codeUtils.js.map +1 -1
  46. package/dist/ios/integrate.d.ts.map +1 -1
  47. package/dist/ios/integrate.js +97 -23
  48. package/dist/ios/integrate.js.map +1 -1
  49. package/dist/ios/pbxprojEditor.d.ts.map +1 -1
  50. package/dist/ios/pbxprojEditor.js +123 -6
  51. package/dist/ios/pbxprojEditor.js.map +1 -1
  52. package/dist/ios/sdkBundle.d.ts +1 -6
  53. package/dist/ios/sdkBundle.d.ts.map +1 -1
  54. package/dist/ios/sdkBundle.js +44 -16
  55. package/dist/ios/sdkBundle.js.map +1 -1
  56. package/dist/mcp/server.d.ts.map +1 -1
  57. package/dist/mcp/server.js +12 -6
  58. package/dist/mcp/server.js.map +1 -1
  59. package/dist/mcp/service.d.ts +3 -0
  60. package/dist/mcp/service.d.ts.map +1 -1
  61. package/dist/mcp/service.js +31 -8
  62. package/dist/mcp/service.js.map +1 -1
  63. package/dist/ops/handlers.d.ts.map +1 -1
  64. package/dist/ops/handlers.js +24 -19
  65. package/dist/ops/handlers.js.map +1 -1
  66. package/dist/remote/sdkHomeDownload.d.ts +1 -1
  67. package/dist/remote/sdkHomeDownload.d.ts.map +1 -1
  68. package/dist/remote/sdkHomeDownload.js +27 -6
  69. package/dist/remote/sdkHomeDownload.js.map +1 -1
  70. package/docs/API.md +16 -16
  71. package/docs/CLI.md +21 -22
  72. package/docs/INTEGRATION.md +30 -11
  73. package/docs/MCP.md +1 -1
  74. package/docs/README.md +0 -1
  75. package/docs/archive/api/downloadSDKConfig.md +2 -2
  76. package/docs/archive/api/getChannelConfig-meetgames.md +1 -1
  77. package/docs/archive/product//351/234/200/346/261/202/346/226/207/346/241/243.md +1 -1
  78. package/package.json +2 -5
  79. package/recipes/android-default.yaml +0 -5
  80. package/recipes/integrate-default.yaml +0 -5
  81. package/src/cache.ts +164 -0
  82. package/src/cli.ts +46 -38
  83. package/src/config/meetSdkDefaultConfig.ts +4 -3
  84. package/src/config/meetSdkIosConfig.ts +3 -1
  85. package/src/config/meetSdkRemoteConfig.ts +5 -1
  86. package/src/contracts/types.ts +11 -0
  87. package/src/core/doctor.ts +7 -7
  88. package/src/core/pipeline.ts +3 -0
  89. package/src/core/previewPatches.ts +2 -2
  90. package/src/core/reporter.ts +3 -0
  91. package/src/core/workspace.ts +2 -0
  92. package/src/index.ts +7 -0
  93. package/src/ios/channelConfig.ts +1 -1
  94. package/src/ios/codeUtils.ts +4 -0
  95. package/src/ios/integrate.ts +110 -27
  96. package/src/ios/pbxprojEditor.ts +121 -4
  97. package/src/ios/sdkBundle.ts +41 -18
  98. package/src/mcp/server.ts +12 -6
  99. package/src/mcp/service.ts +39 -8
  100. package/src/ops/handlers.ts +23 -19
  101. package/src/remote/sdkHomeDownload.ts +28 -7
  102. package/tests/doctor.test.ts +4 -2
  103. package/tests/{test-projects-hosts.test.ts → fixtures-hosts.test.ts} +2 -2
  104. package/tests/ios.sdkBundle.test.ts +10 -5
  105. package/tests/mcp.e2e.ts +2 -5
  106. package/tests/meetSdkRemoteConfig.test.ts +25 -0
  107. package/tests/pipeline.android.test.ts +1 -2
  108. package/tests/pipeline.ios.test.ts +133 -20
  109. package/tests/pipeline.preview.patch.test.ts +2 -2
  110. package/tests/sdkVersionConfig.test.ts +3 -2
  111. package/dist/aab-converter/aab-entry.d.ts +0 -3
  112. package/dist/aab-converter/aab-entry.d.ts.map +0 -1
  113. package/dist/aab-converter/aab-entry.js +0 -49
  114. package/dist/aab-converter/aab-entry.js.map +0 -1
  115. package/dist/aab-converter/apksExtractor.d.ts +0 -2
  116. package/dist/aab-converter/apksExtractor.d.ts.map +0 -1
  117. package/dist/aab-converter/apksExtractor.js +0 -108
  118. package/dist/aab-converter/apksExtractor.js.map +0 -1
  119. package/dist/aab-converter/bundletoolRunner.d.ts +0 -15
  120. package/dist/aab-converter/bundletoolRunner.d.ts.map +0 -1
  121. package/dist/aab-converter/bundletoolRunner.js +0 -46
  122. package/dist/aab-converter/bundletoolRunner.js.map +0 -1
  123. package/dist/aab-converter/cliArgs.d.ts +0 -27
  124. package/dist/aab-converter/cliArgs.d.ts.map +0 -1
  125. package/dist/aab-converter/cliArgs.js +0 -170
  126. package/dist/aab-converter/cliArgs.js.map +0 -1
  127. package/dist/aab-converter/convertAabToApk.d.ts +0 -7
  128. package/dist/aab-converter/convertAabToApk.d.ts.map +0 -1
  129. package/dist/aab-converter/convertAabToApk.js +0 -69
  130. package/dist/aab-converter/convertAabToApk.js.map +0 -1
  131. package/dist/aab-converter/resourcePaths.d.ts +0 -4
  132. package/dist/aab-converter/resourcePaths.d.ts.map +0 -1
  133. package/dist/aab-converter/resourcePaths.js +0 -42
  134. package/dist/aab-converter/resourcePaths.js.map +0 -1
  135. package/dist/aab-converter/signingOptions.d.ts +0 -9
  136. package/dist/aab-converter/signingOptions.d.ts.map +0 -1
  137. package/dist/aab-converter/signingOptions.js +0 -21
  138. package/dist/aab-converter/signingOptions.js.map +0 -1
  139. package/dist/aab-converter/types.d.ts +0 -24
  140. package/dist/aab-converter/types.d.ts.map +0 -1
  141. package/dist/aab-converter/types.js +0 -2
  142. package/dist/aab-converter/types.js.map +0 -1
  143. package/dist/shared/fileUtils.d.ts +0 -5
  144. package/dist/shared/fileUtils.d.ts.map +0 -1
  145. package/dist/shared/fileUtils.js +0 -35
  146. package/dist/shared/fileUtils.js.map +0 -1
  147. package/dist/shared/logger.d.ts +0 -10
  148. package/dist/shared/logger.d.ts.map +0 -1
  149. package/dist/shared/logger.js +0 -37
  150. package/dist/shared/logger.js.map +0 -1
  151. package/dist/shared/pathUtils.d.ts +0 -4
  152. package/dist/shared/pathUtils.d.ts.map +0 -1
  153. package/dist/shared/pathUtils.js +0 -22
  154. package/dist/shared/pathUtils.js.map +0 -1
  155. package/dist/shared/processRunner.d.ts +0 -12
  156. package/dist/shared/processRunner.d.ts.map +0 -1
  157. package/dist/shared/processRunner.js +0 -31
  158. package/dist/shared/processRunner.js.map +0 -1
  159. package/docs/AAB_CONVERTER_CLI_PLAN.md +0 -392
  160. package/logs/convert-20260622-155037.log +0 -5
  161. package/logs/convert-20260622-155226.log +0 -6
  162. package/scripts/package-aab-cli-win.mjs +0 -193
  163. package/src/aab-converter/aab-entry.ts +0 -48
  164. package/src/aab-converter/apksExtractor.ts +0 -119
  165. package/src/aab-converter/bundletoolRunner.ts +0 -63
  166. package/src/aab-converter/cliArgs.ts +0 -194
  167. package/src/aab-converter/convertAabToApk.ts +0 -81
  168. package/src/aab-converter/resourcePaths.ts +0 -43
  169. package/src/aab-converter/signingOptions.ts +0 -29
  170. package/src/aab-converter/types.ts +0 -26
  171. package/src/shared/fileUtils.ts +0 -41
  172. package/src/shared/logger.ts +0 -49
  173. package/src/shared/pathUtils.ts +0 -24
  174. package/src/shared/processRunner.ts +0 -43
  175. package/test-projects/README.md +0 -51
  176. package/test-projects/_preview/pipeline.patch +0 -281
  177. package/tests/aab-converter.test.ts +0 -213
  178. /package/{meetsdk-android.json → config/meetsdk-android.json} +0 -0
  179. /package/{meetsdk-ios.json → config/meetsdk-ios.json} +0 -0
@@ -1,392 +0,0 @@
1
- # AAB 转 APK 命令行工具方案
2
-
3
- 本文记录 `meet-sdk-tool` 工具集新增 AAB 转 APK 工具的第一版方案。当前结论以本文为准:先做纯命令行绿色版 zip,不做桌面 UI,不做安装包,不内置 ADB。
4
-
5
- ## 目标
6
-
7
- 面向运营人员提供一个 Windows 可直接解压使用的命令行工具,把 Android App Bundle (`.aab`) 转换为可以安装到手机的通用 APK (`universal.apk`)。
8
-
9
- 第一版只覆盖最小闭环:
10
-
11
- - 输入 `.aab`
12
- - 输入签名文件路径和签名参数
13
- - 调用 Google 官方 `bundletool`
14
- - 生成 `.apks`
15
- - 从 `.apks` 中提取 `universal.apk`
16
- - 输出最终 `.apk`
17
-
18
- ## 非目标
19
-
20
- 第一版暂不实现:
21
-
22
- - Electron 桌面 UI
23
- - Windows 安装包 `.exe installer`
24
- - ADB 检测或一键安装手机
25
- - 自动更新
26
- - 远程签名服务
27
- - Android / iOS SDK 接入流程复用
28
- - 接入现有 `recipe / pipeline / integrate` 机制
29
-
30
- ## 技术选型
31
-
32
- 采用当前仓库已有的 TypeScript / Node 技术栈:
33
-
34
- | 能力 | 选型 | 说明 |
35
- |---|---|---|
36
- | CLI 逻辑 | TypeScript + Node.js | 与现有 `meet-sdk-tool` 技术栈一致 |
37
- | AAB 转 APK | `bundletool.jar` | Google 官方工具 |
38
- | Java 运行环境 | Windows x64 JRE | 绿色版内置,运营无需安装 Java |
39
- | Windows 运行环境 | Windows x64 `node.exe` | 绿色版内置,运营无需安装 Node |
40
- | 打包方式 | macOS 组装 zip | zip 内放 Windows 运行时和资源 |
41
- | 验收方式 | Windows 实机验收 | macOS 只负责生成 zip,不替代 Windows 验证 |
42
-
43
- 已确认的资源策略:
44
-
45
- - `bundletool.jar` 使用官方 GitHub Releases 最新稳定版;实现时从 `https://github.com/google/bundletool/releases/latest` 确认并固定版本。
46
- - 截至 2026-06-22,官方 latest release 为 `1.18.3`。
47
- - Windows Node 运行时当前固定为 Node.js `v24.17.0`。
48
- - Windows JRE 当前固定为 Eclipse Temurin JRE `21.0.11+10`。
49
- - 允许在仓库或 release 资源目录中保存 Windows x64 `node.exe`、Windows x64 JRE、`bundletool.jar`。
50
- - 第一版强制传入正式签名参数,不支持 debug / default 签名模式。
51
-
52
- ## 交付形态
53
-
54
- 最终产物:
55
-
56
- ```text
57
- release/MeetSDKTool-AABConverter-win-x64.zip
58
- ```
59
-
60
- 解压后目录建议:
61
-
62
- ```text
63
- MeetSDKTool-AABConverter-win-x64/
64
- aab2apk.cmd
65
- app/
66
- aab-converter/
67
- aab-entry.js
68
- shared/
69
- runtime/
70
- node/
71
- node.exe
72
- jre/
73
- bin/
74
- java.exe
75
- tools/
76
- bundletool.jar
77
- logs/
78
- output/
79
- ```
80
-
81
- `bundletool.jar` 是跨平台 jar;`node.exe` 和 `java.exe` 必须是 Windows x64 版本,不能放 macOS 版本。
82
-
83
- ## 使用方式
84
-
85
- 推荐通过环境变量传密码,避免密码出现在命令历史和进程参数中:
86
-
87
- ```bat
88
- set KS_PASS=your_keystore_password
89
- set KEY_PASS=your_key_password
90
-
91
- aab2apk.cmd convert ^
92
- --aab "D:\packages\game.aab" ^
93
- --out-dir "D:\output" ^
94
- --ks "D:\keys\release.jks" ^
95
- --ks-key-alias "release" ^
96
- --ks-pass-env KS_PASS ^
97
- --key-pass-env KEY_PASS
98
- ```
99
-
100
- 也可以支持直接传密码,但仅用于临时测试:
101
-
102
- ```bat
103
- aab2apk.cmd convert ^
104
- --aab "D:\packages\game.aab" ^
105
- --out-dir "D:\output" ^
106
- --ks "D:\keys\release.jks" ^
107
- --ks-key-alias "release" ^
108
- --ks-pass "your_keystore_password" ^
109
- --key-pass "your_key_password"
110
- ```
111
-
112
- ## CLI 参数
113
-
114
- 命令:
115
-
116
- ```text
117
- aab2apk.cmd convert --aab <file.aab> --out-dir <dir> [options]
118
- ```
119
-
120
- 参数:
121
-
122
- | 参数 | 必填 | 说明 |
123
- |---|---:|---|
124
- | `--aab` | 是 | AAB 文件路径 |
125
- | `--out-dir` | 是 | APK 输出目录 |
126
- | `--ks` | 是 | keystore / jks 文件路径 |
127
- | `--ks-key-alias` | 是 | keystore 中的 key alias |
128
- | `--ks-pass-env` | 否 | 从指定环境变量读取 keystore 密码,推荐 |
129
- | `--key-pass-env` | 否 | 从指定环境变量读取 key 密码,推荐 |
130
- | `--ks-pass` | 否 | 直接传 keystore 密码,不推荐 |
131
- | `--key-pass` | 否 | 直接传 key 密码,不推荐 |
132
- | `--apk-name` | 否 | 自定义输出 APK 文件名 |
133
- | `--keep-apks` | 否 | 保留中间产物 `.apks` |
134
- | `--no-overwrite` | 否 | 不覆盖已存在的输出 APK |
135
- | `--verbose` | 否 | 输出详细日志 |
136
-
137
- 密码解析规则:
138
-
139
- 1. 优先读取 `--ks-pass-env` / `--key-pass-env` 指定的环境变量。
140
- 2. 如果未传 env 参数,再读取 `--ks-pass` / `--key-pass`。
141
- 3. 如果签名参数不完整,直接报错,不进入 bundletool 执行阶段。
142
- 4. 不支持缺省签名、debug 签名或无签名转换。
143
-
144
- 输出覆盖规则:
145
-
146
- - 默认覆盖已存在的同名 APK 和中间 `.apks` 文件。
147
- - 如需防止覆盖,显式传 `--no-overwrite`。
148
-
149
- ## 核心转换流程
150
-
151
- 内部调用 `bundletool`:
152
-
153
- ```bat
154
- java.exe -jar bundletool.jar build-apks ^
155
- --bundle="D:\packages\game.aab" ^
156
- --output="D:\output\game.apks" ^
157
- --mode=universal ^
158
- --ks="D:\keys\release.jks" ^
159
- --ks-key-alias="release" ^
160
- --ks-pass=pass:xxx ^
161
- --key-pass=pass:xxx ^
162
- --overwrite
163
- ```
164
-
165
- 然后把 `.apks` 当 zip 解压,提取:
166
-
167
- ```text
168
- universal.apk
169
- ```
170
-
171
- 默认输出命名:
172
-
173
- ```text
174
- 原AAB文件名_universal.apk
175
- ```
176
-
177
- 示例:
178
-
179
- ```text
180
- game.aab -> game_universal.apk
181
- ```
182
-
183
- ## 模块设计
184
-
185
- 新增独立模块,不耦合现有接入工具:
186
-
187
- ```text
188
- src/
189
- aab-converter/
190
- aab-entry.ts
191
- cliArgs.ts
192
- convertAabToApk.ts
193
- bundletoolRunner.ts
194
- apksExtractor.ts
195
- signingOptions.ts
196
- types.ts
197
-
198
- shared/
199
- processRunner.ts
200
- fileUtils.ts
201
- pathUtils.ts
202
- logger.ts
203
- errors.ts
204
- ```
205
-
206
- 允许的依赖方向:
207
-
208
- ```text
209
- aab-converter -> shared
210
- 原接入工具 -> shared
211
- ```
212
-
213
- 禁止的依赖方向:
214
-
215
- ```text
216
- aab-converter -> recipe / pipeline / integrate
217
- recipe / pipeline / integrate -> aab-converter
218
- ```
219
-
220
- 原因:AAB 转 APK 是独立文件转换,原接入工具是宿主工程改造。两者业务模型不同,只能共用基础库,不能共用集成 pipeline。
221
-
222
- ## 与现有工具的关系
223
-
224
- `meet-sdk-tool` 作为工具集存在,包含多个独立工具:
225
-
226
- ```text
227
- SDK 接入工具:
228
- meetgames setup / integrate / doctor
229
- 使用 recipe / pipeline / op handlers
230
-
231
- AAB 转 APK 工具:
232
- aab2apk.cmd convert
233
- 使用 aab-converter 独立流程
234
- ```
235
-
236
- 现有 `meetgames` CLI、MCP、SDK 接入逻辑不应因 AAB 转换工具发生行为变化。
237
-
238
- ## 打包方案
239
-
240
- macOS 负责生成 Windows 绿色版 zip,但只是组装文件,不编译 Windows 原生代码。
241
-
242
- 打包前置条件:
243
-
244
- - 已完成 TypeScript 编译
245
- - 已准备 Windows x64 `node.exe`
246
- - 已准备 Windows x64 JRE
247
- - 已准备 `bundletool.jar`
248
- - 运行时依赖不包含 macOS 原生二进制
249
-
250
- 建议脚本:
251
-
252
- ```text
253
- npm run build:aab
254
- npm run package:aab-win-zip
255
- ```
256
-
257
- 打包脚本职责:
258
-
259
- 1. 清理临时 release 目录。
260
- 2. 拷贝编译后的 `dist/aab-converter` 和必要的 `dist/shared`。
261
- 3. 拷贝 `runtime/win-x64/node/node.exe`。
262
- 4. 拷贝 `runtime/win-x64/jre`。
263
- 5. 拷贝 `tools/bundletool.jar`。
264
- 6. 生成 `aab2apk.cmd`。
265
- 7. 创建空 `logs/` 和 `output/`。
266
- 8. 压缩为 `MeetSDKTool-AABConverter-win-x64.zip`。
267
- 9. 排除 `.DS_Store`、`__MACOSX/` 等 macOS 文件。
268
-
269
- ## Windows 验收
270
-
271
- macOS 生成 zip 后,必须在 Windows 机器验收。
272
-
273
- 最小验收命令:
274
-
275
- ```bat
276
- aab2apk.cmd --help
277
- ```
278
-
279
- 完整验收命令:
280
-
281
- ```bat
282
- set KS_PASS=your_keystore_password
283
- set KEY_PASS=your_key_password
284
-
285
- aab2apk.cmd convert ^
286
- --aab "D:\test\game.aab" ^
287
- --out-dir "D:\test\out" ^
288
- --ks "D:\test\release.jks" ^
289
- --ks-key-alias "release" ^
290
- --ks-pass-env KS_PASS ^
291
- --key-pass-env KEY_PASS ^
292
- --verbose
293
- ```
294
-
295
- 验收项:
296
-
297
- - Windows 上解压 zip 后能直接运行。
298
- - 机器未安装 Node 时也能运行。
299
- - 机器未安装 Java 时也能运行。
300
- - 中文路径可用。
301
- - 带空格路径可用。
302
- - 输出目录不存在时可创建。
303
- - 默认覆盖同名输出。
304
- - 传 `--no-overwrite` 时,同名输出存在则失败并提示。
305
- - keystore 密码错误时提示清晰。
306
- - alias 错误时提示清晰。
307
- - bundletool 失败时保留日志。
308
- - 成功生成 `*_universal.apk`。
309
-
310
- ## 错误提示
311
-
312
- 需要对常见错误做中文化映射:
313
-
314
- | 场景 | 提示方向 |
315
- |---|---|
316
- | AAB 文件不存在 | 检查 `--aab` 路径 |
317
- | AAB 后缀不正确 | 只支持 `.aab` 文件 |
318
- | keystore 文件不存在 | 检查 `--ks` 路径 |
319
- | 签名参数不完整 | 检查 `--ks-key-alias` 和密码参数 |
320
- | 环境变量不存在 | 检查 `--ks-pass-env` / `--key-pass-env` |
321
- | Java 缺失 | 绿色版包损坏或 JRE 未打入 |
322
- | bundletool 缺失 | 绿色版包损坏或 `bundletool.jar` 未打入 |
323
- | 密码或 alias 错误 | 检查 keystore 密码、key 密码和 alias |
324
- | 未找到 `universal.apk` | 检查 bundletool 输出和 `.apks` 内容 |
325
- | 输出目录不可写 | 更换输出目录或检查权限 |
326
-
327
- ## 测试策略
328
-
329
- 核心单元测试:
330
-
331
- - CLI 参数解析
332
- - 签名参数解析
333
- - bundletool 参数构造
334
- - 输出文件命名
335
- - `.apks` 解压与 `universal.apk` 查找
336
-
337
- 集成测试:
338
-
339
- - 使用测试 AAB 跑通转换
340
- - 验证输出 APK 存在
341
- - 验证 `--keep-apks`
342
- - 验证默认覆盖
343
- - 验证 `--no-overwrite`
344
-
345
- 测试文件放置目录:
346
-
347
- ```text
348
- test-assets/aab-converter/
349
- sample.aab
350
- sample-release.jks
351
- README.md 或其他本地 .md
352
- ```
353
-
354
- 注意:
355
-
356
- - 只放测试包和测试签名文件,不能放生产 keystore。
357
- - `.aab`、`.apk`、`.apks`、`.jks`、`.keystore`、该目录下的 `.md` 默认不提交到 git。
358
- - 测试密码和 alias 可以写在同目录的本地 `.md` 中或通过环境变量传入,不要提交真实生产密码。
359
-
360
- 回归测试:
361
-
362
- - 现有 `npm test` 必须通过
363
- - 现有 `meetgames setup / integrate / doctor` 行为不变
364
-
365
- ## 实施顺序
366
-
367
- 1. 新增 `src/aab-converter` 核心模块。
368
- 2. 新增必要的 `src/shared` 基础能力。
369
- 3. 新增 `aab-entry.ts` CLI 入口。
370
- 4. 新增测试。
371
- 5. 准备 `bundletool.jar`、Windows Node、Windows JRE。
372
- 6. 新增绿色版 zip 打包脚本。
373
- 7. 在 macOS 生成 zip。
374
- 8. 在 Windows 实机验收。
375
- 9. 补充用户使用说明和常见问题。
376
-
377
- ## 实现注意事项
378
-
379
- - `.apks` 必须按 ZIP 结构流式读取,不能一次性读入内存。实际测试样本超过 2GB,一次性 `readFile` 会触发 Node 单 Buffer 限制。
380
- - bundletool 命令日志必须脱敏 `--ks-pass` 和 `--key-pass`。
381
- - macOS 本地验证时不能自动使用 `runtime/win-x64/jre/bin/java.exe`,应使用系统 `java` 或显式 `AAB2APK_JAVA_PATH`;Windows 绿色版运行时使用包内 `runtime/jre/bin/java.exe`。
382
- - `test-assets/aab-converter/` 下的 AAB、JKS 和本地密钥说明 `.md` 只能用于本地验证,不能提交。
383
-
384
- ## 最终结论
385
-
386
- 第一版采用纯命令行绿色版:
387
-
388
- ```text
389
- TypeScript CLI + Windows node.exe + Windows JRE + bundletool.jar
390
- ```
391
-
392
- macOS 负责生成 zip,Windows 负责验收。AAB 转 APK 工具作为 `meet-sdk-tool` 工具集中的独立工具存在,不复用现有 `recipe / pipeline / integrate` 机制,只在必要时共用基础工具库。
@@ -1,5 +0,0 @@
1
- [2026-06-22T07:50:37.206Z] [INFO] AAB: /Users/work/Workspace/Projects/meet-sdk-tool/test-assets/aab-converter/sample.aab
2
- [2026-06-22T07:50:37.206Z] [INFO] Output directory: /private/tmp/aab2apk-out
3
- [2026-06-22T07:50:37.206Z] [INFO] APK set: /private/tmp/aab2apk-out/sample.apks
4
- [2026-06-22T07:50:37.206Z] [INFO] Output APK: /private/tmp/aab2apk-out/sample_universal.apk
5
- [2026-06-22T07:50:37.207Z] [INFO] Running bundletool: java -jar /Users/work/Workspace/Projects/meet-sdk-tool/tools/bundletool.jar build-apks --bundle=/Users/work/Workspace/Projects/meet-sdk-tool/test-assets/aab-converter/sample.aab --output=/private/tmp/aab2apk-out/sample.apks --mode=universal --ks=/Users/work/Workspace/Projects/meet-sdk-tool/test-assets/aab-converter/dragonstone.jks --ks-key-alias=dragonstone --ks-pass=pass:****** --key-pass=pass:****** --overwrite
@@ -1,6 +0,0 @@
1
- [2026-06-22T07:52:26.222Z] [INFO] AAB: /Users/work/Workspace/Projects/meet-sdk-tool/test-assets/aab-converter/sample.aab
2
- [2026-06-22T07:52:26.222Z] [INFO] Output directory: /private/tmp/aab2apk-out
3
- [2026-06-22T07:52:26.222Z] [INFO] APK set: /private/tmp/aab2apk-out/sample.apks
4
- [2026-06-22T07:52:26.222Z] [INFO] Output APK: /private/tmp/aab2apk-out/sample_universal.apk
5
- [2026-06-22T07:52:26.222Z] [INFO] Running bundletool: java -jar /Users/work/Workspace/Projects/meet-sdk-tool/tools/bundletool.jar build-apks --bundle=/Users/work/Workspace/Projects/meet-sdk-tool/test-assets/aab-converter/sample.aab --output=/private/tmp/aab2apk-out/sample.apks --mode=universal --ks=/Users/work/Workspace/Projects/meet-sdk-tool/test-assets/aab-converter/dragonstone.jks --ks-key-alias=dragonstone --ks-pass=pass:****** --key-pass=pass:****** --overwrite
6
- [2026-06-22T07:52:58.806Z] [INFO] Extracted universal APK: /private/tmp/aab2apk-out/sample_universal.apk
@@ -1,193 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import zlib from "node:zlib";
4
-
5
- const root = process.cwd();
6
- const productName = "MeetSDKTool-AABConverter-win-x64";
7
- const releaseDir = path.join(root, "release");
8
- const stagingDir = path.join(releaseDir, productName);
9
- const zipPath = path.join(releaseDir, `${productName}.zip`);
10
-
11
- function fail(message) {
12
- console.error(`[package:aab-win-zip] ERROR: ${message}`);
13
- process.exit(1);
14
- }
15
-
16
- function assertExists(p, label) {
17
- if (!fs.existsSync(p)) fail(`${label} not found: ${p}`);
18
- }
19
-
20
- function copyDir(src, dest) {
21
- assertExists(src, "Source directory");
22
- fs.mkdirSync(path.dirname(dest), { recursive: true });
23
- fs.cpSync(src, dest, { recursive: true });
24
- }
25
-
26
- function copyFileIfExists(src, dest) {
27
- if (!fs.existsSync(src)) return;
28
- fs.mkdirSync(path.dirname(dest), { recursive: true });
29
- fs.copyFileSync(src, dest);
30
- }
31
-
32
- function copyNodeRuntime(src, dest) {
33
- assertExists(path.join(src, "node.exe"), "Windows node.exe");
34
- fs.mkdirSync(dest, { recursive: true });
35
- fs.copyFileSync(path.join(src, "node.exe"), path.join(dest, "node.exe"));
36
- copyFileIfExists(path.join(src, "LICENSE"), path.join(dest, "LICENSE"));
37
- copyFileIfExists(path.join(src, "README.md"), path.join(dest, "README.md"));
38
- }
39
-
40
- function writeCmd() {
41
- const cmd = [
42
- "@echo off",
43
- "setlocal",
44
- 'set "TOOL_DIR=%~dp0"',
45
- '"%TOOL_DIR%runtime\\node\\node.exe" "%TOOL_DIR%app\\aab-converter\\aab-entry.js" %*',
46
- "exit /b %ERRORLEVEL%",
47
- "",
48
- ].join("\r\n");
49
- fs.writeFileSync(path.join(stagingDir, "aab2apk.cmd"), cmd, "utf8");
50
- }
51
-
52
- function crc32(buf) {
53
- let crc = 0xffffffff;
54
- for (const b of buf) {
55
- crc ^= b;
56
- for (let i = 0; i < 8; i += 1) {
57
- crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1));
58
- }
59
- }
60
- return (crc ^ 0xffffffff) >>> 0;
61
- }
62
-
63
- function u16(value) {
64
- const buf = Buffer.alloc(2);
65
- buf.writeUInt16LE(value);
66
- return buf;
67
- }
68
-
69
- function u32(value) {
70
- const buf = Buffer.alloc(4);
71
- buf.writeUInt32LE(value);
72
- return buf;
73
- }
74
-
75
- function listEntries(baseDir) {
76
- const out = [];
77
- function walk(abs, rel) {
78
- const stat = fs.statSync(abs);
79
- if (stat.isDirectory()) {
80
- if (rel) out.push({ rel: `${rel}/`, dir: true, content: Buffer.alloc(0) });
81
- for (const name of fs.readdirSync(abs).sort()) {
82
- if (name === ".DS_Store" || name === "__MACOSX") continue;
83
- walk(path.join(abs, name), rel ? `${rel}/${name}` : name);
84
- }
85
- return;
86
- }
87
- if (stat.isFile()) {
88
- out.push({ rel, dir: false, content: fs.readFileSync(abs) });
89
- }
90
- }
91
- walk(baseDir, productName);
92
- return out;
93
- }
94
-
95
- function writeZip(baseDir, outPath) {
96
- const entries = listEntries(baseDir);
97
- const localParts = [];
98
- const centralParts = [];
99
- let offset = 0;
100
- for (const entry of entries) {
101
- const name = Buffer.from(entry.rel, "utf8");
102
- const compressed = entry.dir ? Buffer.alloc(0) : zlib.deflateRawSync(entry.content);
103
- const method = entry.dir ? 0 : 8;
104
- const crc = entry.dir ? 0 : crc32(entry.content);
105
- const local = Buffer.concat([
106
- u32(0x04034b50),
107
- u16(20),
108
- u16(0),
109
- u16(method),
110
- u16(0),
111
- u16(0),
112
- u32(crc),
113
- u32(compressed.length),
114
- u32(entry.content.length),
115
- u16(name.length),
116
- u16(0),
117
- name,
118
- compressed,
119
- ]);
120
- localParts.push(local);
121
- centralParts.push(
122
- Buffer.concat([
123
- u32(0x02014b50),
124
- u16(20),
125
- u16(20),
126
- u16(0),
127
- u16(method),
128
- u16(0),
129
- u16(0),
130
- u32(crc),
131
- u32(compressed.length),
132
- u32(entry.content.length),
133
- u16(name.length),
134
- u16(0),
135
- u16(0),
136
- u16(0),
137
- u16(0),
138
- u32(entry.dir ? 0x10 : 0),
139
- u32(offset),
140
- name,
141
- ])
142
- );
143
- offset += local.length;
144
- }
145
- const centralDir = Buffer.concat(centralParts);
146
- const eocd = Buffer.concat([
147
- u32(0x06054b50),
148
- u16(0),
149
- u16(0),
150
- u16(entries.length),
151
- u16(entries.length),
152
- u32(centralDir.length),
153
- u32(offset),
154
- u16(0),
155
- ]);
156
- fs.writeFileSync(outPath, Buffer.concat([...localParts, centralDir, eocd]));
157
- }
158
-
159
- assertExists(path.join(root, "dist", "aab-converter", "aab-entry.js"), "Compiled AAB CLI entry");
160
- assertExists(path.join(root, "dist", "shared"), "Compiled shared modules");
161
- assertExists(path.join(root, "tools", "bundletool.jar"), "bundletool.jar");
162
- assertExists(path.join(root, "runtime", "win-x64", "node", "node.exe"), "Windows node.exe");
163
- assertExists(path.join(root, "runtime", "win-x64", "jre", "bin", "java.exe"), "Windows JRE java.exe");
164
-
165
- fs.rmSync(stagingDir, { recursive: true, force: true });
166
- fs.rmSync(zipPath, { force: true });
167
- fs.mkdirSync(stagingDir, { recursive: true });
168
-
169
- copyDir(path.join(root, "dist", "aab-converter"), path.join(stagingDir, "app", "aab-converter"));
170
- copyDir(path.join(root, "dist", "shared"), path.join(stagingDir, "app", "shared"));
171
- copyNodeRuntime(path.join(root, "runtime", "win-x64", "node"), path.join(stagingDir, "runtime", "node"));
172
- copyDir(path.join(root, "runtime", "win-x64", "jre"), path.join(stagingDir, "runtime", "jre"));
173
- fs.mkdirSync(path.join(stagingDir, "tools"), { recursive: true });
174
- fs.copyFileSync(path.join(root, "tools", "bundletool.jar"), path.join(stagingDir, "tools", "bundletool.jar"));
175
- fs.mkdirSync(path.join(stagingDir, "logs"), { recursive: true });
176
- fs.mkdirSync(path.join(stagingDir, "output"), { recursive: true });
177
- writeCmd();
178
- fs.writeFileSync(
179
- path.join(stagingDir, "README.txt"),
180
- [
181
- "Meet SDK AAB Converter",
182
- "",
183
- "Example:",
184
- " set KS_PASS=your_keystore_password",
185
- " set KEY_PASS=your_key_password",
186
- " aab2apk.cmd convert --aab D:\\packages\\game.aab --out-dir D:\\output --ks D:\\keys\\release.jks --ks-key-alias release --ks-pass-env KS_PASS --key-pass-env KEY_PASS",
187
- "",
188
- ].join("\r\n"),
189
- "utf8"
190
- );
191
-
192
- writeZip(stagingDir, zipPath);
193
- console.log(`[package:aab-win-zip] wrote ${zipPath}`);
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env node
2
- import path from "node:path";
3
- import { UserFacingError, errorMessage } from "../shared/errors.js";
4
- import { createFileLogger } from "../shared/logger.js";
5
- import { convertAabToApk } from "./convertAabToApk.js";
6
- import { parseAabCliArgv, printAabHelp, validateConvertArgs } from "./cliArgs.js";
7
- import { resolveBundletoolPath, resolveJavaPath, resolveToolRoot } from "./resourcePaths.js";
8
-
9
- const EXIT = {
10
- OK: 0,
11
- INVALID_ARGS: 2,
12
- CONVERT_FAILED: 3,
13
- } as const;
14
-
15
- export async function main(): Promise<void> {
16
- const parsed = parseAabCliArgv(process.argv);
17
- if (parsed.help) {
18
- printAabHelp();
19
- process.exit(EXIT.OK);
20
- }
21
-
22
- const toolRoot = resolveToolRoot(import.meta.url);
23
- const convertArgs = validateConvertArgs(parsed);
24
- const logger = createFileLogger(path.join(toolRoot, "logs"));
25
- try {
26
- const result = await convertAabToApk({
27
- ...convertArgs,
28
- javaPath: resolveJavaPath(toolRoot),
29
- bundletoolPath: resolveBundletoolPath(toolRoot),
30
- logger,
31
- });
32
- console.log(`[aab2apk] APK: ${result.apkPath}`);
33
- if (result.apksPath) console.log(`[aab2apk] APKS: ${result.apksPath}`);
34
- if (result.logPath) console.log(`[aab2apk] Log: ${result.logPath}`);
35
- } finally {
36
- logger.close();
37
- }
38
- }
39
-
40
- main().catch((err: unknown) => {
41
- if (err instanceof UserFacingError) {
42
- console.error(`[aab2apk] ERROR (${err.code}): ${err.message}`);
43
- if (err.details) console.error(err.details);
44
- process.exit(err.code === "INVALID_ARGS" || err.code === "INVALID_COMMAND" ? EXIT.INVALID_ARGS : EXIT.CONVERT_FAILED);
45
- }
46
- console.error(`[aab2apk] ERROR: ${errorMessage(err)}`);
47
- process.exit(EXIT.CONVERT_FAILED);
48
- });