@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/dist/vite.esm.js CHANGED
@@ -8,7 +8,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
8
8
  /** 构建时替换为当前发布包的实际版本。 */
9
9
  const HB_SDK_VERSION = typeof undefined === 'string'
10
10
  ? undefined
11
- : '0.8.1-alpha.13';
11
+ : '0.8.1-alpha.15';
12
12
 
13
13
  /**
14
14
  * iframe 与调试台页面的开发期 console 捕获转发。
@@ -3113,6 +3113,35 @@ function requireSemver () {
3113
3113
 
3114
3114
  var semverExports = requireSemver();
3115
3115
 
3116
+ const MINIAPP_BIND_APP_IDS_MAX_COUNT = 20;
3117
+ function parseMiniappBindAppIds(value, options = {}) {
3118
+ const sourceLabel = options.sourceLabel ?? 'package.json#heybox.bindAppIds';
3119
+ if (value === undefined)
3120
+ return Object.freeze([]);
3121
+ if (!Array.isArray(value))
3122
+ throw new Error(`${sourceLabel} 必须是字符串数组`);
3123
+ if (value.length > MINIAPP_BIND_APP_IDS_MAX_COUNT) {
3124
+ throw new Error(`${sourceLabel} 最多包含 ${MINIAPP_BIND_APP_IDS_MAX_COUNT} 个游戏 appid`);
3125
+ }
3126
+ const seen = new Set();
3127
+ const appIds = [];
3128
+ for (const item of value) {
3129
+ if (typeof item !== 'string' || !/^[1-9]\d*$/.test(item)) {
3130
+ throw new Error(`${sourceLabel} 包含非法游戏 appid:${String(item)}`);
3131
+ }
3132
+ const numeric = Number(item);
3133
+ if (!Number.isSafeInteger(numeric) || numeric > 2_147_483_647) {
3134
+ throw new Error(`${sourceLabel} 包含超出正 int32 范围的游戏 appid:${item}`);
3135
+ }
3136
+ if (seen.has(item))
3137
+ throw new Error(`${sourceLabel} 包含重复游戏 appid:${item}`);
3138
+ seen.add(item);
3139
+ appIds.push(item);
3140
+ }
3141
+ appIds.sort((left, right) => Number(left) - Number(right));
3142
+ return Object.freeze(appIds);
3143
+ }
3144
+
3116
3145
  const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
3117
3146
  const MINI_PROGRAM_MESSAGE_VERSION = 2;
3118
3147
  const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
@@ -3278,7 +3307,7 @@ function parseMiniappPermissions(value, options) {
3278
3307
  }
3279
3308
  return { declared: false };
3280
3309
  }
3281
- if (!isRecord$2(value)) {
3310
+ if (!isRecord$3(value)) {
3282
3311
  throw new Error(`${sourceLabel} 必须是 JSON 对象`);
3283
3312
  }
3284
3313
  const knownKeys = new Set(MINIAPP_PERMISSION_KEYS);
@@ -3292,7 +3321,7 @@ function parseMiniappPermissions(value, options) {
3292
3321
  continue;
3293
3322
  const declaration = value[key];
3294
3323
  const itemLabel = `${sourceLabel}.${key}`;
3295
- if (!isRecord$2(declaration))
3324
+ if (!isRecord$3(declaration))
3296
3325
  throw new Error(`${itemLabel} 必须是 JSON 对象`);
3297
3326
  const definition = MINI_PROGRAM_PERMISSION_CATALOG.find((item) => item.key === key);
3298
3327
  if (!definition)
@@ -3326,11 +3355,15 @@ function requiresExplicitPermissions(sdkVersion) {
3326
3355
  function getMissingPermissionsWarning(sourceLabel = 'package.json#heybox.permissions') {
3327
3356
  return `${sourceLabel} 尚未声明。当前按 0.8 兼容行为构建;0.9.0 起将拒绝构建,请尽快补充权限声明(无权限需求时配置 {})。`;
3328
3357
  }
3329
- function isRecord$2(value) {
3358
+ function isRecord$3(value) {
3330
3359
  return typeof value === 'object' && value !== null && !Array.isArray(value);
3331
3360
  }
3332
3361
 
3333
3362
  const MINIAPP_PLATFORM_VALUES = ['android', 'ios', 'ohos', 'windows', 'macos', 'linux'];
3363
+ /** 窗口尺寸字段的脏值兜底上限;真正的上限是宿主侧显示器 workArea。 */
3364
+ const MINIAPP_WINDOW_SIZE_MAX = 16_384;
3365
+ const MINIAPP_WINDOW_NUMBER_FIELDS = ['defaultWidth', 'defaultHeight', 'minWidth', 'minHeight'];
3366
+ const MINIAPP_WINDOW_ALLOWED_FIELDS = [...MINIAPP_WINDOW_NUMBER_FIELDS, 'resizable'];
3334
3367
  function validateMiniappPackageVersionForBuild(version) {
3335
3368
  return validateMiniappManifestVersion(version, 'package.json.version');
3336
3369
  }
@@ -3343,12 +3376,16 @@ function validateMiniappManifestVersion(version, sourceLabel = 'manifest.version
3343
3376
  }
