@heybox/hb-sdk 0.8.2-alpha.2 → 0.8.2-alpha.5

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 (47) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +5 -1
  3. package/dist/cli-chunks/{build-bVThH3Z5.cjs → build-D2T3ky_9.cjs} +5 -5
  4. package/dist/cli-chunks/{context-DpKqGnjp.cjs → context-DEgmdpSL.cjs} +326 -104
  5. package/dist/cli-chunks/{create-BWC2343y.cjs → create-DKqQhrG5.cjs} +5 -1
  6. package/dist/cli-chunks/{dev-t14YpHHY.cjs → dev-BEXM7cmq.cjs} +17 -9
  7. package/dist/cli-chunks/{doctor-BWkrE0Hj.cjs → doctor-B7UixGrd.cjs} +1 -1
  8. package/dist/cli-chunks/{index-CDtcZVWv.cjs → index-BtwfuoWI.cjs} +15 -17
  9. package/dist/cli-chunks/{index-D3FMcOZq.cjs → index-C69OtRug.cjs} +2 -2
  10. package/dist/cli-chunks/{index.esm-BrJBaJWS.cjs → index.esm-C86KlVdK.cjs} +11 -10
  11. package/dist/cli-chunks/{login-DRMl7nFH.cjs → login-Bp_HMx-i.cjs} +2 -2
  12. package/dist/cli-chunks/{project-vite-CXgdzIYw.cjs → project-vite-DZxipd13.cjs} +1 -1
  13. package/dist/cli-chunks/{remote-ibQybNiM.cjs → remote-B_sVsbQP.cjs} +80 -60
  14. package/dist/cli-chunks/runtime-gate-GR9jZd37.cjs +58 -0
  15. package/dist/cli-chunks/{runtime-gate-BWb8QIcX.cjs → runtime-permission-env-COWketgE.cjs} +4533 -40
  16. package/dist/cli-chunks/{session-CIoBspg-.cjs → session-6-AApcTZ.cjs} +1 -1
  17. package/dist/cli-chunks/{skill-5labzuWk.cjs → skill-CPDLk7t7.cjs} +2 -2
  18. package/dist/cli-chunks/{version-QiVEs76a.cjs → version-hHrSPRwW.cjs} +1 -1
  19. package/dist/cli.cjs +1 -1
  20. package/dist/devtools/browser-dev-host/assets/browser-dev-host-CpwQQhA1.js +101 -0
  21. package/dist/devtools/browser-dev-host/assets/{desktop-app-launch-B4KsgItx.js → desktop-app-launch-BdWBOIvY.js} +1 -1
  22. package/dist/devtools/browser-dev-host/assets/{index-9Nb3XhtI.js → index-BQSEynx6.js} +3 -3
  23. package/dist/devtools/browser-dev-host/index.html +2 -2
  24. package/dist/index.cjs.js +1 -1
  25. package/dist/index.esm.js +1 -1
  26. package/dist/miniapp-publish.cjs.js +8782 -0
  27. package/dist/miniapp-publish.esm.js +8778 -1
  28. package/dist/templates/vanilla-vite-js/assets/heybox-placeholder.png +0 -0
  29. package/dist/templates/vanilla-vite-js/package.json.ejs +3 -0
  30. package/dist/vite.cjs.js +499 -346
  31. package/dist/vite.esm.js +500 -347
  32. package/package.json +7 -6
  33. package/skill/SKILL.md +5 -1
  34. package/skill/references/api-root.md +5 -4
  35. package/skill/references/cli.md +3 -3
  36. package/skill/references/recipes.md +3 -2
  37. package/skill/skill.json +5 -5
  38. package/types/miniapp-manifest/basic-info-browser.d.ts +9 -0
  39. package/types/miniapp-manifest/basic-info-image-header.d.ts +6 -0
  40. package/types/miniapp-manifest/basic-info-node.d.ts +17 -0
  41. package/types/miniapp-manifest/basic-info-placeholder.d.ts +1 -0
  42. package/types/miniapp-manifest/basic-info.d.ts +12 -0
  43. package/types/miniapp-manifest/schema.d.ts +7 -1
  44. package/types/miniapp-publish/index.d.ts +8 -0
  45. package/dist/cli-chunks/runtime-permission-env-DiTeD75Q.cjs +0 -4498
  46. package/dist/devtools/browser-dev-host/assets/browser-dev-host-DxzOkljJ.js +0 -101
  47. /package/types/{vite → miniapp-manifest}/runtime-gate.d.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,6 +1,37 @@
1
1
  # Changelog
2
2
 
