@heybox/hb-sdk 0.8.1-alpha.13 → 0.8.1-alpha.15

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 (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +38 -0
  3. package/dist/cli-chunks/{build-CBPmqCVZ.cjs → build-6nkCiXV0.cjs} +21 -7
  4. package/dist/cli-chunks/{context-CvuPJq1G.cjs → context-DyeSJSqE.cjs} +2 -2
  5. package/dist/cli-chunks/{create-CyMWFYrc.cjs → create-wXBzkgmP.cjs} +1 -1
  6. package/dist/cli-chunks/{dev-BcAPHk9L.cjs → dev-Caq_oO7n.cjs} +28 -10
  7. package/dist/cli-chunks/{doctor-CIwaVrM1.cjs → doctor-n4vISsD-.cjs} +1 -1
  8. package/dist/cli-chunks/{index-E0qh3FUb.cjs → index-BTUkiKjC.cjs} +15 -15
  9. package/dist/cli-chunks/{index-B0KCGR9H.cjs → index-hyB5_A_G.cjs} +2 -2
  10. package/dist/cli-chunks/{index.esm-DTEOIvDp.cjs → index.esm-DU-rsJ3-.cjs} +7 -7
  11. package/dist/cli-chunks/{login-DGvHzH9j.cjs → login-CsXZXtCS.cjs} +2 -2
  12. package/dist/cli-chunks/{project-vite-CRnpRhSb.cjs → project-vite-87sg4Mnk.cjs} +1 -1
  13. package/dist/cli-chunks/{remote-C1_Hs11w.cjs → remote-Cfr3wcGq.cjs} +74 -10
  14. package/dist/cli-chunks/{runtime-permission-env-Bn1ONUDU.cjs → runtime-permission-env-B3wQZF2c.cjs} +128 -1
  15. package/dist/cli-chunks/{session-BvZgS7VI.cjs → session-YWsulrKF.cjs} +1 -1
  16. package/dist/cli-chunks/{skill-BVT6fBpQ.cjs → skill-CS2JoOL8.cjs} +2 -2
  17. package/dist/cli-chunks/{version-CJTEIaOQ.cjs → version-DXjXtCXW.cjs} +1 -1
  18. package/dist/cli.cjs +1 -1
  19. package/dist/devtools/browser-dev-host/assets/{browser-dev-host-DOzYp6JS.js → browser-dev-host-GNgPrL3B.js} +21 -21
  20. package/dist/devtools/browser-dev-host/assets/{desktop-app-launch-DZ_umnQp.js → desktop-app-launch-BUVHeuGC.js} +3 -3
  21. package/dist/devtools/browser-dev-host/assets/{index-DoPg_zh_.js → index-C3EtSrqt.js} +4 -4
  22. package/dist/devtools/browser-dev-host/index.html +2 -2
  23. package/dist/index.cjs.js +1 -1
  24. package/dist/index.esm.js +1 -1
  25. package/dist/miniapp-publish.cjs.js +30 -0
  26. package/dist/miniapp-publish.esm.js +30 -1
  27. package/dist/vite.cjs.js +179 -17
  28. package/dist/vite.esm.js +179 -18
  29. package/package.json +5 -5
  30. package/skill/SKILL.md +17 -14
  31. package/skill/references/api-protocol.md +3 -3
  32. package/skill/references/api-root.md +3 -1
  33. package/skill/references/cli.md +2 -0
  34. package/skill/references/safety-boundaries.md +2 -0
  35. package/skill/skill.json +5 -5
  36. package/types/miniapp-manifest/bindings.d.ts +5 -0
  37. package/types/miniapp-manifest/index.d.ts +1 -0
  38. package/types/miniapp-manifest/node.d.ts +9 -1
  39. package/types/miniapp-manifest/schema.d.ts +39 -0
  40. package/types/miniapp-publish/index.d.ts +1 -0
  41. package/types/vite/index.d.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  本文件随 `@heybox/hb-sdk` npm 包公开分发,只记录外部小程序开发者可观察到的变化。内部集成、实现策略和审查证据不在此维护。
4
4
 
5
+ ## 0.8.1-alpha.15
6
+
7
+ - 支持在 `package.json#heybox.bindAppIds` 中声明当前版本关联的小黑盒 PC 游戏 appid;构建产物写入规范化后的 Manifest 顶层 `bindAppIds`,最多 20 项。
8
+ - `hb-sdk build` 保持离线校验;`hb-sdk remote deploy` 会在上传前校验游戏是否存在且可用,并在正式提审时由服务端再次校验。缺失或空数组表示清空绑定,`--from-version` 继承源版本快照。
9
+ - `hb-sdk remote info` 只读展示本地、线上、新增和移除绑定;绑定不是 Runtime 权限,修改仍只能更新项目配置并重新发布版本。
10
+
11
+ ## 0.8.1-alpha.14
12
+
13
+ - 新增 `package.json#heybox.window` 开发者声明:`defaultWidth` / `defaultHeight` / `minWidth` / `minHeight` / `resizable`,构建时写入 `dist/manifest.json`,所有平台都会写入,由各宿主自行消费。
14
+ - 声明严格校验:空对象、未知字段、`width` / `height` 写法、非正整数、超过 `16384`、`min` 未与对应默认值配对或 `min > default` 全部构建期失败;`x` / `y` 显式报错,**位置不开放**。
15
+ - `resizable` 只约束用户手动拖拽;用户拖拽位置不受该字段限制,窗口位置始终不对开发者开放。
16
+ - `hb-sdk build` 与 `hb-sdk remote deploy` 校验 `dist/manifest.json.window` 与 `package.json#heybox.window` 一致,不一致时要求重新构建。
17
+ - Host/Runtime 集成维护者:页面通道合同升级到 `1.6.0`,新增 `window.getGeometry` 请求与 `window.geometryChanged` 上报,只有用户拖拽来源才允许写入本地记忆。detail 页面与工坊插件须成对发布和回滚。
18
+
5
19
  ## 0.8.1-alpha.13
6
20
 
7
21
  - 审核前预览现在可以准备和启动当前候选版本声明的 Companion;旧版 PC Host 会返回 `COMPANION_PREVIEW_UNSUPPORTED`,需升级客户端后重试。
package/README.md CHANGED
@@ -106,6 +106,19 @@ async function revokeAuthorizationFromUserAction() {
106
106
  }
107
107
  ```
108
108
 
109
+ 需要让已发布版本出现在新 PC 的游戏工作区时,在同一个 `heybox` 对象中按需声明精确的小黑盒 PC 游戏 appid:
110
+
111
+ ```json
112
+ {
113
+ "heybox": {
114
+ "platforms": ["windows", "macos"],
115
+ "bindAppIds": ["730", "3513350"]
116
+ }
117
+ }
118
+ ```
119
+
120
+ `bindAppIds` 只接受规范十进制字符串,最多 20 项,并按数值升序写入 Manifest。它不是权限,构建不联网;游戏真实性在远端预检和提审时校验。字段缺失或 `[]` 都表示清空绑定,`remote deploy --from-version` 则继承源版本快照。PC 展示仍受 `platforms` 过滤。
121
+
109
122
  再启用构建插件:
110
123
 
111
124
  ```ts
@@ -138,6 +151,29 @@ console.log(info.sdk.version);
138
151
 
139
152
  环境信息无需权限,只用于界面适配、兼容降级、埋点和诊断,不能作为鉴权、权限控制或风控依据。完整字段和 Browser Mock 行为见[环境信息](https://docs.xiaoheihe.cn/hb_sdk/guide/environment)。
140
153
 
154
+ ## 窗口尺寸
155
+
156
+ 新 PC 工坊小程序支持在 `package.json#heybox.window` 声明开窗默认尺寸与可缩放性:
157
+
158
+ ```json
159
+ {
160
+ "heybox": {
161
+ "platforms": ["android", "ios", "ohos", "windows", "macos"],
162
+ "window": {
163
+ "defaultWidth": 480,
164
+ "defaultHeight": 900,
165
+ "minWidth": 360,
166
+ "minHeight": 600,
167
+ "resizable": true
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ `window` 整体可选,字段为 `defaultWidth` / `defaultHeight` / `minWidth` / `minHeight` / `resizable`;`{}`、未知字段、`width` / `height` 写法、非正整数、超过 `16384`、`min` 未与对应默认值配对或 `min > default` 都会在构建期明确失败。`minWidth` / `minHeight` 必须与 `defaultWidth` / `defaultHeight` 同时声明。
174
+
175
+ `resizable` 只约束**用户手动拖拽**;**位置不开放**:开发者不能声明或设置窗口位置,位置由用户拖拽产生并由宿主按小程序身份本地记忆。完整语义见[窗口尺寸与可缩放性](https://docs.xiaoheihe.cn/hb_sdk/guide/window-size)。
176
+
141
177
  ## 文件与下载
142
178
 
143
179
  `files` 提供当前小程序私有 sandbox 和由用户选择授予的外部 File/Directory;
@@ -282,6 +318,8 @@ SDK 还提供 `getHandshakeState()` / `onHandshakeStateChange()` 管理持久握
282
318
  - Companion stdio 是原始 `Uint8Array` 且输出为 at-least-once 投递;业务自行定义 framing、去重、重放与恢复。
283
319
  - Browser Dev Host 的 Companion Fake 只用于状态和 UI 开发,不能作为桌面程序可执行、签名、权限或进程监管的真机证据。
284
320
  - 本地真 PC Companion 需要 `package.json#heybox.companions`、本地产物、已绑定项目和 COA `companion` 批准;未配置本地产物时使用 Browser Fake。配置或产物更新后校验并切换不可变快照;已有进程保留原产物,新产物须重新准备与启动。
321
+ - `package.json#heybox.window` 只声明开窗初值与可缩放性;`resizable` 只约束用户拖拽。
322
+ - 窗口位置不开放:开发者不能声明、也不能设置位置;位置由用户拖拽并由宿主按小程序身份本地记忆。
285
323
 
286
324
  私有 Page Channel 适配没有修改 `@heybox/hb-sdk` 公开 API 或 iframe wire;本次 files/download
287
325
  Runtime 能力随 `0.8.0-alpha.11` release family 发布,部署 detail 前必须保证
@@ -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-Bn1ONUDU.cjs');
6
+ var runtimePermissionEnv = require('./runtime-permission-env-B3wQZF2c.cjs');
7
7
  require('node:crypto');
8
8
  require('node:zlib');
9
9
  var runtimeGate = require('./runtime-gate-BWb8QIcX.cjs');
10
- var index = require('./index-E0qh3FUb.cjs');
11
- var projectVite = require('./project-vite-CRnpRhSb.cjs');
12
- require('./version-CJTEIaOQ.cjs');
10
+ var index = require('./index-BTUkiKjC.cjs');
11
+ var projectVite = require('./project-vite-87sg4Mnk.cjs');
12
+ require('./version-DXjXtCXW.cjs');
13
13
  require('node:async_hooks');
14
14
  require('node:module');
15
15
  require('path');
@@ -23,6 +23,9 @@ require('buffer');
23
23
  require('util');
24
24
  require('node:url');
25
25
 
26
+ function areStringArraysEqual(left, right) {
27
+ return left.length === right.length && left.every((value, index) => value === right[index]);
28
+ }
26
29
  async function runBuildCommand(_options = {}, runtime = {}) {
27
30
  const logger = runtime.logger ?? index.createCliLogger();
28
31
  const projectRoot = projectVite.findProjectRoot(runtime.cwd ?? process.cwd());
@@ -31,6 +34,8 @@ async function runBuildCommand(_options = {}, runtime = {}) {
31
34
  const mode = env.HB_SDK_ENV?.trim() || undefined;
32
35
  const skipPlatformCsp = runtimePermissionEnv.shouldSkipMiniappPlatformCspFromEnv(env);
33
36
  const declaredPlatforms = runtimePermissionEnv.readMiniappPlatformsFromPackageJson(projectRoot);
37
+ const declaredWindow = runtimePermissionEnv.readMiniappWindowFromPackageJson(projectRoot);
38
+ const declaredBindAppIds = runtimePermissionEnv.readMiniappBindAppIdsFromPackageJson(projectRoot);
34
39
  runtimePermissionEnv.readMiniappPermissionsFromPackageJson(projectRoot, runtimePermissionEnv.resolveMiniappSdkVersion());
35
40
  await fs.rm(distDir, { force: true, recursive: true });
36
41
  try {
@@ -49,7 +54,7 @@ async function runBuildCommand(_options = {}, runtime = {}) {
49
54
  write: true,
50
55
  },
51
56
  }), { successText: 'Vite 构建完成' });
52
- const result = await validateBuildOutput(projectRoot, logger, declaredPlatforms);
57
+ const result = await validateBuildOutput(projectRoot, logger, declaredPlatforms, declaredWindow, declaredBindAppIds);
53
58
  logger.success(`构建完成: dist/ | manifest version ${result.version} | sdkVersion ${result.sdkVersion} | 待上传文件 ${result.fileCount} 个 | ${formatSize(result.totalBytes)}`);
54
59
  return result;
55
60
  }, env);