3344
3377
  function renderMiniappManifest(manifest) {
3345
3378
  const platforms = validateMiniappPlatforms(manifest.platforms, 'manifest.platforms');
3379
+ const window = validateMiniappWindow(manifest.window, 'manifest.window');
3380
+ const bindAppIds = parseMiniappBindAppIds(manifest.bindAppIds, { sourceLabel: 'manifest.bindAppIds' });
3346
3381
  const output = {
3347
3382
  version: manifest.version,
3348
3383
  sdkVersion: manifest.sdkVersion,
3349
3384
  platforms,
3350
3385
  ...(manifest.permissions === undefined ? {} : { permissions: manifest.permissions }),
3351
3386
  ...(manifest.companions === undefined ? {} : { companions: manifest.companions }),
3387
+ ...(window === undefined ? {} : { window }),
3388
+ ...(bindAppIds.length === 0 ? {} : { bindAppIds }),
3352
3389
  };
3353
3390
  return `${JSON.stringify(output, null, 2)}\n`;
3354
3391
  }
@@ -3369,6 +3406,70 @@ function validateMiniappPlatforms(platforms, sourceLabel = 'platforms') {
3369
3406
  }
3370
3407
  return Object.freeze(MINIAPP_PLATFORM_VALUES.filter((platform) => seen.has(platform)));
3371
3408
  }