3
- 本文件随 `@heybox/hb-sdk` npm 包公开分发,只记录外部小程序开发者可观察到的变化。内部集成、实现策略和审查证据不在此维护。
3
+ 本文件随 `@heybox/hb-sdk` npm 包公开分发,只记录外部小程序开发者可观察到的变化。
4
+
5
+ ## 0.8.2-alpha.5
6
+
7
+ - 无面向小程序开发者的可见变化。
8
+
9
+ ## 0.8.2-alpha.4
10
+
11
+ - Companion 准备与启动的 Host 可信授权信息新增可选归档 SHA-256,供宿主展示产物完整性;公开 SDK `CompanionInfo` 与 iframe 返回保持不变。
12
+ - 补充授权弹窗展示产物来源、签名状态、目标平台、人类可读归档大小和完整 SHA-256 的文档说明。
13
+
14
+ ## 0.8.2-alpha.3 - 2026-09-21
15
+
16
+ > 摘要:预发布验证版本名称和图片项目化、正式资料自动迁移及上传前发布预检。
17
+ > 影响级别:必须操作
18
+ > 开发者动作:仅测试项目升级至 0.8.2-alpha.3,在 package.json#heybox 配置 name、icon、coverImages;已有正式发布版本可通过 bind/dev/deploy 自动迁移全缺失配置。
19
+ > 兼容范围:这是 0.8.2 稳定版前的验证版本;满足最低 SDK 策略的旧产物继续兼容,平台与权限的 0.9.0 切点不变。
20
+
21
+ ### 必须操作
22
+
23
+ - 在项目声明名称、icon 和 1–5 张封面;图片使用项目内 JPEG、PNG、GIF 或 WebP,icon 为 200×200,单图不超过 10 MiB 和 4000 万像素。提交前替换脚手架占位图。
24
+ - 每次部署重新构建当前项目,不再复用历史版本产物。详见[版本资料与迁移](https://docs.xiaoheihe.cn/hb_sdk/guide/version-profile)。
25
+
26
+ ### 新增能力
27
+
28
+ - bind/dev/deploy 在三项资料全部缺失时迁移当前正式发布快照,已下架项目也可迁移;部分配置和从未发布的项目需手工配置。
29
+ - remote info 只读比较本地和已发布资料;图片无法读取时显示 unavailable。
30
+
31
+ ### 行为调整
32
+
33
+ - build 保持离线,图片原样进入构建产物;Open 展示构建资料的只读预览。
34
+ - 不合规发布在上传前反馈;提交失败后保留已上传文件,再次提交相同构建时复用。
4
35
 
5
36
  ## 0.8.2-alpha.2
6
37
 
package/README.md CHANGED
@@ -117,7 +117,7 @@ async function revokeAuthorizationFromUserAction() {
117
117
  }
118
118
  ```
119
119
 
120
- `bindAppIds` 只接受规范十进制字符串(形如 `/^[1-9]\d*$/`,拒绝前导零、空串与非数字),每项必须是正 int32(最大 `2147483647`),数组内不允许重复,最多 20 项,并按数值升序写入 Manifest。它不是权限,构建不联网;游戏真实性在远端预检和提审时校验。字段缺失或 `[]` 都表示清空绑定,`remote deploy --from-version` 则继承源版本快照。PC 展示仍受 `platforms` 过滤。
120
+ `bindAppIds` 只接受规范十进制字符串(形如 `/^[1-9]\d*$/`,拒绝前导零、空串与非数字),每项必须是正 int32(最大 `2147483647`),数组内不允许重复,最多 20 项,并按数值升序写入 Manifest。它不是权限,构建不联网;游戏真实性在远端预检和提审时校验。字段缺失或 `[]` 都表示清空绑定。PC 展示仍受 `platforms` 过滤。
121
121
 
122
122
  再启用构建插件:
123
123
 
@@ -415,3 +415,7 @@ hb-sdk remote deploy --release-note "本次更新说明"
415
415
  - [Runtime Host 迁移](./docs/runtime-host-migration.md)
416
416
 
417
417
  Host/Runtime 集成维护者请先阅读 [`@heybox/hb-sdk-runtime` README](../hb-sdk-runtime/README.md)、[Runtime Reference](../hb-sdk-runtime/docs/reference/) 和 [`@heybox/hb-sdk-protocol` README](../hb-sdk-protocol/README.md)。
418
+
419
+ ## 版本名称与图片(0.8.2)
420
+
421
+ 在 `package.json#heybox` 配置 `name`、`icon`、`coverImages`。图片为项目内文件,icon 200×200、封面 1–5 张,单张不超过 10 MiB 和 4000 万像素。构建原样复制到版本产物,发布时才更新线上资料。全缺失配置可通过 bind/dev/deploy 从当前正式发布快照自动迁移;离线 build 不迁移。详见[版本资料与迁移指南](https://docs.xiaoheihe.cn/hb_sdk/guide/version-profile)。
@@ -3,13 +3,13 @@
3
3
  var fs$1 = require('node:fs');
4
4
  var fs = require('node:fs/promises');
5
5
  var path = require('node:path');
6
- var runtimePermissionEnv = require('./runtime-permission-env-DiTeD75Q.cjs');
6
+ var runtimePermissionEnv = require('./runtime-permission-env-COWketgE.cjs');
7
7
  require('node:crypto');
8
8
  require('node:zlib');
9
- var runtimeGate = require('./runtime-gate-BWb8QIcX.cjs');
10
- var index = require('./index-CDtcZVWv.cjs');
11
- var projectVite = require('./project-vite-CXgdzIYw.cjs');
12
- require('./version-QiVEs76a.cjs');
9
+ var runtimeGate = require('./runtime-gate-GR9jZd37.cjs');
10
+ var index = require('./index-BtwfuoWI.cjs');
11
+ var projectVite = require('./project-vite-DZxipd13.cjs');
12
+ require('./version-hHrSPRwW.cjs');
13
13
  require('node:async_hooks');
14
14
  require('node:module');
15
15
  require('path');
@@ -1,10 +1,327 @@
1
1
  'use strict';
2
2
 
3
- var runtimePermissionEnv = require('./runtime-permission-env-DiTeD75Q.cjs');
4
- var session = require('./session-CIoBspg-.cjs');
5
- var fs$1 = require('node:fs');
3
+ var node_crypto = require('node:crypto');
6
4
  var fs = require('node:fs/promises');
7
5
  var path = require('node:path');
6
+ var runtimePermissionEnv = require('./runtime-permission-env-COWketgE.cjs');
7
+ var fs$1 = require('node:fs');
8
+ var sharp = require('sharp');
9
+ var session = require('./session-6-AApcTZ.cjs');
10
+
11
+ async function inspectBasicInfoImage(bytes, sourcePath, kind) {
12
+ const inspected = await inspectDecodedBasicInfoImage(bytes, kind);
13
+ const extensions = { jpg: ['jpg', 'jpeg'], png: ['png'], gif: ['gif'], webp: ['webp'] };
14
+ const ext = sourcePath.split('.').pop().toLowerCase();
15
+ if (!extensions[inspected.extension]?.includes(ext))
16
+ throw new Error('资料图片格式与扩展名不一致');
17
+ return inspected;
18
+ }
19
+ async function inspectRemoteBasicInfoImage(bytes, kind) {
20
+ return inspectDecodedBasicInfoImage(bytes, kind);
21
+ }
22
+ async function inspectDecodedBasicInfoImage(bytes, kind) {
23
+ if (!bytes.length || bytes.length > runtimePermissionEnv.BASIC_INFO_MAX_BYTES)
24
+ throw new Error('资料图片不得为空或超过 10 MiB');
25
+ const decoder = sharp(bytes, { limitInputPixels: runtimePermissionEnv.BASIC_INFO_MAX_PIXELS, failOn: 'warning' });
26
+ const metadata = await decoder.metadata();
27
+ const extensions = { jpeg: 'jpg', png: 'png', gif: 'gif', webp: 'webp' };
28
+ if (!metadata.format || !extensions[metadata.format])
29
+ throw new Error('资料图片格式不支持');
30
+ const width = metadata.width ?? 0;
31
+ const height = metadata.pageHeight ?? metadata.height ?? 0;
32
+ if (!width || !height || width * height > runtimePermissionEnv.BASIC_INFO_MAX_PIXELS)
33
+ throw new Error('资料图片不得超过 4000 万像素');
34
+ if (kind === 'icon' && (width !== 200 || height !== 200))
35
+ throw new Error('icon 必须是 200×200');
36
+ // 强制解码而非只信任图片头;不将解码结果写回图片。
37
+ await decoder.stats();
38
+ return { sha256: node_crypto.createHash('sha256').update(bytes).digest('hex'), extension: extensions[metadata.format] };
39
+ }
40
+ async function readBasicInfoSource(projectRoot, relative, kind) {
41
+ runtimePermissionEnv.validateBasicInfoPath(relative);
42
+ const root = await fs.realpath(projectRoot);
43
+ const target = await fs.realpath(path.join(root, relative));
44
+ const resolvedRelative = path.relative(root, target).split(path.sep).join('/');
45
+ runtimePermissionEnv.validateBasicInfoPath(resolvedRelative);
46
+ if (resolvedRelative.startsWith('../') || path.isAbsolute(resolvedRelative))
47
+ throw new Error('资料图片不能逃逸项目目录');
48
+ const info = await fs.stat(target);
49
+ if (!info.isFile() || info.size > runtimePermissionEnv.BASIC_INFO_MAX_BYTES)
50
+ throw new Error('资料图片必须为不超过 10 MiB 的普通文件');
51
+ const file = await fs.open(target, fs$1.constants.O_RDONLY | fs$1.constants.O_NOFOLLOW);
52
+ try {
53
+ const actual = await file.stat();
54
+ if (!actual.isFile() || actual.size > runtimePermissionEnv.BASIC_INFO_MAX_BYTES)
55
+ throw new Error('资料图片文件已变化');
56
+ const bytes = await file.readFile();
57
+ return { bytes, ...(await inspectBasicInfoImage(bytes, relative, kind)) };
58
+ }
59
+ finally {
60
+ await file.close();
61
+ }
62
+ }
63
+ /** 逐级校验真实目录,避免保留目录或迁移目录中的符号链接将写入带到项目外。 */
64
+ async function ensureBasicInfoDirectory(rootPath, relative) {
65
+ const root = await fs.realpath(rootPath);
66
+ let current = root;
67
+ for (const part of relative.split('/')) {
68
+ if (!part || part === '.' || part === '..')
69
+ throw new Error('资料目录不合法');
70
+ current = path.join(current, part);
71
+ await fs.mkdir(current).catch((error) => {
72
+ if (error.code !== 'EEXIST')
73
+ throw error;
74
+ });
75
+ const resolved = await fs.realpath(current);
76
+ const within = path.relative(root, resolved);
77
+ if (within.startsWith('..') || path.isAbsolute(within))
78
+ throw new Error('资料目录不能逃逸项目');
79
+ }
80
+ }
81
+
82
+ class MiniProgramProjectBindingError extends Error {
83
+ code;
84
+ constructor(message, code) {
85
+ super(message);
86
+ this.code = code;
87
+ this.name = 'MiniProgramProjectBindingError';
88
+ }
89
+ }
90
+ function findMiniProgramProjectRoot(startDir) {
91
+ let current = path.resolve(startDir);
92
+ while (true) {
93
+ if (fs$1.existsSync(path.join(current, 'package.json'))) {
94
+ return current;
95
+ }
96
+ const parent = path.dirname(current);
97
+ if (parent === current) {
98
+ throw new MiniProgramProjectBindingError('当前目录或父目录未找到 package.json', 'PACKAGE_JSON_NOT_FOUND');
99
+ }
100
+ current = parent;
101
+ }
102
+ }
103
+ async function readMiniProgramProjectPackage(startDir) {
104
+ const projectRoot = findMiniProgramProjectRoot(startDir);
105
+ const packageJsonPath = path.join(projectRoot, 'package.json');
106
+ const raw = await fs.readFile(packageJsonPath, 'utf8');
107
+ let packageJson;
108
+ try {
109
+ packageJson = JSON.parse(raw);
110
+ }
111
+ catch (error) {
112
+ const message = error instanceof Error ? error.message : String(error);
113
+ throw new MiniProgramProjectBindingError(`package.json 不是合法 JSON:${packageJsonPath}\n${message}`, 'PACKAGE_JSON_INVALID');
114
+ }
115
+ return {
116
+ projectRoot,
117
+ packageJsonPath,
118
+ packageJson,
119
+ raw,
120
+ indent: detectJsonIndent(raw),
121
+ trailingNewline: raw.endsWith('\r\n') ? '\r\n' : raw.endsWith('\n') ? '\n' : '',
122
+ };
123
+ }
124
+ function readBoundMiniProgramIdFromPackageJson(packageJson) {
125
+ const value = packageJson.heybox?.miniProgramId;
126
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
127
+ }
128
+ async function readBoundMiniProgramId(startDir) {
129
+ return readBoundMiniProgramIdFromPackageJson((await readMiniProgramProjectPackage(startDir)).packageJson);
130
+ }
131
+ async function requireBoundMiniProgramId(startDir) {
132
+ const context = await readMiniProgramProjectPackage(startDir);
133
+ const miniProgramId = readBoundMiniProgramIdFromPackageJson(context.packageJson);
134
+ if (!miniProgramId) {
135
+ throw new MiniProgramProjectBindingError(`当前项目未绑定小程序。请先运行 hb-sdk remote create 或 hb-sdk remote bind <mini-program-id>。\n配置位置:${context.packageJsonPath}#heybox.miniProgramId`, 'MINI_PROGRAM_UNBOUND');
136
+ }
137
+ return { ...context, miniProgramId };
138
+ }
139
+ async function writeBoundMiniProgramId(startDir, miniProgramId, options = {}) {
140
+ const context = await readMiniProgramProjectPackage(startDir);
141
+ const normalizedMiniProgramId = String(miniProgramId).trim();
142
+ if (!normalizedMiniProgramId) {
143
+ throw new MiniProgramProjectBindingError('miniProgramId 不能为空', 'PACKAGE_JSON_INVALID');
144
+ }
145
+ const currentMiniProgramId = readBoundMiniProgramIdFromPackageJson(context.packageJson);
146
+ if (currentMiniProgramId && currentMiniProgramId !== normalizedMiniProgramId && !options.force) {
147
+ throw new MiniProgramProjectBindingError(`当前项目已绑定小程序 ${currentMiniProgramId}。如需覆盖本地绑定,请使用 --force。`, 'MINI_PROGRAM_BINDING_EXISTS');
148
+ }
149
+ if (currentMiniProgramId === normalizedMiniProgramId) {
150
+ return {
151
+ ...context,
152
+ changed: false,
153
+ miniProgramId: normalizedMiniProgramId,
154
+ };
155
+ }
156
+ if (context.packageJson.heybox !== undefined && !isPlainObject(context.packageJson.heybox)) {
157
+ throw new MiniProgramProjectBindingError('package.json heybox 字段必须是对象,不能写入 miniProgramId', 'PACKAGE_JSON_HEYBOX_INVALID');
158
+ }
159
+ context.packageJson.heybox = {
160
+ ...(context.packageJson.heybox ?? {}),
161
+ miniProgramId: normalizedMiniProgramId,
162
+ };
163
+ await fs.writeFile(context.packageJsonPath, `${JSON.stringify(context.packageJson, null, context.indent)}${context.trailingNewline || '\n'}`, 'utf8');
164
+ return {
165
+ ...context,
166
+ changed: true,
167
+ miniProgramId: normalizedMiniProgramId,
168
+ };
169
+ }
170
+ function detectJsonIndent(raw) {
171
+ const match = raw.match(/\n(\s+)"[^"]+":/);
172
+ return match?.[1] ?? 2;
173
+ }
174
+ function isPlainObject(value) {
175
+ return Object.prototype.toString.call(value) === '[object Object]';
176
+ }
177
+
178
+ function trustedProfileUrl(raw) {
179
+ const url = new URL(raw);
180
+ const trusted = ['max-c.com', 'xiaoheihe.cn', 'heybox.com'];
181
+ if (url.protocol !== 'https:' ||
182
+ url.username ||
183
+ url.password ||
184
+ (url.port && url.port !== '443') ||
185
+ !trusted.some((host) => url.hostname === host || url.hostname.endsWith(`.${host}`)))
186
+ throw new Error('线上资料图片 URL 不受信任');
187
+ return url;
188
+ }
189
+ async function downloadImage(raw, fetchImpl) {
190
+ const response = await fetchImpl(trustedProfileUrl(raw), { redirect: 'error', signal: AbortSignal.timeout(15_000) });
191
+ if (!response.ok || !response.body)
192
+ throw new Error('下载线上资料图片失败');
193
+ if (Number(response.headers.get('content-length')) > runtimePermissionEnv.BASIC_INFO_MAX_BYTES)
194
+ throw new Error('图片超过 10 MiB');
195
+ const chunks = [];
196
+ let size = 0;
197
+ const reader = response.body.getReader();
198
+ try {
199
+ for (;;) {
200
+ const part = await reader.read();
201
+ if (part.done)
202
+ break;
203
+ size += part.value.length;
204
+ if (size > runtimePermissionEnv.BASIC_INFO_MAX_BYTES)
205
+ throw new Error('图片超过 10 MiB');
206
+ chunks.push(part.value);
207
+ }
208
+ }
209
+ finally {
210
+ await reader.cancel();
211
+ }
212
+ return Buffer.concat(chunks);
213
+ }
214
+ /** package-last 提交:失败只清理本次新增素材,原配置始终保留。 */
215
+ async function migrateBasicInfo(cwd, detail, miniProgramId, fetchImpl = fetch, force = false) {
216
+ const context = await readMiniProgramProjectPackage(cwd);
217
+ const heybox = context.packageJson.heybox ?? {};
218
+ const existing = heybox.miniProgramId;
219
+ if (existing && existing !== miniProgramId && !force)
220
+ throw new Error('当前项目已绑定其他小程序,请使用 --force');
221
+ const fields = ['name', 'icon', 'coverImages'];
222
+ const present = fields.filter((key) => Object.prototype.hasOwnProperty.call(heybox, key));
223
+ if (present.length && present.length !== 3)
224
+ throw new Error(`资料配置不完整,请补齐 ${fields.filter((key) => !present.includes(key)).join('、')}`);
225
+ const created = [];
226
+ const temp = `${context.packageJsonPath}.${node_crypto.randomUUID()}.tmp`;
227
+ try {
228
+ let profile = present.length ? runtimePermissionEnv.parseMiniappBasicInfo(heybox) : undefined;
229
+ if (profile) {
230
+ await readBasicInfoSource(context.projectRoot, profile.icon, 'icon');
231
+ for (const cover of profile.coverImages)
232
+ await readBasicInfoSource(context.projectRoot, cover, 'cover');
233
+ if (existing === miniProgramId)
234
+ return { changed: false, miniProgramId };
235
+ }
236
+ else {
237
+ if (detail.mini_program_id !== miniProgramId)
238
+ throw new Error('远端小程序身份不一致');
239
+ const snapshot = detail.published_basic_info;
240
+ if (!snapshot?.version ||
241
+ !snapshot.icon_url ||
242
+ !Array.isArray(snapshot.cover_image_urls) ||
243
+ snapshot.cover_image_urls.length < 1 ||
244
+ snapshot.cover_image_urls.length > 5)
245
+ throw new Error('没有当前正式发布快照,请手工配置 heybox.name/icon/coverImages');
246
+ profile = { name: runtimePermissionEnv.normalizeBasicInfoName(snapshot.name), icon: '', coverImages: [] };
247
+ const entries = [
248
+ { url: snapshot.icon_url, kind: 'icon', label: 'icon' },
249
+ ...snapshot.cover_image_urls.map((url, i) => ({ url, kind: 'cover', label: `cover-${String(i + 1).padStart(2, '0')}` })),
250
+ ];
251
+ for (const entry of entries) {
252
+ const bytes = await downloadImage(entry.url, fetchImpl);
253
+ const image = await inspectRemoteBasicInfoImage(bytes, entry.kind);
254
+ const relative = `assets/heybox/${entry.label}.${image.sha256.slice(0, 12)}.${image.extension}`;
255
+ const target = path.join(context.projectRoot, relative);
256
+ await ensureBasicInfoDirectory(context.projectRoot, 'assets/heybox');
257
+ try {
258
+ await fs.writeFile(target, bytes, { flag: 'wx' });
259
+ created.push(target);
260
+ }
261
+ catch (error) {
262
+ if (error.code !== 'EEXIST' ||
263
+ !(await readBasicInfoSource(context.projectRoot, relative, entry.kind).then((existing) => existing.bytes.equals(bytes), () => false)))
264
+ throw new Error('迁移素材名称冲突,未覆盖已有文件');
265
+ }
266
+ if (entry.kind === 'icon')
267
+ profile.icon = relative;
268
+ else
269
+ profile.coverImages.push(relative);
270
+ }
271
+ }
272
+ // 避免覆盖用户在下载期间修改的配置。
273
+ if ((await fs.readFile(context.packageJsonPath, 'utf8')) !== context.raw)
274
+ throw new Error('package.json 已变化,请重试迁移');
275
+ context.packageJson.heybox = { ...heybox, miniProgramId, ...profile };
276
+ await fs.writeFile(temp, JSON.stringify(context.packageJson, null, context.indent) + context.trailingNewline, { flag: 'wx' });
277
+ await fs.rename(temp, context.packageJsonPath);
278
+ return { changed: true, miniProgramId };
279
+ }
280
+ catch (error) {
281
+ // 若用户同时修改了 package,保留可能已被引用的素材供重试复用。
282
+ if ((await fs.readFile(context.packageJsonPath, 'utf8').catch(() => '')) === context.raw) {
283
+ await Promise.all(created.map((file) => fs.unlink(file).catch(() => { })));
284
+ }
285
+ throw error;
286
+ }
287
+ finally {
288
+ await fs.unlink(temp).catch(() => { });
289
+ }
290
+ }
291
+ async function compareBasicInfo(cwd, detail, fetchImpl = fetch) {
292
+ const context = await readMiniProgramProjectPackage(cwd);
293
+ const published = detail.published_basic_info;
294
+ let local;
295
+ try {
296
+ local = runtimePermissionEnv.parseMiniappBasicInfo(context.packageJson.heybox ?? {});
297
+ }
298
+ catch {
299
+ return { status: 'unavailable' };
300
+ }
301
+ if (!local || !published?.version)
302
+ return { status: 'unavailable', local, published };
303
+ try {
304
+ const pairs = [
305
+ { local: local.icon, remote: published.icon_url, kind: 'icon' },
306
+ ...local.coverImages.map((p, i) => ({ local: p, remote: published.cover_image_urls[i], kind: 'cover' })),
307
+ ];
308
+ let changed = local.name !== runtimePermissionEnv.normalizeBasicInfoName(published.name) || local.coverImages.length !== published.cover_image_urls.length;
309
+ for (const pair of pairs) {
310
+ if (!pair.remote) {
311
+ changed = true;
312
+ continue;
313
+ }
314
+ const source = await readBasicInfoSource(context.projectRoot, pair.local, pair.kind);
315
+ const bytes = await downloadImage(pair.remote, fetchImpl);
316
+ const remote = await inspectRemoteBasicInfoImage(bytes, pair.kind);
317
+ changed ||= source.sha256 !== remote.sha256;
318
+ }
319
+ return { status: changed ? 'changed' : 'unchanged', local, published };
320
+ }
321
+ catch {
322
+ return { status: 'unavailable', local, published };
323
+ }
324
+ }
8
325
 
9
326
  function shouldSkipMiniappPlatformCsp(runtimePermissions) {
10
327
  const parsed = runtimePermissionEnv.parseMiniProgramRuntimePermissions(runtimePermissions);
@@ -149,11 +466,9 @@ async function precheckUserMiniprogramVersion(options, runtime) {
149
466
  return remotePostForm(runtimePermissionEnv.PRECHECK_USER_MINIPROGRAM_VERSION_API_PATH, options, runtime);
150
467
  }
151
468
  async function submitUserMiniprogramAudit(options, runtime) {
152
- const form = { ...options };
153
- if (form.manifest && typeof form.manifest !== 'string') {
154
- form.manifest = JSON.stringify(form.manifest);
155
- }
156
- return remotePostForm(runtimePermissionEnv.SUBMIT_USER_MINIPROGRAM_AUDIT_API_PATH, form, runtime);
469
+ return remotePostForm(runtimePermissionEnv.SUBMIT_USER_MINIPROGRAM_AUDIT_API_PATH, {
470
+ mini_program_id: options.mini_program_id, session_id: options.session_id, preflight_token: options.preflight_token,
471
+ }, runtime);
157
472
  }
158
473
  async function getVersionPreviewInfo(options, runtime) {
159
474
  return remoteGet(runtimePermissionEnv.USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH, {
@@ -240,102 +555,6 @@ function createFormBody(fields) {
240
555
  return body.toString();
241
556
  }
242
557
 
243
- class MiniProgramProjectBindingError extends Error {
244
- code;
245
- constructor(message, code) {
246
- super(message);
247
- this.code = code;
248
- this.name = 'MiniProgramProjectBindingError';
249
- }
250
- }
251
- function findMiniProgramProjectRoot(startDir) {
252
- let current = path.resolve(startDir);
253
- while (true) {
254
- if (fs$1.existsSync(path.join(current, 'package.json'))) {
255
- return current;
256
- }
257
- const parent = path.dirname(current);
258
- if (parent === current) {
259
- throw new MiniProgramProjectBindingError('当前目录或父目录未找到 package.json', 'PACKAGE_JSON_NOT_FOUND');
260
- }
261
- current = parent;
262
- }
263
- }
264
- async function readMiniProgramProjectPackage(startDir) {
265
- const projectRoot = findMiniProgramProjectRoot(startDir);
266
- const packageJsonPath = path.join(projectRoot, 'package.json');
267
- const raw = await fs.readFile(packageJsonPath, 'utf8');
268
- let packageJson;
269
- try {
270
- packageJson = JSON.parse(raw);
271
- }
272
- catch (error) {
273
- const message = error instanceof Error ? error.message : String(error);
274
- throw new MiniProgramProjectBindingError(`package.json 不是合法 JSON:${packageJsonPath}\n${message}`, 'PACKAGE_JSON_INVALID');
275
- }
276
- return {
277
- projectRoot,
278
- packageJsonPath,
279
- packageJson,
280
- raw,
281
- indent: detectJsonIndent(raw),
282
- trailingNewline: raw.endsWith('\r\n') ? '\r\n' : raw.endsWith('\n') ? '\n' : '',
283
- };
284
- }
285
- function readBoundMiniProgramIdFromPackageJson(packageJson) {
286
- const value = packageJson.heybox?.miniProgramId;
287
- return typeof value === 'string' && value.trim() ? value.trim() : undefined;
288
- }
289
- async function readBoundMiniProgramId(startDir) {
290
- return readBoundMiniProgramIdFromPackageJson((await readMiniProgramProjectPackage(startDir)).packageJson);
291
- }
292
- async function requireBoundMiniProgramId(startDir) {
293
- const context = await readMiniProgramProjectPackage(startDir);
294
- const miniProgramId = readBoundMiniProgramIdFromPackageJson(context.packageJson);
295
- if (!miniProgramId) {
296
- throw new MiniProgramProjectBindingError(`当前项目未绑定小程序。请先运行 hb-sdk remote create 或 hb-sdk remote bind <mini-program-id>。\n配置位置:${context.packageJsonPath}#heybox.miniProgramId`, 'MINI_PROGRAM_UNBOUND');
297
- }
298
- return { ...context, miniProgramId };
299
- }
300
- async function writeBoundMiniProgramId(startDir, miniProgramId, options = {}) {
301
- const context = await readMiniProgramProjectPackage(startDir);
302
- const normalizedMiniProgramId = String(miniProgramId).trim();
303
- if (!normalizedMiniProgramId) {
304
- throw new MiniProgramProjectBindingError('miniProgramId 不能为空', 'PACKAGE_JSON_INVALID');
305
- }
306
- const currentMiniProgramId = readBoundMiniProgramIdFromPackageJson(context.packageJson);
307
- if (currentMiniProgramId && currentMiniProgramId !== normalizedMiniProgramId && !options.force) {
308
- throw new MiniProgramProjectBindingError(`当前项目已绑定小程序 ${currentMiniProgramId}。如需覆盖本地绑定,请使用 --force。`, 'MINI_PROGRAM_BINDING_EXISTS');
309
- }
310
- if (currentMiniProgramId === normalizedMiniProgramId) {
311
- return {
312
- ...context,
313
- changed: false,
314
- miniProgramId: normalizedMiniProgramId,
315
- };
316
- }
317
- if (context.packageJson.heybox !== undefined && !isPlainObject(context.packageJson.heybox)) {
318
- throw new MiniProgramProjectBindingError('package.json heybox 字段必须是对象,不能写入 miniProgramId', 'PACKAGE_JSON_HEYBOX_INVALID');
319
- }
320
- context.packageJson.heybox = {
321
- ...(context.packageJson.heybox ?? {}),
322
- miniProgramId: normalizedMiniProgramId,
323
- };
324
- await fs.writeFile(context.packageJsonPath, `${JSON.stringify(context.packageJson, null, context.indent)}${context.trailingNewline || '\n'}`, 'utf8');
325
- return {
326
- ...context,
327
- changed: true,
328
- miniProgramId: normalizedMiniProgramId,
329
- };
330
- }
331
- function detectJsonIndent(raw) {
332
- const match = raw.match(/\n(\s+)"[^"]+":/);
333
- return match?.[1] ?? 2;
334
- }
335
- function isPlainObject(value) {
336
- return Object.prototype.toString.call(value) === '[object Object]';
337
- }
338
-
339
558
  async function resolveRemoteEnvironment(options = {}, runtime = {}) {
340
559
  const env = options.env ?? process.env;
341
560
  const apiBaseUrl = session.resolveHeyboxApiBaseUrl({ ...options, env });
@@ -393,12 +612,15 @@ function isTruthyEnvFlag(value) {
393
612
  }
394
613
 
395
614
  exports.MiniProgramProjectBindingError = MiniProgramProjectBindingError;
615
+ exports.compareBasicInfo = compareBasicInfo;
396
616
  exports.createBrowserDevPermissionState = createBrowserDevPermissionState;
397
617
  exports.createBrowserDevRuntimePermissions = createBrowserDevRuntimePermissions;
398
618
  exports.createLocalDevLaunchToken = createLocalDevLaunchToken;
399
619
  exports.createMobileDevSessionRuntimePermissions = createMobileDevSessionRuntimePermissions;
400
620
  exports.createRemoteApiClientForEnvironment = createRemoteApiClientForEnvironment;
401
621
  exports.getBoundMiniProgram = getBoundMiniProgram;
622
+ exports.migrateBasicInfo = migrateBasicInfo;
623
+ exports.readBasicInfoSource = readBasicInfoSource;
402
624
  exports.readBoundMiniProgramId = readBoundMiniProgramId;
403
625
  exports.readBoundMiniProgramIdFromPackageJson = readBoundMiniProgramIdFromPackageJson;
404
626
  exports.readMiniProgramProjectPackage = readMiniProgramProjectPackage;
@@ -5,7 +5,7 @@ var fs = require('node:fs/promises');
5
5
  var path = require('node:path');
6
6
  var require$$0 = require('fs');
7
7
  var require$$1 = require('path');
8
- var index = require('./index-CDtcZVWv.cjs');
8
+ var index = require('./index-BtwfuoWI.cjs');
9
9
  require('node:module');
10
10
  require('os');
11
11
  require('node:crypto');
@@ -1336,6 +1336,10 @@ async function renderTemplateDirectory(sourceDir, targetDir, data) {
1336
1336
  if (!entry.isFile()) {
1337
1337
  continue;
1338
1338
  }
1339
+ if (!entry.name.endsWith('.ejs')) {
1340
+ await fs.copyFile(sourcePath, targetPath);
1341
+ continue;
1342
+ }
1339
1343
  const content = await fs.readFile(sourcePath, 'utf8');
1340
1344
  const rendered = ejsExports.render(content, data, {
1341
1345
  filename: sourcePath,
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var context = require('./context-DEgmdpSL.cjs');
3
4
  var node_crypto = require('node:crypto');
4
5
  var fs = require('node:fs');
5
6
  var os = require('node:os');
@@ -8,17 +9,16 @@ var net = require('node:net');
8
9
  var promises = require('node:dns/promises');
9
10
  var node_http = require('node:http');
10
11
  var node_module = require('node:module');
11
- var context = require('./context-DpKqGnjp.cjs');
12
- var runtimePermissionEnv = require('./runtime-permission-env-DiTeD75Q.cjs');
12
+ var runtimePermissionEnv = require('./runtime-permission-env-COWketgE.cjs');
13
13
  var undici = require('undici');
14
14
  var fs$1 = require('node:fs/promises');
15
- var index = require('./index-CDtcZVWv.cjs');
15
+ var index = require('./index-BtwfuoWI.cjs');
16
16
  var require$$0$1 = require('crypto');
17
17
  var require$$0 = require('buffer');
18
- var session = require('./session-CIoBspg-.cjs');
18
+ var session = require('./session-6-AApcTZ.cjs');
19
19
  var browser = require('./browser-RAy8e8cV.cjs');
20
- var projectVite = require('./project-vite-CXgdzIYw.cjs');
21
- var version = require('./version-QiVEs76a.cjs');
20
+ var projectVite = require('./project-vite-DZxipd13.cjs');
21
+ var version = require('./version-hHrSPRwW.cjs');
22
22
  require('node:zlib');
23
23
 
24
24
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -1950,8 +1950,8 @@ const DEFAULT_CHANNEL_TRANSPORT = {
1950
1950
  };
1951
1951
  const IPV4_LOOPBACK_ADDRESSES = new net.BlockList();
1952
1952
  IPV4_LOOPBACK_ADDRESSES.addSubnet('127.0.0.0', 8, 'ipv4');
1953
- const { WebSocket, WebSocketServer } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-t14YpHHY.cjs', document.baseURI).href)))('ws');
1954
- const { parse: parseHtml } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-t14YpHHY.cjs', document.baseURI).href)))('parse5');
1953
+ const { WebSocket, WebSocketServer } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-BEXM7cmq.cjs', document.baseURI).href)))('ws');
1954
+ const { parse: parseHtml } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-BEXM7cmq.cjs', document.baseURI).href)))('parse5');
1955
1955
  const BROWSER_DEV_HOST_ROOT_CANDIDATES = [
1956
1956
  path.resolve(__dirname, 'devtools/browser-dev-host'),
1957
1957
  path.resolve(__dirname, '../devtools/browser-dev-host'),
@@ -11049,7 +11049,7 @@ function assertNodeRuntimeCapabilities(ctx, settings) {
11049
11049
  function createNodeRealtimeDriverRegistry() {
11050
11050
  const registry = createRuntimeRealtimeDriverRegistry();
11051
11051
  registry.registerLazy('node-websocket', async () => {
11052
- const mod = await Promise.resolve().then(function () { return require('./index.esm-BrJBaJWS.cjs'); });
11052
+ const mod = await Promise.resolve().then(function () { return require('./index.esm-C86KlVdK.cjs'); });
11053
11053
  return mod.createNodeWebSocketRuntimeDriver();
11054
11054
  });
11055
11055
  return registry;
@@ -15382,6 +15382,7 @@ async function loadDevLaunchContext(options) {
15382
15382
  if (!miniProgramId) {
15383
15383
  throw new Error('hb-sdk dev 需要绑定小程序。请先运行 hb-sdk remote create 或 hb-sdk remote bind <mini-program-id>。');
15384
15384
  }
15385
+ let migratingProfile = false;
15385
15386
  try {
15386
15387
  const { detail, miniProgramId: verifiedMiniProgramId } = await runDevStartupTask(options.logger, '正在读取远端小程序权限', () => loadRemoteDevContextWithTimeout({
15387
15388
  fetchImpl: options.fetchImpl,
@@ -15392,6 +15393,11 @@ async function loadDevLaunchContext(options) {
15392
15393
  }),
15393
15394
  timeoutMs: options.timeoutMs,
15394
15395
  }), '已读取远端小程序权限');
15396
+ migratingProfile = true;
15397
+ const migration = await context.migrateBasicInfo(options.projectRoot, detail, verifiedMiniProgramId, options.fetchImpl);
15398
+ migratingProfile = false;
15399
+ if (migration.changed)
15400
+ options.logger.info('已迁移当前正式发布名称与图片,继续启动开发环境');
15395
15401
  const localPermissions = runtimePermissionEnv.readMiniappPermissionsFromPackageJson(options.projectRoot, runtimePermissionEnv.resolveMiniappSdkVersion());
15396
15402
  if (!localPermissions.declared)
15397
15403
  options.logger.warn(runtimePermissionEnv.getMissingPermissionsWarning());
@@ -15406,6 +15412,8 @@ async function loadDevLaunchContext(options) {
15406
15412
  };
15407
15413
  }
15408
15414
  catch (error) {
15415
+ if (migratingProfile)
15416
+ throw new Error(`版本资料配置失败:${index.readRedactedErrorMessage(error)}。请配置 heybox.name/icon/coverImages 后重试。`, { cause: error });
15409
15417
  if (error instanceof context.MiniProgramProjectBindingError && error.code !== 'MINI_PROGRAM_UNBOUND') {
15410
15418
  throw error;
15411
15419
  }
@@ -3,7 +3,7 @@
3
3
  var fs = require('node:fs/promises');
4
4
  var os = require('node:os');
5
5
  var path = require('node:path');
6
- var index = require('./index-CDtcZVWv.cjs');
6
+ var index = require('./index-BtwfuoWI.cjs');
7
7
  require('node:module');
8
8
  require('node:fs');
9
9
  require('path');