@@ -78,7 +83,7 @@ function createFixedBuildOutputPlugin(projectRoot) {
78
83
  },
79
84
  };
80
85
  }
81
- async function validateBuildOutput(projectRoot, logger, declaredPlatforms) {
86
+ async function validateBuildOutput(projectRoot, logger, declaredPlatforms, declaredWindow, declaredBindAppIds) {
82
87
  const distDir = path.join(projectRoot, 'dist');
83
88
  const entryHtmlPath = path.join(distDir, 'index.html');
84
89
  const manifestPath = path.join(distDir, 'manifest.json');
@@ -96,10 +101,18 @@ async function validateBuildOutput(projectRoot, logger, declaredPlatforms) {
96
101
  if (hadBom) {
97
102
  logger.warn('dist/manifest.json 包含 BOM,已自动剥离');
98
103
  }
99
- const { platforms, sdkVersion, version, permissions } = runtimePermissionEnv.validateMiniappManifestForDeploy(manifest);
104
+ const validated = runtimePermissionEnv.validateMiniappManifestForDeploy(manifest);
105
+ const { platforms, sdkVersion, version, permissions, window } = validated;
106
+ const bindAppIds = validated.bindAppIds ?? [];
100
107
  if (!runtimePermissionEnv.areMiniappPlatformsEqual(platforms, declaredPlatforms)) {
101
108
  throw new Error('dist/manifest.json.platforms 与 package.json#heybox.platforms 不一致,请重新 build');
102
109
  }
110
+ if (!runtimePermissionEnv.areMiniappWindowsEqual(window, declaredWindow)) {
111
+ throw new Error('dist/manifest.json.window 与 package.json#heybox.window 不一致,请重新 build');
112
+ }
113
+ if (!areStringArraysEqual(bindAppIds, declaredBindAppIds)) {
114
+ throw new Error('dist/manifest.json.bindAppIds 与 package.json#heybox.bindAppIds 不一致,请重新 build');
115
+ }
103
116
  const allFiles = await walkDistFiles(distDir);
104
117
  const uploadFiles = allFiles.filter((file) => runtimePermissionEnv.shouldUploadDistFile(file.relativePath));
105
118
  const sizeError = runtimePermissionEnv.validateMiniappUploadSizeBudgets(uploadFiles);
@@ -108,6 +121,7 @@ async function validateBuildOutput(projectRoot, logger, declaredPlatforms) {
108
121
  }
109
122
  return {
110
123
  distDir,
124
+ bindAppIds,
111
125
  fileCount: uploadFiles.length,
112
126
  sdkVersion,
113
127
  platforms,
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var runtimePermissionEnv = require('./runtime-permission-env-Bn1ONUDU.cjs');
4
- var session = require('./session-BvZgS7VI.cjs');
3
+ var runtimePermissionEnv = require('./runtime-permission-env-B3wQZF2c.cjs');
4
+ var session = require('./session-YWsulrKF.cjs');
5
5
  var fs$1 = require('node:fs');
6
6
  var fs = require('node:fs/promises');
7
7
  var path = require('node:path');
@@ -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-E0qh3FUb.cjs');
8
+ var index = require('./index-BTUkiKjC.cjs');
9
9
  require('node:module');
10
10
  require('os');
11
11
  require('readline');
@@ -8,17 +8,17 @@ var net = require('node:net');
8
8
  var promises = require('node:dns/promises');
9
9
  var node_http = require('node:http');
10
10
  var node_module = require('node:module');
11
- var context = require('./context-CvuPJq1G.cjs');
12
- var runtimePermissionEnv = require('./runtime-permission-env-Bn1ONUDU.cjs');
11
+ var context = require('./context-DyeSJSqE.cjs');
12
+ var runtimePermissionEnv = require('./runtime-permission-env-B3wQZF2c.cjs');
13
13
  var undici = require('undici');
14
14
  var fs$1 = require('node:fs/promises');
15
- var index = require('./index-E0qh3FUb.cjs');
15
+ var index = require('./index-BTUkiKjC.cjs');
16
16
  var require$$0$1 = require('crypto');
17
17
  var require$$0 = require('buffer');
18
- var session = require('./session-BvZgS7VI.cjs');
18
+ var session = require('./session-YWsulrKF.cjs');
19
19
  var browser = require('./browser-RAy8e8cV.cjs');
20
- var projectVite = require('./project-vite-CRnpRhSb.cjs');
21
- var version = require('./version-CJTEIaOQ.cjs');
20
+ var projectVite = require('./project-vite-87sg4Mnk.cjs');
21
+ var version = require('./version-DXjXtCXW.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-BcAPHk9L.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-BcAPHk9L.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-Caq_oO7n.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-Caq_oO7n.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'),
@@ -1985,6 +1985,7 @@ async function startBrowserDevHostServer(options) {
1985
1985
  miniProgramSupportsDesktop: options.miniProgramSupportsDesktop,
1986
1986
  mobileLaunchRedirect: initialMobileLaunchRedirect,
1987
1987
  nativeAppLaunchUnavailableReason: options.nativeAppLaunchUnavailableReason,
1988
+ miniProgramManifestAccessToken: options.miniProgramManifestAccessToken,
1988
1989
  runtimePermissions: options.runtimePermissions,
1989
1990
  devReadiness: options.devReadiness,
1990
1991
  };
@@ -11048,7 +11049,7 @@ function assertNodeRuntimeCapabilities(ctx, settings) {
11048
11049
  function createNodeRealtimeDriverRegistry() {
11049
11050
  const registry = createRuntimeRealtimeDriverRegistry();
11050
11051
  registry.registerLazy('node-websocket', async () => {
11051
- const mod = await Promise.resolve().then(function () { return require('./index.esm-DTEOIvDp.cjs'); });
11052
+ const mod = await Promise.resolve().then(function () { return require('./index.esm-DU-rsJ3-.cjs'); });
11052
11053
  return mod.createNodeWebSocketRuntimeDriver();
11053
11054
  });
11054
11055
  return registry;
@@ -15146,6 +15147,8 @@ const VITE_LOG_LEVEL = 'warn';
15146
15147
  const COMPANION_DEV_MANIFEST_PATH = '/__hb_sdk__/companion/manifest.json';
15147
15148
  const COMPANION_DEV_MANIFEST_MAX_BYTES = 1024 * 1024;
15148
15149
  const COMPANION_ARTIFACT_ACCESS_TOKEN_ENV = 'HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN';
15150
+ // 本地 PC 调试 manifest 的专用令牌:开窗时扩展还没拿到 LocalDevelopmentBinding,只能靠启动 URI 带来。
15151
+ const MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV = 'HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN';
15149
15152
  const COMPANION_ARTIFACT_ACCESS_TOKEN_HEADER = 'x-hb-sdk-companion-dev-token';
15150
15153
  async function runDevCommand(options, runtime = {}) {
15151
15154
  const logger = runtime.logger ?? index.createCliLogger();
@@ -15169,6 +15172,7 @@ async function runDevCommand(options, runtime = {}) {
15169
15172
  };
15170
15173
  const projectRoot = projectVite.findProjectRoot(runtime.cwd ?? process.cwd());
15171
15174
  const companionArtifactAccessToken = (runtime.randomBytes ?? node_crypto.randomBytes)(32).toString('base64url');
15175
+ const miniProgramManifestAccessToken = (runtime.randomBytes ?? node_crypto.randomBytes)(32).toString('base64url');
15172
15176
  const devSessionId = node_crypto.randomBytes(16).toString('hex');
15173
15177
  const miniProgramPlatforms = runtimePermissionEnv.readMiniappPlatformsFromPackageJson(projectRoot);
15174
15178
  const { context: devLaunchContext, miniProgram } = await loadDevLaunchContext({
@@ -15230,7 +15234,7 @@ async function runDevCommand(options, runtime = {}) {
15230
15234
  return true;
15231
15235
  });
15232
15236
  const vite = await runDevStartupTask(logger, '正在加载项目 Vite', () => projectVite.loadProjectVite(projectRoot), '已加载项目 Vite');
15233
- const appServer = await runDevStartupTask(logger, '正在创建 Vite 开发服务器', () => runtimePermissionEnv.withMiniappPlatformCspEnv(context.shouldSkipMiniappPlatformCsp(devLaunchContext.runtimePermissions), () => withCompanionArtifactAccessTokenEnv(companionArtifactAccessToken, () => withManagedDevOutputEnv(() => vite.createServer(createViteServerOptions(projectRoot, resolvedOptions, launchPlugin)), runtime.env), runtime.env), runtime.env), '已创建 Vite 开发服务器');
15237
+ const appServer = await runDevStartupTask(logger, '正在创建 Vite 开发服务器', () => runtimePermissionEnv.withMiniappPlatformCspEnv(context.shouldSkipMiniappPlatformCsp(devLaunchContext.runtimePermissions), () => withCompanionArtifactAccessTokenEnv(companionArtifactAccessToken, () => withMiniProgramManifestAccessTokenEnv(miniProgramManifestAccessToken, () => withManagedDevOutputEnv(() => vite.createServer(createViteServerOptions(projectRoot, resolvedOptions, launchPlugin)), runtime.env), runtime.env), runtime.env), runtime.env), '已创建 Vite 开发服务器');
15234
15238
  await runDevStartupTask(logger, '正在启动 Vite 开发服务器', () => appServer.listen(appPort), 'Vite 开发服务器已启动');
15235
15239
  const appUrl = await runDevStartupTask(logger, '正在探测小程序入口', () => resolveViteAppUrl(appServer, resolvedOptions, fetchImpl), '已确定小程序入口');
15236
15240
  const buildLanAddresses = () => createLanAddressCandidates({
@@ -15254,6 +15258,7 @@ async function runDevCommand(options, runtime = {}) {
15254
15258
  getPort: getPortImpl,
15255
15259
  defaultLanAddressId: lanAddresses[0]?.id,
15256
15260
  lanAddresses,
15261
+ miniProgramManifestAccessToken,
15257
15262
  lookupHostname: runtime.lookupHostname,
15258
15263
  loginService: runtime.loginService,
15259
15264
  miniProgram,
@@ -15603,6 +15608,19 @@ async function withCompanionArtifactAccessTokenEnv(token, action, env = process.
15603
15608
  env[COMPANION_ARTIFACT_ACCESS_TOKEN_ENV] = previous;
15604
15609
  }
15605
15610
  }
15611
+ async function withMiniProgramManifestAccessTokenEnv(token, action, env = process.env) {
15612
+ const previous = env[MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV];
15613
+ env[MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV] = token;
15614
+ try {
15615
+ return await action();
15616
+ }
15617
+ finally {
15618
+ if (previous === undefined)
15619
+ delete env[MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV];
15620
+ else
15621
+ env[MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV] = previous;
15622
+ }
15623
+ }
15606
15624
  function installShutdownHandlers(closers, processLike) {
15607
15625
  const shutdown = async () => {
15608
15626
  await closeAll(closers);
@@ -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-E0qh3FUb.cjs');
6
+ var index = require('./index-BTUkiKjC.cjs');
7
7
  require('node:module');
8
8
  require('node:fs');
9
9
  require('path');
@@ -234,19 +234,19 @@ function requireArgument () {
234
234
 
235
235
  var command = {};
236
236
 
237
- const require$5 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-E0qh3FUb.cjs', document.baseURI).href)));
237
+ const require$5 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BTUkiKjC.cjs', document.baseURI).href)));
238
238
  function __require$4() { return require$5("node:events"); }
239
239
 
240
- const require$4 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-E0qh3FUb.cjs', document.baseURI).href)));
240
+ const require$4 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BTUkiKjC.cjs', document.baseURI).href)));
241
241
  function __require$3() { return require$4("node:child_process"); }
242
242
 
243
- const require$3 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-E0qh3FUb.cjs', document.baseURI).href)));
243
+ const require$3 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BTUkiKjC.cjs', document.baseURI).href)));
244
244
  function __require$2() { return require$3("node:path"); }
245
245
 
246
- const require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-E0qh3FUb.cjs', document.baseURI).href)));
246
+ const require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BTUkiKjC.cjs', document.baseURI).href)));
247
247
  function __require$1() { return require$2("node:fs"); }
248
248
 
249
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-E0qh3FUb.cjs', document.baseURI).href)));
249
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BTUkiKjC.cjs', document.baseURI).href)));
250
250
  function __require() { return require$1("node:process"); }
251
251
 
252
252
  var help = {};
@@ -13424,7 +13424,7 @@ function getPathname(pathWithQuery) {
13424
13424
  }
13425
13425
 
13426
13426
  const CLI_VERSION_PLACEHOLDER = ['__HB', 'SDK', 'CLI', 'VERSION__'].join('_');
13427
- const BUILT_CLI_VERSION = '0.8.1-alpha.13';
13427
+ const BUILT_CLI_VERSION = '0.8.1-alpha.15';
13428
13428
  const PACKAGE_JSON_CANDIDATES = [
13429
13429
  path.resolve(__dirname, '..', '..', 'package.json'),
13430
13430
  path.resolve(__dirname, '..', 'package.json'),
@@ -13773,39 +13773,39 @@ function createCommandLoggerResolver(options) {
13773
13773
  };
13774
13774
  }
13775
13775
  const defaultClearLoginStatus = async (...args) => {
13776
- const { clearLoginStatus } = await Promise.resolve().then(function () { return require('./login-DGvHzH9j.cjs'); });
13776
+ const { clearLoginStatus } = await Promise.resolve().then(function () { return require('./login-CsXZXtCS.cjs'); });
13777
13777
  return clearLoginStatus(...args);
13778
13778
  };
13779
13779
  const defaultLoginToHeybox = async (...args) => {
13780
- const { loginToHeybox } = await Promise.resolve().then(function () { return require('./login-DGvHzH9j.cjs'); });
13780
+ const { loginToHeybox } = await Promise.resolve().then(function () { return require('./login-CsXZXtCS.cjs'); });
13781
13781
  return loginToHeybox(...args);
13782
13782
  };
13783
13783
  const defaultPrintLoginStatus = async (...args) => {
13784
- const { printLoginStatus } = await Promise.resolve().then(function () { return require('./login-DGvHzH9j.cjs'); });
13784
+ const { printLoginStatus } = await Promise.resolve().then(function () { return require('./login-CsXZXtCS.cjs'); });
13785
13785
  return printLoginStatus(...args);
13786
13786
  };
13787
13787
  const defaultRunBuildCommand = async (...args) => {
13788
- const { runBuildCommand } = await Promise.resolve().then(function () { return require('./build-CBPmqCVZ.cjs'); });
13788
+ const { runBuildCommand } = await Promise.resolve().then(function () { return require('./build-6nkCiXV0.cjs'); });
13789
13789
  return runBuildCommand(...args);
13790
13790
  };
13791
13791
  const defaultRunCreateCommand = async (...args) => {
13792
- const { runCreateCommand } = await Promise.resolve().then(function () { return require('./create-CyMWFYrc.cjs'); });
13792
+ const { runCreateCommand } = await Promise.resolve().then(function () { return require('./create-wXBzkgmP.cjs'); });
13793
13793
  return runCreateCommand(...args);
13794
13794
  };
13795
13795
  const defaultRunDevCommand = async (...args) => {
13796
- const { runDevCommand } = await Promise.resolve().then(function () { return require('./dev-BcAPHk9L.cjs'); }).then(function (n) { return n.dev; });
13796
+ const { runDevCommand } = await Promise.resolve().then(function () { return require('./dev-Caq_oO7n.cjs'); }).then(function (n) { return n.dev; });
13797
13797
  return runDevCommand(...args);
13798
13798
  };
13799
13799
  const defaultRunDoctorCommand = async (...args) => {
13800
- const { runDoctorCommand } = await Promise.resolve().then(function () { return require('./doctor-CIwaVrM1.cjs'); });
13800
+ const { runDoctorCommand } = await Promise.resolve().then(function () { return require('./doctor-n4vISsD-.cjs'); });
13801
13801
  return runDoctorCommand(...args);
13802
13802
  };
13803
13803
  const defaultRunSkillInstallCommand = async (...args) => {
13804
- const { runSkillInstallCommand } = await Promise.resolve().then(function () { return require('./skill-BVT6fBpQ.cjs'); });
13804
+ const { runSkillInstallCommand } = await Promise.resolve().then(function () { return require('./skill-CS2JoOL8.cjs'); });
13805
13805
  return runSkillInstallCommand(...args);
13806
13806
  };
13807
13807
  const defaultRunRemoteCommand = async (...args) => {
13808
- const { runRemoteCommand } = await Promise.resolve().then(function () { return require('./remote-C1_Hs11w.cjs'); });
13808
+ const { runRemoteCommand } = await Promise.resolve().then(function () { return require('./remote-Cfr3wcGq.cjs'); });
13809
13809
  return runRemoteCommand(...args);
13810
13810
  };
13811
13811
  function resolveStandaloneLogger(options, verbose) {
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var index$2 = require('./index-E0qh3FUb.cjs');
3
+ var index$2 = require('./index-BTUkiKjC.cjs');
4
4
  var require$$0$3 = require('fs');
5
5
  var require$$0$2 = require('crypto');
6
6
  var require$$1$2 = require('path');
7
7
  var require$$0$5 = require('assert');
8
8
  var require$$0$4 = require('events');
9
9
  var require$$1$1 = require('util');
10
- var runtimePermissionEnv = require('./runtime-permission-env-Bn1ONUDU.cjs');
10
+ var runtimePermissionEnv = require('./runtime-permission-env-B3wQZF2c.cjs');
11
11
  var require$$0$7 = require('net');
12
12
  var require$$0$6 = require('url');
13
13
  var require$$2$1 = require('http');
@@ -11,8 +11,8 @@ var require$$0$4 = require('url');
11
11
  var require$$0 = require('zlib');
12
12
  var require$$0$1 = require('buffer');
13
13
  var require$$1 = require('util');
14
- var index = require('./index-E0qh3FUb.cjs');
15
- var dev = require('./dev-BcAPHk9L.cjs');
14
+ var index = require('./index-BTUkiKjC.cjs');
15
+ var dev = require('./dev-Caq_oO7n.cjs');
16
16
  require('node:module');
17
17
  require('node:fs');
18
18
  require('node:fs/promises');
@@ -27,11 +27,11 @@ require('node:os');
27
27
  require('node:net');
28
28
  require('node:dns/promises');
29
29
  require('node:http');
30
- require('./context-CvuPJq1G.cjs');
31
- require('./runtime-permission-env-Bn1ONUDU.cjs');
32
- require('./version-CJTEIaOQ.cjs');
30
+ require('./context-DyeSJSqE.cjs');
31
+ require('./runtime-permission-env-B3wQZF2c.cjs');
32
+ require('./version-DXjXtCXW.cjs');
33
33
  require('node:async_hooks');
34
- require('./session-BvZgS7VI.cjs');
34
+ require('./session-YWsulrKF.cjs');
35
35
  require('fs');
36
36
  require('constants');
37
37
  require('undici');
@@ -41,7 +41,7 @@ require('node:buffer');
41
41
  require('node:url');
42
42
  require('node:util');
43
43
  require('node:child_process');
44
- require('./project-vite-CRnpRhSb.cjs');
44
+ require('./project-vite-87sg4Mnk.cjs');
45
45
  require('node:zlib');
46
46
 
47
47
  var bufferUtil = {exports: {}};
@@ -3,9 +3,9 @@
3
3
  var promises = require('node:readline/promises');
4
4
  var node_crypto = require('node:crypto');
5
5
  var node_http = require('node:http');
6
- var session = require('./session-BvZgS7VI.cjs');
6
+ var session = require('./session-YWsulrKF.cjs');
7
7
  var browser = require('./browser-RAy8e8cV.cjs');
8
- var index = require('./index-E0qh3FUb.cjs');
8
+ var index = require('./index-BTUkiKjC.cjs');
9
9
  require('node:path');
10
10
  require('fs');
11
11
  require('constants');
@@ -5,7 +5,7 @@ var fs$1 = require('node:fs/promises');
5
5
  var node_module = require('node:module');
6
6
  var path = require('node:path');
7
7
  var node_url = require('node:url');
8
- var index = require('./index-E0qh3FUb.cjs');
8
+ var index = require('./index-BTUkiKjC.cjs');
9
9
 
10
10
  function findProjectRoot(startDir) {
11
11
  let current = path.resolve(startDir);
@@ -1,17 +1,17 @@
1
1
  'use strict';
2
2
 
3
3
  var promises = require('node:readline/promises');
4
- var index = require('./index-E0qh3FUb.cjs');
5
- var session = require('./session-BvZgS7VI.cjs');
4
+ var index = require('./index-BTUkiKjC.cjs');
5
+ var session = require('./session-YWsulrKF.cjs');
6
6
  var childProcess = require('node:child_process');
7
7
  var fs = require('node:fs');
8
8
  var fs$1 = require('node:fs/promises');
9
9
  var path = require('node:path');
10
- var runtimePermissionEnv = require('./runtime-permission-env-Bn1ONUDU.cjs');
10
+ var runtimePermissionEnv = require('./runtime-permission-env-B3wQZF2c.cjs');
11
11
  var node_crypto = require('node:crypto');
12
12
  require('node:zlib');
13
13
  var runtimeGate = require('./runtime-gate-BWb8QIcX.cjs');
14
- var context = require('./context-CvuPJq1G.cjs');
14
+ var context = require('./context-DyeSJSqE.cjs');
15
15
  require('node:module');
16
16
  require('path');
17
17
  require('os');
@@ -24,7 +24,7 @@ require('buffer');
24
24
  require('util');
25
25
  require('fs');
26
26
  require('constants');
27
- require('./version-CJTEIaOQ.cjs');
27
+ require('./version-DXjXtCXW.cjs');
28
28
  require('node:async_hooks');
29
29
 
30
30
  async function getCDNUploadInfo(options, runtime = {}) {
@@ -69,6 +69,7 @@ async function precheckUserMiniprogramVersion(options, runtime = {}) {
69
69
  // 展示资料由服务端沿用 last approved 或注入默认名/默认图;CLI 不传 name/icon/cover。
70
70
  ...(options.version ? { version: options.version } : {}),
71
71
  ...(options.source_version ? { source_version: options.source_version } : {}),
72
+ ...(options.manifest ? { manifest: JSON.stringify(options.manifest) } : {}),
72
73
  release_note: options.releaseNote,
73
74
  auto_publish: String(options.autoPublish),
74
75
  }, runtime);
@@ -274,7 +275,7 @@ function createDefaultCosClient(uploadToken, COS) {
274
275
  };
275
276
  }
276
277
  async function loadCosConstructor() {
277
- const cosModule = await Promise.resolve().then(function () { return require('./index-B0KCGR9H.cjs'); }).then(function (n) { return n.index; });
278
+ const cosModule = await Promise.resolve().then(function () { return require('./index-hyB5_A_G.cjs'); }).then(function (n) { return n.index; });
278
279
  return cosModule.default;
279
280
  }
280
281
  function formatSize(bytes) {
@@ -516,6 +517,8 @@ async function runDeployCommand(options, runtime = {}) {
516
517
  }
517
518
  const fromVersion = options.fromVersion?.trim();
518
519
  const declaredPlatforms = fromVersion ? undefined : runtimePermissionEnv.readMiniappPlatformsFromPackageJson(projectRoot);
520
+ const declaredWindow = fromVersion ? undefined : runtimePermissionEnv.readMiniappWindowFromPackageJson(projectRoot);
521
+ const declaredBindAppIds = fromVersion ? undefined : runtimePermissionEnv.readMiniappBindAppIdsFromPackageJson(projectRoot);
519
522
  const releaseNote = await resolveDeployReleaseNote(options, runtime);
520
523
  const session$1 = await logger.task('正在校验 Heybox 登录态', () => (runtime.requireAuthSession ?? session.requireHeyboxAuthSession)({ loginBaseUrl }), { successText: 'Heybox 登录态有效' });
521
524
  logger.debug(`Heybox API: ${apiBaseUrl}`);
@@ -593,9 +596,23 @@ async function runDeployCommand(options, runtime = {}) {
593
596
  if (!declaredPlatforms || !runtimePermissionEnv.areMiniappPlatformsEqual(parsedManifest.platforms, declaredPlatforms)) {
594
597
  throw new Error('dist/manifest.json.platforms 与 package.json#heybox.platforms 不一致,请重新 build 后再 deploy');
595
598
  }
599
+ if (!runtimePermissionEnv.areMiniappWindowsEqual(parsedManifest.window, declaredWindow)) {
600
+ throw new Error('dist/manifest.json.window 与 package.json#heybox.window 不一致,请重新 build 后再 deploy');
601
+ }
596
602
  if (version !== precheckVersion) {
597
603
  throw new Error(`dist/manifest.json.version (${version}) 与预检版本 (${precheckVersion}) 不一致,请重新 build 后再 deploy`);
598
604
  }
605
+ if (declaredBindAppIds && !areStringArraysEqual(parsedManifest.bindAppIds, declaredBindAppIds)) {
606
+ throw new Error('dist/manifest.json.bindAppIds 与 package.json#heybox.bindAppIds 不一致,请重新 build 后再 deploy');
607
+ }
608
+ await runVersionPrecheck({
609
+ session: session$1,
610
+ miniProgramId,
611
+ version,
612
+ manifest,
613
+ releaseNote,
614
+ autoPublish,
615
+ }, runtime, apiBaseUrl, projectRoot, logger);
599
616
  const uploadFiles = await logger.task('正在校验 dist 上传文件', async () => {
600
617
  const allFiles = await walkDistFiles(distDir);
601
618
  const blocked = allFiles.find((entry) => runtimePermissionEnv.relativePathContainsNodeModulesSegment(entry.relativePath));
@@ -733,6 +750,9 @@ function findProjectRoot(startDir) {
733
750
  async function readPackageJson(projectRoot) {
734
751
  return JSON.parse(await fs$1.readFile(path.join(projectRoot, 'package.json'), 'utf8'));
735
752
  }
753
+ function areStringArraysEqual(left, right) {
754
+ return left.length === right.length && left.every((value, index) => value === right[index]);
755
+ }
736
756
  async function readDeployManifest(projectRoot, logger) {
737
757
  const distDir = path.join(projectRoot, 'dist');
738
758
  const manifestPath = path.join(distDir, 'manifest.json');
@@ -752,11 +772,18 @@ async function readDeployManifest(projectRoot, logger) {
752
772
  logger.warn('dist/manifest.json 包含 BOM,已自动剥离');
753
773
  }
754
774
  const validated = runtimePermissionEnv.validateMiniappManifestForDeploy(manifest);
775
+ const bindAppIds = validated.bindAppIds ?? [];
755
776
  return {
756
- manifest: { ...manifest, platforms: validated.platforms },
777
+ manifest: {
778
+ ...manifest,
779
+ platforms: validated.platforms,
780
+ ...(bindAppIds.length === 0 ? {} : { bindAppIds }),
781
+ },
782
+ bindAppIds,
757
783
  sdkVersion: validated.sdkVersion,
758
784
  version: validated.version,
759
785
  platforms: validated.platforms,
786
+ ...(validated.window === undefined ? {} : { window: validated.window }),
760
787
  };
761
788
  }
762
789
  function validateVersionForDeploy(version, sourceLabel) {
@@ -1261,9 +1288,18 @@ async function remoteInfo(options, context, runtime) {
1261
1288
  const detail = await context.api.getMiniProgramDetail(miniProgramId);
1262
1289
  const localPermissions = runtimePermissionEnv.readMiniappPermissionsFromPackageJson(context.packageJson.projectRoot, runtimePermissionEnv.resolveMiniappSdkVersion());
1263
1290
  const permissionViews = runtimePermissionEnv.composeMiniappRuntimePermissions(localPermissions, detail.runtime_permissions).views;
1264
- const output = { changed: false, miniProgramId, detail: toCamelCaseDeep(detail), permissions: permissionViews };
1291
+ const localBindAppIds = runtimePermissionEnv.readMiniappBindAppIdsFromPackageJson(context.packageJson.projectRoot);
1292
+ const currentBindApps = normalizeRemoteBoundApps(detail.bind_apps);
1293
+ const currentByID = new Map(currentBindApps.map((item) => [item.appid, item]));
1294
+ const local = localBindAppIds.map((appid) => currentByID.get(appid) ?? { appid });
1295
+ const currentIDs = new Set(currentBindApps.map((item) => item.appid));
1296
+ const added = local.filter((item) => !currentIDs.has(item.appid));
1297
+ const localIDs = new Set(localBindAppIds);
1298
+ const removed = currentBindApps.filter((item) => !localIDs.has(item.appid));
1299
+ const bindings = { local, current: currentBindApps, added, removed };
1300
+ const output = { changed: false, miniProgramId, detail: toCamelCaseDeep(detail), permissions: permissionViews, bindings };
1265
1301
  if (!options.json) {
1266
- printMiniProgramDetail(context.logger, detail, permissionViews);
1302
+ printMiniProgramDetail(context.logger, detail, permissionViews, bindings);
1267
1303
  }
1268
1304
  return finishRemoteCommand(options, runtime, output);
1269
1305
  }
@@ -1762,7 +1798,7 @@ async function promptConfirm(message, runtime) {
1762
1798
  rl.close();
1763
1799
  }
1764
1800
  }
1765
- function printMiniProgramDetail(logger, detail, permissionViews) {
1801
+ function printMiniProgramDetail(logger, detail, permissionViews, bindings) {
1766
1802
  logger.info(`Mini-program: ${detail.mini_program_id ?? '--'}`);
1767
1803
  logger.info(`Name: ${detail.name ?? '--'}`);
1768
1804
  logger.info(`Status: ${detail.status ?? '--'}`);
@@ -1781,6 +1817,34 @@ function printMiniProgramDetail(logger, detail, permissionViews) {
1781
1817
  : '';
1782
1818
  logger.info(` ${view.key}: 声明=${view.declared ? '是' : '否'}${approval} 最终=${view.effective ? '可用' : '不可用'}${configs}`);
1783
1819
  }
1820
+ if (bindings) {
1821
+ logger.info('关联游戏(本地/线上/新增/移除):');
1822
+ logger.info(` local=${formatBoundApps(bindings.local)}`);
1823
+ logger.info(` current=${formatBoundApps(bindings.current)}`);
1824
+ logger.info(` added=${formatBoundApps(bindings.added)}`);
1825
+ logger.info(` removed=${formatBoundApps(bindings.removed)}`);
1826
+ }
1827
+ }
1828
+ function normalizeRemoteBoundApps(value) {
1829
+ if (!Array.isArray(value))
1830
+ return [];
1831
+ return value.flatMap((item) => {
1832
+ if (!item || typeof item !== 'object')
1833
+ return [];
1834
+ const source = item;
1835
+ if (typeof source.appid !== 'string' || !/^[1-9]\d*$/.test(source.appid))
1836
+ return [];
1837
+ return [
1838
+ {
1839
+ appid: source.appid,
1840
+ ...(typeof source.name === 'string' && source.name.trim() ? { name: source.name.trim() } : {}),
1841
+ ...(typeof source.icon_url === 'string' && source.icon_url.trim() ? { icon_url: source.icon_url.trim() } : {}),
1842
+ },
1843
+ ];
1844
+ });
1845
+ }
1846
+ function formatBoundApps(items) {
1847
+ return items.length === 0 ? '--' : items.map((item) => (item.name ? `${item.appid}(${item.name})` : item.appid)).join(', ');
1784
1848
  }
1785
1849
  function printDeveloperEntities(logger, entities) {
1786
1850
  for (const entity of entities) {