3409
+ /**
3410
+ * 校验并归一化 `package.json#heybox.window` / `manifest.window` 声明。
3411
+ *
3412
+ * @remarks
3413
+ * 与 `heybox.permissions` 同样的严格风格:未知字段报错,不做静默忽略。
3414
+ * `width` / `height` 显式给出改名提示;`x` / `y` 按未知字段显式报错(`INV-NO-DEV-POSITION`)。
3415
+ * 未声明时返回 `undefined`,由调用方决定不写入产物。
3416
+ * @param value - 待校验的原始声明(不可信输入)。
3417
+ * @param sourceLabel - 报错前缀,默认指向开发者声明路径。
3418
+ * @throws 当声明不是非空对象、含未知字段、数值/布尔类型不合法或 min/default 不配对时抛出。
3419
+ */
3420
+ function validateMiniappWindow(value, sourceLabel = 'package.json#heybox.window') {
3421
+ if (value === undefined)
3422
+ return undefined;
3423
+ if (!isRecord$2(value) || Object.keys(value).length === 0) {
3424
+ throw new Error(`${sourceLabel} 至少需要声明 defaultWidth/defaultHeight/minWidth/minHeight/resizable 之一`);
3425
+ }
3426
+ if (Object.prototype.hasOwnProperty.call(value, 'width') || Object.prototype.hasOwnProperty.call(value, 'height')) {
3427
+ throw new Error(`${sourceLabel} 请使用 defaultWidth/defaultHeight,而不是 width/height`);
3428
+ }
3429
+ for (const key of Object.keys(value)) {
3430
+ if (!MINIAPP_WINDOW_ALLOWED_FIELDS.includes(key)) {
3431
+ throw new Error(`${sourceLabel} 包含未知字段:${key}`);
3432
+ }
3433
+ }
3434
+ const window = {};
3435
+ for (const field of MINIAPP_WINDOW_NUMBER_FIELDS) {
3436
+ if (!Object.prototype.hasOwnProperty.call(value, field))
3437
+ continue;
3438
+ const size = value[field];
3439
+ if (typeof size !== 'number' || !Number.isSafeInteger(size) || size <= 0) {
3440
+ throw new Error(`${sourceLabel}.${field} 必须是正整数`);
3441
+ }
3442
+ if (size > MINIAPP_WINDOW_SIZE_MAX) {
3443
+ throw new Error(`${sourceLabel}.${field} 不能大于 ${MINIAPP_WINDOW_SIZE_MAX}`);
3444
+ }
3445
+ window[field] = size;
3446
+ }
3447
+ if (Object.prototype.hasOwnProperty.call(value, 'resizable')) {
3448
+ if (typeof value.resizable !== 'boolean') {
3449
+ throw new Error(`${sourceLabel}.resizable 必须是 boolean`);
3450
+ }
3451
+ window.resizable = value.resizable;
3452
+ }
3453
+ for (const [minimum, fallback] of [
3454
+ ['minWidth', 'defaultWidth'],
3455
+ ['minHeight', 'defaultHeight'],
3456
+ ]) {
3457
+ const minimumValue = window[minimum];
3458
+ if (minimumValue === undefined)
3459
+ continue;
3460
+ const fallbackValue = window[fallback];
3461
+ if (fallbackValue === undefined) {
3462
+ throw new Error(`${sourceLabel}.${minimum} 必须与 ${fallback} 同时声明`);
3463
+ }
3464
+ if (minimumValue > fallbackValue) {
3465
+ throw new Error(`${sourceLabel}.${minimum} 必须小于等于 ${fallback}`);
3466
+ }
3467
+ }
3468
+ return window;
3469
+ }
3470
+ function isRecord$2(value) {
3471
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3472
+ }
3372
3473
  function getMiniappManifestVersionError(version) {
3373
3474
  if (typeof version !== 'string' || version.trim() === '') {
3374
3475
  return `必须是合法 SemVer:${String(version)}`;
@@ -3426,6 +3527,17 @@ function readMiniappPlatformsFromPackageJson(root) {
3426
3527
  const heybox = readMiniappHeyboxConfig(packageJson);
3427
3528
  return validateMiniappPlatforms(heybox?.platforms, 'package.json#heybox.platforms');
3428
3529
  }
3530
+ /**
3531
+ * 读取并校验 `package.json#heybox.window`。
3532
+ *
3533
+ * @returns 合法声明;未声明时返回 `undefined`,调用方不得向 manifest 写入空对象或 `null`。
3534
+ * @throws 当声明违反 {@link validateMiniappWindow} 的规则时抛出,报错前缀为 `package.json#heybox.window`。
3535
+ */
3536
+ function readMiniappWindowFromPackageJson(root) {
3537
+ const { packageJson } = readMiniappPackageJson(root, false);
3538
+ const heybox = readMiniappHeyboxConfig(packageJson);
3539
+ return validateMiniappWindow(heybox?.window, 'package.json#heybox.window');
3540
+ }
3429
3541
  function readMiniappPermissionsFromPackageJson(root, sdkVersion) {
3430
3542
  const { packageJson } = readMiniappPackageJson(root, false);
3431
3543
  const heybox = readMiniappHeyboxConfig(packageJson);
@@ -3435,6 +3547,11 @@ function readMiniappPermissionsFromPackageJson(root, sdkVersion) {
3435
3547
  sdkVersion,
3436
3548
  });
3437
3549
  }
3550
+ function readMiniappBindAppIdsFromPackageJson(root) {
3551
+ const { packageJson } = readMiniappPackageJson(root, false);
3552
+ const heybox = readMiniappHeyboxConfig(packageJson);
3553
+ return parseMiniappBindAppIds(heybox?.bindAppIds);
3554
+ }
3438
3555
  function readMiniappPackageJson(root, requireVersion) {
3439
3556
  const packageJsonPath = findNearestPackageJsonPath(root);
3440
3557
  let content;
@@ -12890,11 +13007,23 @@ const sdkVersionPlaceholder = ['__HB_SDK', 'VERSION__'].join('_');
12890
13007
  const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
12891
13008
  const miniDevLoggingConstant = ['__HB_SDK_DEV', 'LOGGING__'].join('_');
12892
13009
  const DEV_MANIFEST_PATH = '/__hb_sdk__/companion/manifest.json';
13010
+ /**
13011
+ * 本地 PC 调试开窗时的 manifest 入口。
13012
+ *
13013
+ * 开窗发生在扩展拿到 `LocalDevelopmentBinding` 之前,所以不能用 Companion 会话令牌守护;
13014
+ * 该入口改由 CLI 随本地调试启动上下文(`mini_url` query)下发的专用令牌守护,
13015
+ * 只回最新快照,不参与 Companion 会话的 digest 钉扎。
13016
+ */
13017
+ const DEV_MINI_PROGRAM_MANIFEST_PATH = '/__hb_sdk__/mini-program/manifest.json';
12893
13018
  const DEV_TOKEN_HEADER = 'x-hb-sdk-companion-dev-token';
13019
+ const DEV_MINI_PROGRAM_MANIFEST_TOKEN_HEADER = 'x-hb-sdk-mini-program-manifest-token';
12894
13020
  const DEV_DIGEST_HEADER = 'x-hb-sdk-companion-manifest-sha256';
12895
13021
  const DEV_ERROR_MESSAGE = 'Companion 开发资源暂时不可用';
13022
+ /** 与 Companion manifest 一致的单响应上限,避免本地调试入口被大体积声明撑爆。 */
13023
+ const DEV_MINI_PROGRAM_MANIFEST_MAX_BYTES = 1024 * 1024;
12896
13024
  const DEV_COMPANION_PATH_PATTERN = /^\/__hb_sdk__\/companions\/([a-z][a-z0-9-]{0,31})\/(windows-x64|macos-arm64)\.zip$/;
12897
13025
  const HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV = 'HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN';
13026
+ const HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV = 'HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN';
12898
13027
  const devSnapshotStoreKey = Symbol.for('@heybox/hb-sdk/dev-companion-snapshots');
12899
13028
  const devSnapshotRegistry = globalThis;
12900
13029
  if (!devSnapshotRegistry[devSnapshotStoreKey]) {
@@ -12911,7 +13040,8 @@ function miniappManifest() {
12911
13040
  const includeCompanions = process.env.HB_SDK_SKIP_COMPANIONS !== '1';
12912
13041
  const deviceLogs = new DeviceLogStore();
12913
13042
  const skipPlatformCsp = shouldSkipMiniappPlatformCspFromEnv();
12914
- let companionArtifactAccessToken = parseCompanionArtifactAccessToken(process.env[HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV]);
13043
+ let companionArtifactAccessToken = parseDevAccessToken(process.env[HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV]);
13044
+ let miniProgramManifestAccessToken = parseDevAccessToken(process.env[HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV]);
12915
13045
  let root = process.cwd();
12916
13046
  let outDir = 'dist';
12917
13047
  let command = 'build';
@@ -12959,8 +13089,16 @@ function miniappManifest() {
12959
13089
  registerLogs();
12960
13090
  let store = devSnapshotStores.get(root);
12961
13091
  companionArtifactAccessToken ??= store?.token;
12962
- if (!store || store.token !== companionArtifactAccessToken) {
12963
- store = { token: companionArtifactAccessToken ?? '', snapshots: new Map() };
13092
+ // Companion 令牌同因:配置重载时 env 已不在,只能从同一服务的注册表恢复。
13093
+ miniProgramManifestAccessToken ??= store?.manifestToken;
13094
+ if (!store ||
13095
+ store.token !== companionArtifactAccessToken ||
13096
+ store.manifestToken !== miniProgramManifestAccessToken) {
13097
+ store = {
13098
+ token: companionArtifactAccessToken ?? '',
13099
+ ...(miniProgramManifestAccessToken ? { manifestToken: miniProgramManifestAccessToken } : {}),
13100
+ snapshots: new Map(),
13101
+ };
12964
13102
  devSnapshotStores.set(root, store);
12965
13103
  }
12966
13104
  const snapshots = store.snapshots;
@@ -13065,7 +13203,8 @@ function miniappManifest() {
13065
13203
  return;
13066
13204
  }
13067
13205
  const companionMatch = pathname?.match(DEV_COMPANION_PATH_PATTERN);
13068
- if (pathname !== DEV_MANIFEST_PATH && !companionMatch) {
13206
+ const miniProgramManifest = pathname === DEV_MINI_PROGRAM_MANIFEST_PATH;
13207
+ if (pathname !== DEV_MANIFEST_PATH && !companionMatch && !miniProgramManifest) {
13069
13208
  next();
13070
13209
  return;
13071
13210
  }
@@ -13075,16 +13214,25 @@ function miniappManifest() {
13075
13214
  response.end();
13076
13215
  return;
13077
13216
  }
13078
- if (!companionArtifactAccessToken ||
13079
- !isNumericLoopbackRequest(request) ||
13080
- !isNumericLoopbackHost(request.headers.host) ||
13081
- !hasValidCompanionArtifactToken(request, companionArtifactAccessToken)) {
13217
+ // 两条链路共用「仅数值 loopback」边界,凭据各自独立。
13218
+ if (!isNumericLoopbackRequest(request) || !isNumericLoopbackHost(request.headers.host)) {
13219
+ response.statusCode = 403;
13220
+ response.end();
13221
+ return;
13222
+ }
13223
+ const authorized = miniProgramManifest
13224
+ ? miniProgramManifestAccessToken !== undefined &&
13225
+ hasValidDevToken(request, DEV_MINI_PROGRAM_MANIFEST_TOKEN_HEADER, miniProgramManifestAccessToken)
13226
+ : companionArtifactAccessToken !== undefined &&
13227
+ hasValidDevToken(request, DEV_TOKEN_HEADER, companionArtifactAccessToken);
13228
+ if (!authorized) {
13082
13229
  response.statusCode = 403;
13083
13230
  response.end();
13084
13231
  return;
13085
13232
  }
13086
13233
  try {
13087
- const requestedDigest = request.headers[DEV_DIGEST_HEADER];
13234
+ // 本地调试入口只回最新快照;digest 钉扎仍然只服务 Companion 会话。
13235
+ const requestedDigest = miniProgramManifest ? undefined : request.headers[DEV_DIGEST_HEADER];
13088
13236
  if (requestedDigest !== undefined && (typeof requestedDigest !== 'string' || !/^[a-f0-9]{64}$/.test(requestedDigest))) {
13089
13237
  response.statusCode = 400;
13090
13238
  response.end();
@@ -13096,9 +13244,15 @@ function miniappManifest() {
13096
13244
  response.end();
13097
13245
  return;
13098
13246
  }
13247
+ if (miniProgramManifest && snapshot.manifestBytes.length > DEV_MINI_PROGRAM_MANIFEST_MAX_BYTES) {
13248
+ response.statusCode = 500;
13249
+ response.setHeader('Content-Type', 'text/plain; charset=utf-8');
13250
+ response.end(DEV_ERROR_MESSAGE);
13251
+ return;
13252
+ }
13099
13253
  response.setHeader('Cache-Control', 'no-store');
13100
13254
  response.setHeader('X-Content-Type-Options', 'nosniff');
13101
- if (pathname === DEV_MANIFEST_PATH) {
13255
+ if (pathname === DEV_MANIFEST_PATH || miniProgramManifest) {
13102
13256
  response.statusCode = 200;
13103
13257
  response.setHeader('Content-Type', 'application/json; charset=utf-8');
13104
13258
  response.setHeader('Content-Length', snapshot.manifestBytes.length);
@@ -13178,8 +13332,10 @@ function miniappManifest() {
13178
13332
  const sdkVersion = resolveSdkVersion();
13179
13333
  const platforms = readMiniappPlatformsFromPackageJson(root);
13180
13334
  const parsedPermissions = readMiniappPermissionsFromPackageJson(root, sdkVersion);
13335
+ const windowDeclaration = readMiniappWindowFromPackageJson(root);
13181
13336
  const companionConfig = readMiniappCompanionsFromPackageJson(root);
13182
13337
  const companionDeclarations = includeCompanions ? companionConfig.declarations : undefined;
13338
+ const bindAppIds = readMiniappBindAppIdsFromPackageJson(root);
13183
13339
  if (!parsedPermissions.declared)
13184
13340
  this.warn(getMissingPermissionsWarning());
13185
13341
  if (companionDeclarations && !parsedPermissions.permissions?.companion) {
@@ -13207,6 +13363,8 @@ function miniappManifest() {
13207
13363
  platforms,
13208
13364
  ...(parsedPermissions.permissions === undefined ? {} : { permissions: parsedPermissions.permissions }),
13209
13365
  ...(companions === undefined ? {} : { companions }),
13366
+ ...(windowDeclaration === undefined ? {} : { window: windowDeclaration }),
13367
+ ...(bindAppIds.length === 0 ? {} : { bindAppIds }),
13210
13368
  }));
13211
13369
  },
13212
13370
  };
@@ -13215,6 +13373,7 @@ function prepareDevSnapshot(root, outDir, platforms, declarations, snapshotRoot,
13215
13373
  const version = validateMiniappPackageVersionForBuild(readMiniappVersionFromPackageJson(root));
13216
13374
  const sdkVersion = resolveSdkVersion();
13217
13375
  const permissions = readMiniappPermissionsFromPackageJson(root, sdkVersion).permissions;
13376
+ const windowDeclaration = readMiniappWindowFromPackageJson(root);
13218
13377
  if (declarations && !permissions?.companion) {
13219
13378
  throw new Error('配置 companions 时必须声明 package.json#heybox.permissions.companion.enabled=true');
13220
13379
  }
@@ -13238,6 +13397,7 @@ function prepareDevSnapshot(root, outDir, platforms, declarations, snapshotRoot,
13238
13397
  platforms,
13239
13398
  ...(permissions === undefined ? {} : { permissions }),
13240
13399
  ...(companions === undefined ? {} : { companions }),
13400
+ ...(windowDeclaration === undefined ? {} : { window: windowDeclaration }),
13241
13401
  };
13242
13402
  return { artifacts, manifestBytes: Buffer.from(renderMiniappManifest(manifest)) };
13243
13403
  }
@@ -13286,11 +13446,12 @@ function isNumericLoopbackHost(host) {
13286
13446
  const port = Number(match[1]);
13287
13447
  return port >= 1 && port <= 65_535;
13288
13448
  }
13289
- function parseCompanionArtifactAccessToken(value) {
13449
+ /** 两条本地调试链路共用的凭据形状:32 字节 base64url,恰好 43 个字符。 */
13450
+ function parseDevAccessToken(value) {
13290
13451
  return value && /^[A-Za-z0-9_-]{43}$/.test(value) ? value : undefined;
13291
13452
  }
13292
- function hasValidCompanionArtifactToken(request, expected) {
13293
- const value = request.headers[DEV_TOKEN_HEADER];
13453
+ function hasValidDevToken(request, header, expected) {
13454
+ const value = request.headers[header];
13294
13455
  const candidate = Buffer.alloc(43);
13295
13456
  if (typeof value === 'string')
13296
13457
  Buffer.from(value).copy(candidate, 0, 0, candidate.length);
@@ -13339,4 +13500,4 @@ function resolveHmrWebSocketUrl(resolved) {
13339
13500
  return `${protocol}://${host}${port ? `:${port}` : ''}`;
13340
13501
  }
13341
13502
 
13342
- export { HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV, MINIAPP_PLATFORM_VALUES, miniappManifest };
13503
+ export { HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV, HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV, MINIAPP_PLATFORM_VALUES, miniappManifest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heybox/hb-sdk",
3
- "version": "0.8.1-alpha.13",
3
+ "version": "0.8.1-alpha.15",
4
4
  "sideEffects": [
5
5
  "./src/index.ts",
6
6
  "./src/core/singleton.ts",
@@ -62,7 +62,7 @@
62
62
  "skills": "1.5.23",
63
63
  "undici": "^7.28.0",
64
64
  "ws": "^8.18.0",
65
- "@heybox/hb-sdk-protocol": "0.8.1-alpha.13"
65
+ "@heybox/hb-sdk-protocol": "0.8.1-alpha.15"
66
66
  },
67
67
  "peerDependencies": {
68
68
  "vite": ">=5"
@@ -114,11 +114,11 @@
114
114
  "vite": "^8.0.12",
115
115
  "vitest": "^3.2.4",
116
116
  "@heybox-domain/heybox-theme": "~0.1.0",
117
- "@heybox-domain/heybox-vue3-ui": "~0.1.0",
118
117
  "@heybox/hb-api": "~1.28.3",
119
- "@heybox/hb-sdk-runtime": "~0.8.1-alpha.13",
120
- "@heybox/runtime-policy": "~0.2.0",
118
+ "@heybox/hb-sdk-runtime": "~0.8.1-alpha.15",
119
+ "@heybox-domain/heybox-vue3-ui": "~0.1.0",
121
120
  "@heybox/runtime": "~0.2.0",
121
+ "@heybox/runtime-policy": "~0.2.0",
122
122
  "@heybox/runtime-transport-fetch": "~0.2.0"
123
123
  },
124
124
  "publishConfig": {
package/skill/SKILL.md CHANGED
@@ -56,6 +56,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
56
56
  15. Use `companion.prepare()` and `companion.launch()` only from separate trusted user actions. Launch does not prepare implicitly. Treat stdio as raw `Uint8Array` with at-least-once output delivery, and implement business framing and sequence de-duplication explicitly.
57
57
  16. Use `environment.getInfo()` for the immutable runtime, Host App, canonical Mini-program, operating-system, and SDK version snapshot. It waits for the handshake automatically. Use `environment.getInfoSync()` only after `getHandshakeState().status === 'ready'` or inside a ready-state subscription; before that it throws `ENVIRONMENT_NOT_READY`.
58
58
  17. Treat missing environment strings as `null` and unknown enum values as `unknown`. Only `sdk.version` is SemVer; do not compare the other opaque version strings or use any environment field for authentication, authorization, or risk control.
59
+ 18. Declare window defaults only through `package.json#heybox.window` (`defaultWidth` / `defaultHeight` / `minWidth` / `minHeight` / `resizable`). `resizable` constrains user dragging only; `minWidth` / `minHeight` are the dragging lower bounds. Position is not open to developers.
59
60
 
60
61
  ## Step 5: Use CLI workflows
61
62
 
@@ -65,20 +66,21 @@ Apply these instructions when writing, reviewing, or debugging code that consume
65
66
  For real PC Companion debugging, configure local artifacts in `package.json#heybox.companions`, keep `miniappManifest()` enabled, bind the project, and ensure COA has approved `companion`. `hb-sdk dev` validates immutable snapshots without requiring an upload; config or artifact changes rotate the snapshot while existing Sessions retain their original artifact. Prepare and launch the new artifact explicitly. Check separate Browser, Mobile and PC Companion readiness in the debugging workbench. Without a local declaration, use Browser Fake only for state/UI integration.
66
67
  4. Use `hb-sdk build [--env <name>] [--verbose]` as the recommended production build entry. It directly owns the Vite build, always cleans and writes `dist/`, and works without CLI login, project binding, or network access.
67
68
  5. Declare the actual supported platforms in `package.json#heybox.platforms` and keep the no-argument `miniappManifest()` explicitly enabled in `vite.config.ts`; `hb-sdk build` must fail when the declaration, Manifest, or Runtime gate output is missing or inconsistent.
68
- 6. Keep project typechecking in `scripts.build`, for example `vue-tsc --noEmit && hb-sdk build`; `hb-sdk build` does not run typechecking or invoke `scripts.build` itself.
69
- 7. Existing projects may continue to use `vite build`; do not auto-migrate them. Do not invent `--mode`, `--json`, config, or output-directory flags for `hb-sdk build`.
70
- 8. Use `hb-sdk login`, `hb-sdk login status`, and `hb-sdk login clear` for remote management, publishing, Mobile debugging, and optional Browser Mock Host `heybox-session` requests. Browser debugging itself can start without CLI login. Mini-program code must still call `auth.login()`; the debug page only confirms authorization. Do not paste CLI credentials into page JavaScript. Phone debugging continues to use the App login.
71
- 9. Use `hb-sdk remote entity current` to confirm the current developer account and `hb-sdk remote entity switch <entity-id>` to change it before remote operations.
72
- 10. Use `hb-sdk remote create` to create and bind a mini-program; use `hb-sdk remote bind <mini-program-id>` to bind an existing manageable mini-program.
73
- 11. Use `hb-sdk remote info`, `hb-sdk remote list`, `hb-sdk remote access`, `hb-sdk remote versions`, `hb-sdk remote preview <version>`, and `hb-sdk remote allowlist ...` for remote inspection and preview management.
74
- 12. Use `hb-sdk remote deploy --release-note <text>` to run the project's `scripts.build`, upload, and submit the current project for audit. Do not recommend the removed top-level `hb-sdk deploy` alias.
75
- 13. `hb-sdk dev` and `hb-sdk remote deploy` skip the platform CSP only when the current version declares and the platform approves `network`. `useOfficialDomain` does not participate in CSP skip decisions. Direct `hb-sdk build`, direct Vite build, anonymous or invalid snapshots, and local Dev Context overrides must keep the platform CSP. Runtime Gate, Manifest, and HTML validation always remain active.
76
- 14. After approval, use `hb-sdk remote release <version>` for manual release or `--auto-publish` when an eligible low-risk version should release automatically.
77
- 15. Treat approval, release, and public display as separate states. Do not promise square, search, or recommendation visibility after release.
78
- 16. Use `hb-sdk remote withdraw`, `hb-sdk remote take-down`, `hb-sdk remote reopen`, and square visibility commands only after showing the target and obtaining required confirmation.
79
- 17. Use `--json` for remote script consumption and `--verbose` only when concise output is insufficient for diagnosis.
80
- 18. Use `hb-sdk doctor` to diagnose whether the local Skill matches the installed SDK; follow its output to install or refresh the Skill.
81
- 19. Do not print or expose cookies, tokens, private headers, or other credentials.
69
+ 6. When a released version should be discoverable from specific PC game workspaces, declare canonical decimal string appids in `package.json#heybox.bindAppIds` (maximum 20). This is version metadata, not a Runtime permission. Missing or empty clears bindings; `--from-version` inherits the source artifact snapshot.
70
+ 7. Keep project typechecking in `scripts.build`, for example `vue-tsc --noEmit && hb-sdk build`; `hb-sdk build` does not run typechecking or invoke `scripts.build` itself.
71
+ 8. Existing projects may continue to use `vite build`; do not auto-migrate them. Do not invent `--mode`, `--json`, config, or output-directory flags for `hb-sdk build`.
72
+ 9. Use `hb-sdk login`, `hb-sdk login status`, and `hb-sdk login clear` for remote management, publishing, Mobile debugging, and optional Browser Mock Host `heybox-session` requests. Browser debugging itself can start without CLI login. Mini-program code must still call `auth.login()`; the debug page only confirms authorization. Do not paste CLI credentials into page JavaScript. Phone debugging continues to use the App login.
73
+ 10. Use `hb-sdk remote entity current` to confirm the current developer account and `hb-sdk remote entity switch <entity-id>` to change it before remote operations.
74
+ 11. Use `hb-sdk remote create` to create and bind a mini-program; use `hb-sdk remote bind <mini-program-id>` to bind an existing manageable mini-program.
75
+ 12. Use `hb-sdk remote info` to compare local/current/added/removed game bindings, and use `hb-sdk remote list`, `hb-sdk remote access`, `hb-sdk remote versions`, `hb-sdk remote preview <version>`, and `hb-sdk remote allowlist ...` for remote inspection and preview management. These commands do not edit game bindings.
76
+ 13. Use `hb-sdk remote deploy --release-note <text>` to run the project's `scripts.build`, upload, and submit the current project for audit. Do not recommend the removed top-level `hb-sdk deploy` alias.
77
+ 14. `hb-sdk dev` and `hb-sdk remote deploy` skip the platform CSP only when the current version declares and the platform approves `network`. `useOfficialDomain` does not participate in CSP skip decisions. Direct `hb-sdk build`, direct Vite build, anonymous or invalid snapshots, and local Dev Context overrides must keep the platform CSP. Runtime Gate, Manifest, and HTML validation always remain active.
78
+ 15. After approval, use `hb-sdk remote release <version>` for manual release or `--auto-publish` when an eligible low-risk version should release automatically.
79
+ 16. Treat approval, release, and public display as separate states. Do not promise square, search, or recommendation visibility after release.
80
+ 17. Use `hb-sdk remote withdraw`, `hb-sdk remote take-down`, `hb-sdk remote reopen`, and square visibility commands only after showing the target and obtaining required confirmation.
81
+ 18. Use `--json` for remote script consumption and `--verbose` only when concise output is insufficient for diagnosis.
82
+ 19. Use `hb-sdk doctor` to diagnose whether the local Skill matches the installed SDK; follow its output to install or refresh the Skill.
83
+ 20. Do not print or expose cookies, tokens, private headers, or other credentials.
82
84
 
83
85
  ## Step 6: Preserve capability boundaries
84
86
 
@@ -95,6 +97,7 @@ For workshop mini-program business code:
95
97
  9. Do not invent string paths, File System Access API handles, Blob downloads, uploads, Range/resume, external deletion, move, append, or persistent external grants. Public file operations use only SDK-created handles; deletion is limited to SDK sandbox handles.
96
98
  10. Do not pass executable paths, dynamic args, cwd, environment variables, shell commands, URLs, hashes, PID, or native handles through `companion`; reviewed Manifest declarations are the only launch source.
97
99
  11. Do not treat `environment.*` as a device-fingerprint or trusted backend signal. It intentionally excludes account data, device identifiers, model, UA, CPU, and memory; use `viewport.getWindowInfo()` for screen geometry.
100
+ 12. Do not declare or set window position through the SDK. `package.json#heybox.window` rejects `x` / `y` and `width` / `height`. Position comes from user dragging and is remembered locally by the Host.
98
101
 
99
102
  For CLI and local development:
100
103
 
@@ -301,10 +301,10 @@ Reference 由 `@heybox/hb-sdk` 的公开导出与源码注释自动生成,不
301
301
  | --- | ---: | ---: | ---: | ---: | ---: |
302
302
  | Root API | 2 | 4 | 75 | 73 | 3 |
303
303
  | Protocol API | 0 | 13 | 58 | 90 | 47 |
304
- | Miniapp Publish API | 0 | 5 | 2 | 0 | 0 |
305
- | Vite API | 0 | 1 | 5 | 1 | 2 |
304
+ | Miniapp Publish API | 0 | 6 | 3 | 0 | 0 |
305
+ | Vite API | 0 | 1 | 5 | 1 | 3 |
306
306
 
307
- <!-- Generated by apps/docs/hb-sdk/scripts/generate-api-docs.ts; schemaVersion=4; fingerprint=a80eea1bb8b0d64255afcb4a664911f025aeae33f3f766c9b126f55668400178 -->
307
+ <!-- Generated by apps/docs/hb-sdk/scripts/generate-api-docs.ts; schemaVersion=4; fingerprint=1dfd4c6306e8ac3680f60db083aa048395e38ffb49c30e19b49cc5db72249533 -->
308
308
 
309
309
  ## SDK API
310
310
 
@@ -27,7 +27,7 @@
27
27
  ## Package metadata
28
28
 
29
29
  - Package: `@heybox/hb-sdk`
30
- - Version at generation time: `0.8.1-alpha.13`
30
+ - Version at generation time: `0.8.1-alpha.15`
31
31
  - Public root export: `@heybox/hb-sdk`
32
32
  - Protocol export: `@heybox/hb-sdk/protocol`
33
33
  - Vite plugin export: `@heybox/hb-sdk/vite`
@@ -328,6 +328,8 @@ hb-sdk build [--env <name>] [--verbose]
328
328
 
329
329
  `hb-sdk build` 直接使用项目安装的 Vite,先清理再生成固定的 `dist/`,并校验小程序入口、Manifest 和可上传产物。它不执行类型检查,不要求 CLI 登录或绑定小程序,也不访问远端服务。项目必须在 `package.json#heybox.platforms` 声明目标平台,并在 `vite.config.ts` 中显式注册无参的 `miniappManifest()`;配置与构建产物不一致时构建失败。
330
330
 
331
+ `hb-sdk remote info` 会只读展示 `package.json#heybox.bindAppIds`、当前线上绑定以及新增/移除差异。修改绑定必须更新项目配置并重新提交版本;CLI、Open 和 COA 都不提供第二个写入入口。普通 deploy 在上传文件前把实际构建 Manifest 交给服务端预检;`--from-version` 只使用源版本快照。
332
+
331
333
  直接运行 `hb-sdk build` 或 `vite build` 时默认注入平台 CSP。`hb-sdk remote deploy` 会在构建前读取绑定小程序的远端批准结果,并与当前版本声明取交集;仅当有效 `network` 权限启用时才跳过平台 CSP,`useOfficialDomain` 不参与该判定。权限缺失、非法或读取失败时继续注入,Runtime Gate、Manifest 与 HTML 构建检查始终保留。
332
334
 
333
335
  推荐由项目的 `scripts.build` 保留类型检查:
@@ -172,6 +172,8 @@ hb-sdk build [--env <name>] [--verbose]
172
172
 
173
173
  `hb-sdk build` 直接使用项目安装的 Vite,先清理再生成固定的 `dist/`,并校验小程序入口、Manifest 和可上传产物。它不执行类型检查,不要求 CLI 登录或绑定小程序,也不访问远端服务。项目必须在 `package.json#heybox.platforms` 声明目标平台,并在 `vite.config.ts` 中显式注册无参的 `miniappManifest()`;配置与构建产物不一致时构建失败。
174
174
 
175
+ `hb-sdk remote info` 会只读展示 `package.json#heybox.bindAppIds`、当前线上绑定以及新增/移除差异。修改绑定必须更新项目配置并重新提交版本;CLI、Open 和 COA 都不提供第二个写入入口。普通 deploy 在上传文件前把实际构建 Manifest 交给服务端预检;`--from-version` 只使用源版本快照。
176
+
175
177
  直接运行 `hb-sdk build` 或 `vite build` 时默认注入平台 CSP。`hb-sdk remote deploy` 会在构建前读取绑定小程序的远端批准结果,并与当前版本声明取交集;仅当有效 `network` 权限启用时才跳过平台 CSP,`useOfficialDomain` 不参与该判定。权限缺失、非法或读取失败时继续注入,Runtime Gate、Manifest 与 HTML 构建检查始终保留。
176
178
 
177
179
  推荐由项目的 `scripts.build` 保留类型检查:
@@ -42,6 +42,8 @@
42
42
  - Companion stdio 是原始 `Uint8Array` 且输出为 at-least-once 投递;业务自行定义 framing、去重、重放与恢复。
43
43
  - Browser Dev Host 的 Companion Fake 只用于状态和 UI 开发,不能作为桌面程序可执行、签名、权限或进程监管的真机证据。
44
44
  - 本地真 PC Companion 需要 `package.json#heybox.companions`、本地产物、已绑定项目和 COA `companion` 批准;未配置本地产物时使用 Browser Fake。配置或产物更新后校验并切换不可变快照;已有进程保留原产物,新产物须重新准备与启动。
45
+ - `package.json#heybox.window` 只声明开窗初值与可缩放性;`resizable` 只约束用户拖拽。
46
+ - 窗口位置不开放:开发者不能声明、也不能设置位置;位置由用户拖拽并由宿主按小程序身份本地记忆。
45
47
 
46
48
  私有 Page Channel 适配没有修改 `@heybox/hb-sdk` 公开 API 或 iframe wire;本次 files/download
47
49
  Runtime 能力随 `0.8.0-alpha.11` release family 发布,部署 detail 前必须保证
package/skill/skill.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "hb-sdk",
3
- "skillVersion": "0.8.1-alpha.13+skill.3784ce3da843",
3
+ "skillVersion": "0.8.1-alpha.15+skill.b220fbc77b56",
4
4
  "sdk": {
5
5
  "package": "@heybox/hb-sdk",
6
- "version": "0.8.1-alpha.13",
7
- "compatibility": "0.8.1-alpha.13"
6
+ "version": "0.8.1-alpha.15",
7
+ "compatibility": "0.8.1-alpha.15"
8
8
  },
9
9
  "distribution": {
10
10
  "type": "npm",
11
11
  "package": "@heybox/hb-sdk",
12
- "version": "0.8.1-alpha.13",
12
+ "version": "0.8.1-alpha.15",
13
13
  "path": "skill"
14
14
  },
15
- "integrity": "sha256-3784ce3da8431dd675160a5c2a66e19d08f5210d3765a2cddcadee6e3dfa2a38"
15
+ "integrity": "sha256-b220fbc77b56021c0620c086c036ceba404be7a49741ffffb8bee9fce57706a7"
16
16
  }
@@ -0,0 +1,5 @@
1
+ export declare const MINIAPP_BIND_APP_IDS_MAX_COUNT = 20;
2
+ export interface ParseMiniappBindAppIdsOptions {
3
+ sourceLabel?: string;
4
+ }
5
+ export declare function parseMiniappBindAppIds(value: unknown, options?: ParseMiniappBindAppIdsOptions): readonly string[];
@@ -3,3 +3,4 @@ export * from './node';
3
3
  export * from './permissions';
4
4
  export * from './companions';
5
5
  export * from './companion-types';
6
+ export * from './bindings';
@@ -1,6 +1,6 @@
1
1
  import { type ParsedMiniappPermissions } from './permissions';
2
2
  import type { MiniappCompanionAuthoringDeclarations } from './companion-types';
3
- import type { MiniappPlatform } from './schema';
3
+ import type { MiniappPlatform, MiniappWindowDeclaration } from './schema';
4
4
  export declare function readMiniappVersionFromPackageJson(root: string): string;
5
5
  /** Companion source 始终相对声明所在 package.json,而非 Vite 的 HTML root。 */
6
6
  export declare function readMiniappCompanionsFromPackageJson(root: string): {
@@ -9,5 +9,13 @@ export declare function readMiniappCompanionsFromPackageJson(root: string): {
9
9
  packageJsonPath: string;
10
10
  };
11
11
  export declare function readMiniappPlatformsFromPackageJson(root: string): readonly MiniappPlatform[];
12
+ /**
13
+ * 读取并校验 `package.json#heybox.window`。
14
+ *
15
+ * @returns 合法声明;未声明时返回 `undefined`,调用方不得向 manifest 写入空对象或 `null`。
16
+ * @throws 当声明违反 {@link validateMiniappWindow} 的规则时抛出,报错前缀为 `package.json#heybox.window`。
17
+ */
18
+ export declare function readMiniappWindowFromPackageJson(root: string): MiniappWindowDeclaration | undefined;
12
19
  export declare function readMiniappPermissionsFromPackageJson(root: string, sdkVersion: string): ParsedMiniappPermissions;
20
+ export declare function readMiniappBindAppIdsFromPackageJson(root: string): readonly string[];
13
21
  export declare function resolveMiniappSdkVersion(): string;