@heybox/hb-sdk 0.8.0-alpha.3 → 0.8.0

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 (72) hide show
  1. package/CHANGELOG.md +136 -422
  2. package/README.md +28 -18
  3. package/dist/cli-chunks/{build-qWzAbpS7.cjs → build-PYCNacya.cjs} +8 -5
  4. package/dist/cli-chunks/{context-CtS2Thp0.cjs → context-m2W2XbL0.cjs} +42 -59
  5. package/dist/cli-chunks/{create-PV5ua977.cjs → create-BdAg3WGA.cjs} +2 -2
  6. package/dist/cli-chunks/{dev-CwKbAm_H.cjs → dev-CyZuw7Yn.cjs} +76 -64
  7. package/dist/cli-chunks/{doctor-tJUGOYrm.cjs → doctor-DU8rCfUF.cjs} +1 -1
  8. package/dist/cli-chunks/{index-DVuD75Hr.cjs → index-DATObqzK.cjs} +2 -2
  9. package/dist/cli-chunks/{index-De687C6-.cjs → index-MMW2ibQm.cjs} +32 -21
  10. package/dist/cli-chunks/{index.esm-B-4yrLNm.cjs → index.esm-BiAaAUFC.cjs} +8 -8
  11. package/dist/cli-chunks/{login-DolpqD8K.cjs → login-B3TThMss.cjs} +2 -2
  12. package/dist/cli-chunks/{project-vite-1rvkK-M8.cjs → project-vite-BQj8YLI4.cjs} +1 -1
  13. package/dist/cli-chunks/{remote-rIAQE_G2.cjs → remote-DNvI7tHH.cjs} +57 -25
  14. package/dist/cli-chunks/{runtime-gate-DFjw66kF.cjs → runtime-gate-BEFp1w_s.cjs} +11 -3
  15. package/dist/cli-chunks/{runtime-permission-env-CjsCe5bp.cjs → runtime-permission-env-CtL8rsjB.cjs} +351 -0
  16. package/dist/cli-chunks/{session-CzaM2Cq3.cjs → session-DjBkjaF8.cjs} +1 -1
  17. package/dist/cli-chunks/{skill-CM40_9WH.cjs → skill-cR_wnaw2.cjs} +2 -2
  18. package/dist/cli-chunks/{version-Bz-AfXQU.cjs → version-yEn1E2Bg.cjs} +1 -1
  19. package/dist/cli.cjs +1 -1
  20. package/dist/devtools/browser-dev-host/assets/browser-dev-host-TzYf9L6C.js +99 -0
  21. package/dist/devtools/browser-dev-host/assets/{index-CeP6SLB3.js → index-C5MZZDa5.js} +4 -4
  22. package/dist/devtools/browser-dev-host/assets/index-P-ra4m1y.css +1 -0
  23. package/dist/devtools/browser-dev-host/assets/workbench-state-BwV7bm4n.js +5 -0
  24. package/dist/devtools/browser-dev-host/index.html +3 -3
  25. package/dist/index.cjs.js +100 -2
  26. package/dist/index.esm.js +100 -2
  27. package/dist/protocol.cjs.js +189 -47
  28. package/dist/protocol.esm.js +189 -47
  29. package/dist/templates/{vue3-vite-ts → vanilla-vite-js}/.gitignore.ejs +0 -1
  30. package/dist/templates/vanilla-vite-js/README.md.ejs +14 -0
  31. package/dist/templates/vanilla-vite-js/index.html.ejs +20 -0
  32. package/dist/templates/vanilla-vite-js/package.json.ejs +22 -0
  33. package/dist/templates/vanilla-vite-js/src/assets/heybox-logo.svg +8 -0
  34. package/dist/templates/vanilla-vite-js/src/main.js +39 -0
  35. package/dist/templates/vanilla-vite-js/src/styles.css +155 -0
  36. package/dist/templates/{vue3-vite-ts/vite.config.ts → vanilla-vite-js/vite.config.js} +1 -2
  37. package/dist/vite.cjs.js +281 -13
  38. package/dist/vite.esm.js +281 -13
  39. package/package.json +13 -9
  40. package/skill/SKILL.md +8 -9
  41. package/skill/references/api-protocol.md +1 -3
  42. package/skill/references/api-root.md +65 -21
  43. package/skill/references/cli.md +17 -5
  44. package/skill/references/examples.md +3 -3
  45. package/skill/references/recipes.md +40 -41
  46. package/skill/references/safety-boundaries.md +9 -5
  47. package/skill/skill.json +5 -5
  48. package/types/core/mini-dev-console.d.ts +36 -0
  49. package/types/miniapp-manifest/index.d.ts +1 -0
  50. package/types/miniapp-manifest/node.d.ts +3 -0
  51. package/types/miniapp-manifest/permissions.d.ts +37 -0
  52. package/types/miniapp-manifest/schema.d.ts +5 -0
  53. package/types/modules/share/index.d.ts +1 -1
  54. package/types/modules/share/show-share-menu.d.ts +1 -1
  55. package/types/modules/share/types.d.ts +2 -4
  56. package/types/vite/index.d.ts +3 -1
  57. package/dist/devtools/browser-dev-host/assets/browser-dev-host-uq-Wac6k.js +0 -97
  58. package/dist/devtools/browser-dev-host/assets/index-KD2f3Jdz.css +0 -1
  59. package/dist/devtools/browser-dev-host/assets/workbench-state-D-1U0JRq.js +0 -5
  60. package/dist/templates/vue3-vite-ts/README.md.ejs +0 -47
  61. package/dist/templates/vue3-vite-ts/index.html.ejs +0 -12
  62. package/dist/templates/vue3-vite-ts/package.json.ejs +0 -33
  63. package/dist/templates/vue3-vite-ts/src/App.vue +0 -78
  64. package/dist/templates/vue3-vite-ts/src/__tests__/App.spec.ts +0 -148
  65. package/dist/templates/vue3-vite-ts/src/auth-handoff.ts +0 -46
  66. package/dist/templates/vue3-vite-ts/src/main.ts +0 -5
  67. package/dist/templates/vue3-vite-ts/src/styles.css +0 -60
  68. package/dist/templates/vue3-vite-ts/src/vite-env.d.ts +0 -1
  69. package/dist/templates/vue3-vite-ts/tsconfig.app.json +0 -17
  70. package/dist/templates/vue3-vite-ts/tsconfig.json +0 -11
  71. package/dist/templates/vue3-vite-ts/tsconfig.node.json +0 -11
  72. package/dist/templates/vue3-vite-ts/vitest.config.ts +0 -10
@@ -24,7 +24,7 @@
24
24
  ## Package metadata
25
25
 
26
26
  - Package: `@heybox/hb-sdk`
27
- - Version at generation time: `0.8.0-alpha.3`
27
+ - Version at generation time: `0.8.0`
28
28
  - Public root export: `@heybox/hb-sdk`
29
29
  - Protocol export: `@heybox/hb-sdk/protocol`
30
30
  - Vite plugin export: `@heybox/hb-sdk/vite`
@@ -232,6 +232,7 @@ Use `@heybox/hb-sdk/vite` only in `vite.config.ts`. Do not import it from iframe
232
232
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
233
233
  import path from 'node:path';
234
234
  import { HB_SDK_VERSION } from '../core/version';
235
+ import { MINI_DEV_CONSOLE_EVENT_TYPE, MINI_DEV_IFRAME_WINDOW_NAME, MINI_DEV_CONSOLE_INSTALL_FLAG } from '../core/mini-dev-console';
235
236
  import {
236
237
  MINIAPP_PLATFORM_VALUES,
237
238
  renderMiniappManifest,
@@ -240,12 +241,14 @@ import {
240
241
  type MiniappPlatform,
241
242
  } from '../miniapp-manifest/schema';
242
243
  import { readMiniappVersionFromPackageJson } from '../miniapp-manifest/node';
244
+ import { readMiniappPermissionsFromPackageJson } from '../miniapp-manifest/node';
245
+ import { getMissingPermissionsWarning } from '../miniapp-manifest/permissions';
243
246
  import { enforceMiniappHtmlPolicy } from './html-policy';
244
247
  import { shouldSkipMiniappPlatformCspFromEnv } from './runtime-permission-env';
245
248
 
246
249
  export interface MiniappManifestPlugin {
247
250
  name: string;
248
- config: (config: MiniappManifestUserConfig) => MiniappManifestUserConfig | void;
251
+ config: (config: MiniappManifestUserConfig, env?: { command: 'build' | 'serve' }) => MiniappManifestUserConfig | void;
249
252
  configResolved: (resolved: MiniappManifestResolvedConfig) => void;
250
253
  /** Rollup 在构建失败后仍会跑 closeBundle;用 buildEnd 记录真实错误,避免二次校验掩盖原因。 */
251
254
  buildEnd: (error?: Error) => void;
@@ -287,12 +290,34 @@ export interface MiniappManifestResolvedConfig {
287
290
  export { MINIAPP_PLATFORM_VALUES };
288
291
  export type { MiniappPlatform };
289
292
 
293
+ /**
294
+ * dev serve 时在入口 HTML 最早位置注入无依赖 console 捕获 bootstrap。
295
+ *
296
+ * 必须内联完整 patch 逻辑而非等 SDK 模块加载:vite client、业务代码的 console 调用
297
+ * 都可能早于 SDK 单例初始化(如 [vite] connecting)。捕获条件:
298
+ * 1. 构建期只有 vite serve 走到本分支(生产构建无此代码,双保险之 define 常量仍注入给 SDK 侧);
299
+ * 2. 运行期仅调试 iframe(window.name 标记由调试台 iframe.name 与本 bootstrap 设置)激活。
300
+ * 转发经 window.parent.postMessage(CSP connect-src 不约束 postMessage),失败静默,
301
+ * 严禁回打 console 造成自触发循环。SDK 侧 installMiniDevConsoleForwarding 安装时
302
+ * 检测同款 INSTALL_FLAG,不会重复 patch。
303
+ */
304
+ function injectMiniDevConsoleBootstrap(html: string): string {
305
+ const bootstrap = `<script>(function(){var LEVELS=['log','debug','info','warn','error'];var FLAG='${MINI_DEV_CONSOLE_INSTALL_FLAG}';var TYPE='${MINI_DEV_CONSOLE_EVENT_TYPE}';if(window.name!=='${MINI_DEV_IFRAME_WINDOW_NAME}')window.name='${MINI_DEV_IFRAME_WINDOW_NAME}';if(window[FLAG])return;var patched=false;for(var i=0;i<LEVELS.length;i++)(function(level){var original=console[level];if(typeof original!=='function')return;console[level]=function(){try{parent.postMessage({type:TYPE,detail:{level:level,args:Array.prototype.slice.call(arguments),timestamp:Date.now()}},'*')}catch(e){}return original.apply(this,arguments)};patched=true})(LEVELS[i]);if(patched)window[FLAG]=true})();</script>`;
306
+ const headIndex = html.indexOf('<head>');
307
+ if (headIndex < 0) {
308
+ return html;
309
+ }
310
+ const insertAt = headIndex + '<head>'.length;
311
+ return `${html.slice(0, insertAt)}${bootstrap}${html.slice(insertAt)}`;
312
+ }
313
+
290
314
  export interface MiniappManifestOptions {
291
315
  platforms: readonly MiniappPlatform[];
292
316
  }
293
317
 
294
318
  const sdkVersionPlaceholder = ['__HB_SDK', 'VERSION__'].join('_');
295
319
  const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
320
+ const miniDevLoggingConstant = ['__HB_SDK_DEV', 'LOGGING__'].join('_');
296
321
 
297
322
  export function miniappManifest(options: MiniappManifestOptions): MiniappManifestPlugin {
298
323
  const platforms = validateMiniappPlatforms(options?.platforms, 'miniappManifest().platforms');
@@ -306,12 +331,17 @@ export function miniappManifest(options: MiniappManifestOptions): MiniappManifes
306
331
 
307
332
  return {
308
333
  name: 'heybox-miniapp-manifest',
309
- config(config) {
334
+ config(config, env) {
310
335
  assertMiniappViteBase(config.base);
336
+ // command 在本钩子时序早于 configResolved,必须取自 env 命令而非闭包;
337
+ // 旧调用方(测试等)未传 env 时按 build 处理。
338
+ const isServe = env?.command === 'serve';
311
339
  return {
312
340
  ...(config.base === undefined ? { base: './' } : {}),
313
341
  define: {
314
342
  [sdkVersionBuildConstant]: JSON.stringify(resolveSdkVersion()),
343
+ // 仅 vite serve(hb-sdk dev)注入;vite build / hb-sdk build 永不注入,捕获模块被死代码消除。
344
+ ...(isServe ? { [miniDevLoggingConstant]: 'true' } : {}),
315
345
  },
316
346
  };
317
347
  },
@@ -333,12 +363,12 @@ export function miniappManifest(options: MiniappManifestOptions): MiniappManifes
333
363
  }
334
364
  },
335
365
  transformIndexHtml(html) {
336
- return enforceMiniappHtmlPolicy(html, {
366
+ const enforced = enforceMiniappHtmlPolicy(html, {
337
367
  ...(command === 'serve' ? { hmrWebSocketUrl } : {}),
338
368
  skipPlatformCsp,
339
369
  });
340
- },
341
- async closeBundle() {
370
+ return command === 'serve' ? injectMiniDevConsoleBootstrap(enforced) : enforced;
371
+ }, async closeBundle() {
342
372
  // Rollup 在 buildStart/transform 失败后仍会调用 closeBundle。
343
373
  // 若此时再抛「缺 index.html」等二次错误,会掩盖真实失败原因。
344
374
  if (priorBuildError) {
@@ -347,6 +377,8 @@ export function miniappManifest(options: MiniappManifestOptions): MiniappManifes
347
377
 
348
378
  const version = validateMiniappPackageVersionForBuild(readMiniappVersionFromPackageJson(root));
349
379
  const sdkVersion = resolveSdkVersion();
380
+ const parsedPermissions = readMiniappPermissionsFromPackageJson(root, sdkVersion);
381
+ if (!parsedPermissions.declared) this.warn(getMissingPermissionsWarning());
350
382
  const outputRoot = path.resolve(root, outDir);
351
383
  const htmlFiles = collectHtmlFiles(outputRoot);
352
384
  const unexpectedHtmlFiles = htmlFiles.filter((file) => path.relative(outputRoot, file) !== 'index.html');
@@ -365,7 +397,15 @@ export function miniappManifest(options: MiniappManifestOptions): MiniappManifes
365
397
 
366
398
  const manifestPath = path.join(outputRoot, 'manifest.json');
367
399
  mkdirSync(path.dirname(manifestPath), { recursive: true });
368
- writeFileSync(manifestPath, renderMiniappManifest({ version, sdkVersion, platforms }));
400
+ writeFileSync(
401
+ manifestPath,
402
+ renderMiniappManifest({
403
+ version,
404
+ sdkVersion,
405
+ platforms,
406
+ ...(parsedPermissions.permissions === undefined ? {} : { permissions: parsedPermissions.permissions }),
407
+ }),
408
+ );
369
409
  },
370
410
  };
371
411
  }
@@ -420,7 +460,7 @@ hb-sdk build [--env <name>] [--verbose]
420
460
 
421
461
  `hb-sdk build` 直接使用项目安装的 Vite,先清理再生成固定的 `dist/`,并校验小程序入口、Manifest 和可上传产物。它不执行类型检查,不要求 CLI 登录或绑定小程序,也不访问远端服务。项目必须在 `vite.config.ts` 中显式注册 `miniappManifest()`。
422
462
 
423
- 直接运行 `hb-sdk build` 或 `vite build` 时默认注入平台 CSP。`hb-sdk remote deploy` 会在构建前读取绑定小程序的远端权限;仅当 `network.request.status=enabled` 时向子构建传递已验证上下文并跳过平台 CSP,`useOfficialDomain` 不参与该判定。权限缺失、非法或读取失败时继续注入,Runtime Gate、Manifest 与 HTML 构建检查始终保留。
463
+ 直接运行 `hb-sdk build` 或 `vite build` 时默认注入平台 CSP。`hb-sdk remote deploy` 会在构建前读取绑定小程序的远端批准结果,并与当前版本声明取交集;仅当有效 `network` 权限启用时才跳过平台 CSP,`useOfficialDomain` 不参与该判定。权限缺失、非法或读取失败时继续注入,Runtime Gate、Manifest 与 HTML 构建检查始终保留。
424
464
 
425
465
  推荐由项目的 `scripts.build` 保留类型检查:
426
466
 
@@ -439,7 +479,9 @@ hb-sdk build [--env <name>] [--verbose]
439
479
 
440
480
  # 快速开始
441
481
 
442
- 如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;业务只需根据小程序是否开通网络权限选择身份流程。
482
+ 新项目可先运行 `hb-sdk create my-miniapp` 获得 Vanilla JavaScript + Vite HelloWorld;模板只演示 Toast,业务代码可以直接替换。
483
+
484
+ 如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;使用受保护能力前需在 `package.json#heybox.permissions` 声明对应权限,详见[权限声明](https://docs.xiaoheihe.cn/hb_sdk/guide/permissions)。
443
485
 
444
486
  ## 交互控件与握手状态
445
487
 
@@ -460,9 +502,9 @@ onUnmounted(stopHandshakeState)
460
502
 
461
503
  把 `sdkReady` 用作按钮的 `disabled` 条件。订阅会立即回放当前状态,因此晚挂载组件也能得到 `ready` 或 `failed` 终态。`ready` 生命周期事件只在握手成功瞬间派发且不会重放,不能作为当前状态来源。
462
504
 
463
- ## 未开通网络权限
505
+ ## 读取当前用户
464
506
 
465
- 无网络小程序可以在本地能力中使用按小程序隔离的 `app_user_id` 和公开资料:
507
+ 声明 `userInfo` 后,可以读取按小程序隔离的 `app_user_id` 和已授权资料。该能力不依赖公开 `network` 权限:
466
508
 
467
509
  ```ts
468
510
  import hbSDK from '@heybox/hb-sdk'
@@ -475,9 +517,9 @@ async function getCurrentUser() {
475
517
 
476
518
  `user.getInfo()` 不展示授权弹窗,也不要求可信用户手势。未登录时返回未登录状态;已登录时隐式授予当前本地用户 scopes(`identity`、`profile`),静默返回 `userInfo.app_user_id`、`profile.nickname` 和 `profile.avatar`。这条路径不能获取授权码或调用 OpenAPI,也不能把身份或资料发送到外部服务。
477
519
 
478
- ## 已开通网络权限
520
+ ## 获取服务端授权码
479
521
 
480
- 有网络小程序通过 `auth.login()` 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作:
522
+ 声明 `userInfo` 后,可通过 `auth.login()` 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作。该调用内部使用 Host 受信请求,不依赖公开 `network` 权限;示例中的 `network.request()` 另需声明并获得 `network` 平台批准。
481
523
 
482
524
  SDK 会自动完成握手。登录按钮应按上面的持久握手状态启用;点击回调直接调用 `auth.login()`,不要先等待其他异步任务,以免首次操作丢失可信手势。
483
525
 
@@ -546,15 +588,14 @@ try {
546
588
 
547
589
  | Host | `files.sandbox` | 外部 picker | `network.download()` |
548
590
  | ---------------- | --------------- | ----------- | -------------------- |
549
- | 旧版 PC | 不支持 | 不支持 | 不支持 |
591
+ | PC | 支持 | 支持 | 支持 |
550
592
  | Mobile | 不支持 | 不支持 | 不支持 |
551
593
  | Web | 不支持 | 不支持 | 不支持 |
552
594
  | Browser Dev Host | 不支持 | 不支持 | 不支持 |
553
595
 
554
596
  当前 Runtime 认识但 Host 未实现时返回 `METHOD_FORBIDDEN`;旧 Runtime 收到新 method 时返回
555
- `METHOD_NOT_FOUND`。此前曾实现面向旧版小黑盒 PC Host 适配;由于新版 PC 即将启用,该适配
556
- 不再发布。文件/下载 API、协议和 Runtime 体系保持不变,后续直接按照新版 PC Host 架构接入。
557
- 当前任何 Host 都不提供 Blob 或内存假下载。
597
+ `METHOD_NOT_FOUND`。新 PC V1 注入真实文件与流式下载 primitive;其他当前 Host 未实现时仍不
598
+ 提供 throwing stub、Blob 或内存假下载。
558
599
 
559
600
  ### 路径与创建
560
601
 
@@ -580,9 +621,10 @@ async function saveFromUserAction(text: string) {
580
621
  }
581
622
  ```
582
623
 
583
- `saveFile()` 打开系统保存对话框并返回精确 File 授权,不会同时创建或保留 Directory
584
- 授权。V1 不接受 `accept`;扩展名过滤只属于 `pickFiles({ accept: ['.json'] })`。外部授权仅在当前
585
- Runtime session 有效。
624
+ `saveFile()` 请求 Host 返回精确 File 授权,不会同时创建或保留 Directory 授权。新 PC 当前先让
625
+ 用户选择保存目录,再以 `suggestedName` 独占创建空文件;同名文件抛出 `FILE_ALREADY_EXISTS`,
626
+ 不会静默覆盖。V1 不接受 `accept`;扩展名过滤只属于 `pickFiles({ accept: ['.json'] })`。外部授权
627
+ 仅在当前 Runtime session 有效。
586
628
 
587
629
  `pickFiles()` / `pickDirectory()` 默认返回 `mode: 'read'` 的授权;只有显式传入
588
630
  `multiple: true` 时,`pickFiles()` 才允许 Host 返回多个文件。需要修改文件、在所选目录中创建
@@ -638,6 +680,8 @@ try {
638
680
 
639
681
  - 根据 `error.code` 区分运行环境、权限、超时和业务失败,不要只比对错误文案。
640
682
  - 权限失败时给出可理解的提示,不要将它当成未登录。
683
+ - `PERMISSION_NOT_DECLARED` 表示当前版本没有在 `package.json#heybox.permissions` 声明所需权限,应修改项目配置并重新构建、提交版本。
684
+ - `PERMISSION_DENIED` 表示权限已声明,但需要的平台批准缺失或批准配置不足。首期只有 `network` 需要平台批准。
641
685
  - `USER_GESTURE_REQUIRED` 表示潜在授权 UI 缺少可信用户手势,应让用户点击按钮后重试。
642
686
  - `AUTHORIZATION_CANCELLED` 表示用户取消、拒绝或关闭授权页面,应正常结束当前操作。
643
687
  - `SERVER_API_REQUIRED` 表示当前用户数据必须经开发者服务端 OpenAPI 获取,不应在页面重试对应 Host API。
@@ -770,7 +814,7 @@ onUnmounted(stopLifecycleEvents)
770
814
  - 当前握手状态只通过 `getHandshakeState()` 查询、通过 `onHandshakeStateChange()` 订阅;订阅会立即回放当前值并返回取消函数。
771
815
  - `ready` 事件只在握手成功瞬间派发,不会向晚订阅者重放,不能用于驱动按钮可用状态或替代握手状态 API。
772
816
  - UI 可见性相关逻辑放在 `show`、`hide`。
773
- - 使用 Host current-user APIs 的无网络小程序,可以在 `heybox_app_login_change` 后刷新本地状态。已开通网络权限的小程序使用 `auth.login()` 获取 code,并由开发者服务端维护业务会话。
817
+ - 使用 `user.getInfo()` 的小程序可以在 `heybox_app_login_change` 后刷新本地状态。需要开发者服务端会话时使用 `auth.login()` 获取 code;两者都不依赖公开 `network` 权限。
774
818
  - 组件或页面销毁时清理 `on` 注册的监听,避免重复响应。
775
819
  - 生命周期事件只派发给注册当时存在的监听器,不会重放;一次性 `launch` 信息应在页面启动阶段通过业务能力调用处理。
776
820
  - 收到 `unload` 后,当前 SDK 上下文不可恢复,未完成请求会失败;不要在同一页面上下文继续重试能力调用。
@@ -82,6 +82,10 @@ npm install
82
82
  npm run dev
83
83
  ```
84
84
 
85
+ 默认产物是 Vanilla JavaScript + Vite HelloWorld,只演示一次 `ui.showToast()` 调用,并预填 `android`、`ios`、`ohos` 三个平台。Demo 可以整体删除,项目不绑定 Vue、TypeScript 或测试框架。
86
+
87
+ 当前 `0.8` 已发布为稳定版本,本页使用 `@latest` 创建项目。
88
+
85
89
  已有项目则在项目目录运行 `npm run dev` 或 `hb-sdk dev`。
86
90
 
87
91
  Agent rules:
@@ -101,13 +105,15 @@ Agent rules:
101
105
  hb-sdk dev
102
106
  ```
103
107
 
104
- CLI 会启动页面服务并自动打开 Vue 3 本地调试台。左侧集中放置调用日志、Storage 和问题三个诊断页签,右侧保持稳定的小程序内容与设备预览。
108
+ CLI 会启动页面服务并自动打开 Vue 3 本地调试台。左侧集中放置日志、Storage 和问题三个诊断页签,右侧保持稳定的小程序内容与设备预览。
109
+
110
+ 默认终端只显示启动结果、调试页地址、小程序地址和手机调试入口。排查启动问题时使用 `hb-sdk dev --verbose` 查看完整启动阶段;手机扫码调试凭证连续重试失败时,默认只在失败和恢复两次状态变化时提示。
105
111
 
106
112
  <img src="/assets/browser-dev-host-workbench.png" alt="小程序工坊 Vue 3 调试台" style="width: 100%; max-width: 1120px;" />
107
113
 
108
114
  浏览器调试入口不要求 CLI 登录或项目绑定。没有账号、绑定小程序或远端 Dev Context 时,页面和基础 Browser Mock 仍可启动;依赖这些上下文的能力会返回明确失败。远端管理、部署和发布仍要求完成登录与绑定。
109
115
 
110
- `hb-sdk dev` 在远端权限快照可用时读取它。只有快照有效且 `network.request.status=enabled` 时才跳过平台 CSP;`useOfficialDomain` 不参与该判定。调试台不提供权限修改入口,远端权限快照始终是 Runtime 的规范输入;快照缺失或无效时保留平台 CSP。
116
+ `hb-sdk dev` 从项目 `package.json` 读取开发者声明,并在远端权限快照可用时读取平台批准结果。只有当前版本声明且平台已批准 `network` 时才跳过平台 CSP;`useOfficialDomain` 不参与该判定。调试台不提供权限或平台批准修改入口;快照缺失或无效时保留平台 CSP。
111
117
 
112
118
  Browser Mock 会自动在实时与兼容链路间切换。手机调试使用 `open_inapp`/`openWindow` 包裹的 LAN 短链接二维码(先开普通 H5 跳转页,再进入小程序并关闭中间页)。
113
119
 
@@ -119,7 +125,7 @@ Browser Mock 会自动在实时与兼容链路间切换。手机调试使用 `op
119
125
  - SDK 初始化与用户身份授权流程
120
126
  - 生命周期、Storage 和排行榜等能力
121
127
 
122
- 调试台提供紧凑的调用日志、当前小程序隔离作用域内的 Storage 只读快照和问题聚合。日志与网络记录只保留脱敏的诊断字段,不展示 payload、结果、查询参数、凭据或原始错误信息。工坊小程序默认不能进行网络请求,网络权限暂未开放申请,不要把本地结果当成线上能力。
128
+ 调试台提供紧凑的日志、当前小程序隔离作用域内的 Storage 只读快照和问题聚合。日志与网络记录只保留脱敏的诊断字段,不展示 payload、结果、查询参数、凭据或原始错误信息。工坊小程序默认不能进行网络请求,网络权限暂未开放申请,不要把本地结果当成线上能力。
123
129
 
124
130
  右侧可切换 iPhone 16 Pro Max(`387 x 821`)与 Pixel 9 Pro(`322 x 716`)两个设备预设。尺寸对应固定上游设备外框的真实屏幕 opening;预设同时决定外框、状态栏、安全区和 viewport。切换设备不会重新加载小程序或重启 Runtime,页面状态和调试会话保持不变。授权与操作弹窗、Toast、Loading 和振动反馈均显示在设备预览内部,不会覆盖整个调试台。
125
131
 
@@ -138,7 +144,7 @@ Browser Mock 会自动在实时与兼容链路间切换。手机调试使用 `op
138
144
  | 参数 | 用途 |
139
145
  | -------------------------------- | -------------------------------- |
140
146
  | `--port <port>` | 指定页面开发服务端口。 |
141
- | `--browser-dev-host-port <port>` | 指定 Browser Dev Host 端口。 |
147
+ | `--browser-dev-host-port <port>` | 指定浏览器调试页服务端口。 |
142
148
  | `--no-open` | 启动后不自动打开浏览器。 |
143
149
  | `--verbose` | 出现问题时输出更详细的诊断信息。 |
144
150
 
@@ -158,7 +164,7 @@ hb-sdk build [--env <name>] [--verbose]
158
164
 
159
165
  `hb-sdk build` 直接使用项目安装的 Vite,先清理再生成固定的 `dist/`,并校验小程序入口、Manifest 和可上传产物。它不执行类型检查,不要求 CLI 登录或绑定小程序,也不访问远端服务。项目必须在 `vite.config.ts` 中显式注册 `miniappManifest()`。
160
166
 
161
- 直接运行 `hb-sdk build` 或 `vite build` 时默认注入平台 CSP。`hb-sdk remote deploy` 会在构建前读取绑定小程序的远端权限;仅当 `network.request.status=enabled` 时向子构建传递已验证上下文并跳过平台 CSP,`useOfficialDomain` 不参与该判定。权限缺失、非法或读取失败时继续注入,Runtime Gate、Manifest 与 HTML 构建检查始终保留。
167
+ 直接运行 `hb-sdk build` 或 `vite build` 时默认注入平台 CSP。`hb-sdk remote deploy` 会在构建前读取绑定小程序的远端批准结果,并与当前版本声明取交集;仅当有效 `network` 权限启用时才跳过平台 CSP,`useOfficialDomain` 不参与该判定。权限缺失、非法或读取失败时继续注入,Runtime Gate、Manifest 与 HTML 构建检查始终保留。
162
168
 
163
169
  推荐由项目的 `scripts.build` 保留类型检查:
164
170
 
@@ -278,3 +284,9 @@ Agent rules:
278
284
  - If doctor reports `SDK_MISMATCH`, upgrade the project dependency to the matching @heybox/hb-sdk version before reinstalling the skill.
279
285
 
280
286
  ## Update reminders
287
+
288
+ ## 版本提醒
289
+
290
+ CLI 会从 npm 的 `latest` 标签检查稳定版更新,发现新版本时在命令结束后输出一行提醒,包含升级命令和目标版本的[更新日志](https://docs.xiaoheihe.cn/hb_sdk/changelog/)链接。提醒最多每 24 小时检查一次,请求超时或网络异常不会影响原命令;CI 环境默认不检查。
291
+
292
+ 需要临时关闭本地检查时,设置 `HB_SDK_NO_UPDATE_CHECK=1`。版本提醒不会读取 Alpha、Beta 或 RC 等预发布标签。
@@ -105,7 +105,7 @@ try {
105
105
  }
106
106
  ```
107
107
 
108
- ### Sandbox download contract (Host pending)
108
+ ### Sandbox download contract (new PC)
109
109
 
110
110
  ```ts
111
111
  import { files, network, HbMiniProgramSDKError } from '@heybox/hb-sdk';
@@ -185,8 +185,8 @@ export default defineConfig({
185
185
  - Do not call unsupported storage delete/clear/info operations.
186
186
  - Do not pass raw internal share/network protocol fields from mini-program code.
187
187
  - Do not send `multipart/form-data` (or handcrafted multipart bodies) through `network.request`; use form-urlencoded string body or a dedicated upload capability.
188
- - Do not pass string paths, browser File System Access handles, Blob targets, Range/resume fields, or upload bodies to `network.download`; use SDK-created File/Directory handles after a supporting Host is available.
189
- - Do not treat any current Host, including legacy PC or Browser Dev Host, as evidence for files/download support. They return `METHOD_FORBIDDEN` and provide no memory fallback; the new PC Host will integrate the retained contract separately.
188
+ - Do not pass string paths, browser File System Access handles, Blob targets, Range/resume fields, or upload bodies to `network.download`; use SDK-created File/Directory handles on new PC.
189
+ - Do not treat Mobile, Web, Browser Dev Host, or legacy PC as evidence for files/download support. Only new PC currently implements the retained contract; other Hosts return `METHOD_FORBIDDEN` and provide no memory fallback.
190
190
  - Do not treat `hb-sdk login` as iframe SDK authentication state.
191
191
  - Do not create a second mock runtime package when `hb-sdk dev` is the supported local mock workflow.
192
192
  - Do not import `@heybox/hb-sdk/vite` from iframe business code or fetch a deployed `manifest.json` directly.
@@ -26,7 +26,9 @@
26
26
 
27
27
  # 快速开始
28
28
 
29
- 如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;业务只需根据小程序是否开通网络权限选择身份流程。
29
+ 新项目可先运行 `hb-sdk create my-miniapp` 获得 Vanilla JavaScript + Vite HelloWorld;模板只演示 Toast,业务代码可以直接替换。
30
+
31
+ 如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;使用受保护能力前需在 `package.json#heybox.permissions` 声明对应权限,详见[权限声明](https://docs.xiaoheihe.cn/hb_sdk/guide/permissions)。
30
32
 
31
33
  ## 交互控件与握手状态
32
34
 
@@ -47,9 +49,9 @@ onUnmounted(stopHandshakeState)
47
49
 
48
50
  把 `sdkReady` 用作按钮的 `disabled` 条件。订阅会立即回放当前状态,因此晚挂载组件也能得到 `ready` 或 `failed` 终态。`ready` 生命周期事件只在握手成功瞬间派发且不会重放,不能作为当前状态来源。
49
51
 
50
- ## 未开通网络权限
52
+ ## 读取当前用户
51
53
 
52
- 无网络小程序可以在本地能力中使用按小程序隔离的 `app_user_id` 和公开资料:
54
+ 声明 `userInfo` 后,可以读取按小程序隔离的 `app_user_id` 和已授权资料。该能力不依赖公开 `network` 权限:
53
55
 
54
56
  ```ts
55
57
  import hbSDK from '@heybox/hb-sdk'
@@ -62,9 +64,9 @@ async function getCurrentUser() {
62
64
 
63
65
  `user.getInfo()` 不展示授权弹窗,也不要求可信用户手势。未登录时返回未登录状态;已登录时隐式授予当前本地用户 scopes(`identity`、`profile`),静默返回 `userInfo.app_user_id`、`profile.nickname` 和 `profile.avatar`。这条路径不能获取授权码或调用 OpenAPI,也不能把身份或资料发送到外部服务。
64
66
 
65
- ## 已开通网络权限
67
+ ## 获取服务端授权码
66
68
 
67
- 有网络小程序通过 `auth.login()` 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作:
69
+ 声明 `userInfo` 后,可通过 `auth.login()` 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作。该调用内部使用 Host 受信请求,不依赖公开 `network` 权限;示例中的 `network.request()` 另需声明并获得 `network` 平台批准。
68
70
 
69
71
  SDK 会自动完成握手。登录按钮应按上面的持久握手状态启用;点击回调直接调用 `auth.login()`,不要先等待其他异步任务,以免首次操作丢失可信手势。
70
72
 
@@ -133,15 +135,14 @@ try {
133
135
 
134
136
  | Host | `files.sandbox` | 外部 picker | `network.download()` |
135
137
  | ---------------- | --------------- | ----------- | -------------------- |
136
- | 旧版 PC | 不支持 | 不支持 | 不支持 |
138
+ | PC | 支持 | 支持 | 支持 |
137
139
  | Mobile | 不支持 | 不支持 | 不支持 |
138
140
  | Web | 不支持 | 不支持 | 不支持 |
139
141
  | Browser Dev Host | 不支持 | 不支持 | 不支持 |
140
142
 
141
143
  当前 Runtime 认识但 Host 未实现时返回 `METHOD_FORBIDDEN`;旧 Runtime 收到新 method 时返回
142
- `METHOD_NOT_FOUND`。此前曾实现面向旧版小黑盒 PC Host 适配;由于新版 PC 即将启用,该适配
143
- 不再发布。文件/下载 API、协议和 Runtime 体系保持不变,后续直接按照新版 PC Host 架构接入。
144
- 当前任何 Host 都不提供 Blob 或内存假下载。
144
+ `METHOD_NOT_FOUND`。新 PC V1 注入真实文件与流式下载 primitive;其他当前 Host 未实现时仍不
145
+ 提供 throwing stub、Blob 或内存假下载。
145
146
 
146
147
  ### 路径与创建
147
148
 
@@ -167,9 +168,10 @@ async function saveFromUserAction(text: string) {
167
168
  }
168
169
  ```
169
170
 
170
- `saveFile()` 打开系统保存对话框并返回精确 File 授权,不会同时创建或保留 Directory
171
- 授权。V1 不接受 `accept`;扩展名过滤只属于 `pickFiles({ accept: ['.json'] })`。外部授权仅在当前
172
- Runtime session 有效。
171
+ `saveFile()` 请求 Host 返回精确 File 授权,不会同时创建或保留 Directory 授权。新 PC 当前先让
172
+ 用户选择保存目录,再以 `suggestedName` 独占创建空文件;同名文件抛出 `FILE_ALREADY_EXISTS`,
173
+ 不会静默覆盖。V1 不接受 `accept`;扩展名过滤只属于 `pickFiles({ accept: ['.json'] })`。外部授权
174
+ 仅在当前 Runtime session 有效。
173
175
 
174
176
  `pickFiles()` / `pickDirectory()` 默认返回 `mode: 'read'` 的授权;只有显式传入
175
177
  `multiple: true` 时,`pickFiles()` 才允许 Host 返回多个文件。需要修改文件、在所选目录中创建
@@ -200,16 +202,17 @@ Runtime session 有效。
200
202
 
201
203
  `app_user_id` 是平台为“用户 × 当前小程序”生成的稳定、隔离标识,不是黑盒用户 ID。它是不透明字符串,只能在当前小程序内使用,不能解析或跨小程序关联。
202
204
 
203
- 用户身份接入取决于小程序是否已开通 `network.request`。两类小程序使用不同的身份入口:
205
+ 身份能力与公开 `network` 权限相互独立。根据业务需要选择入口,并在 `package.json#heybox.permissions` 声明对应权限:
204
206
 
205
- | 小程序类型 | 身份入口 | 结果用途 |
206
- | -------------- | ------------------------- | ------------------------------------------------------ |
207
- | 已开通网络权限 | `auth.login({ scopes? })` | 获取短期授权码,由开发者服务端换取 OpenAPI token |
208
- | 未开通网络权限 | `user.getInfo()` | 静默读取隔离身份及昵称头像,隐式授予本地用户 scopes |
207
+ | 身份入口 | 权限 | 结果用途 |
208
+ | ------------------------- | -------------- | ------------------------------------------------ |
209
+ | `auth.login({ scopes? })` | `userInfo` | 获取短期授权码,由开发者服务端换取 OpenAPI token |
210
+ | `user.getInfo()` | `userInfo` | 读取当前用户的隔离身份及已授权资料 |
211
+ | `user.getSteamGameList()` | `steamLibrary` | 读取当前用户的 Steam 游戏库 |
209
212
 
210
213
  不要混用两条路径。SDK 不向小程序页面提供 token、cookie、黑盒用户 ID 或平台私有凭据。
211
214
 
212
- ## 已开通网络权限
215
+ ## 获取服务端授权码
213
216
 
214
217
  在登录、绑定账号或读取资料等明确的用户操作中调用 `auth.login()`:
215
218
 
@@ -263,9 +266,13 @@ async function loginFromUserAction() {
263
266
 
264
267
  用户已授权所需 scope 时,`auth.login()` 可以不展示 UI,静默返回新的短期授权码。业务仍应把可能出现的授权 UI 设计在明确的用户操作之后,不要在页面初始化阶段自动调用。
265
268
 
269
+ `auth.login()` 内部使用 Host 的受信请求,不会调用小程序公开的 `network.request()`,因此不要求声明 `network`。如果页面需要把 code 通过 `network.request()` 发给开发者服务端,则该请求本身仍需声明并获得 `network` 的平台批准。
270
+
271
+ OpenAPI 凭据、授权码和 access token 的生命周期也不由 `network` 权限开启或关闭。
272
+
266
273
  ## 撤销当前小程序授权
267
274
 
268
- 已开通和未开通网络权限的小程序都可以在明确的用户操作中撤销授权:
275
+ 任何小程序都可以在明确的用户操作中撤销授权;`user.revokeAuthorization()` 位于无需声明白名单:
269
276
 
270
277
  ```ts
271
278
  async function revokeAuthorizationFromUserAction() {
@@ -279,22 +286,11 @@ async function revokeAuthorizationFromUserAction() {
279
286
 
280
287
  `user.revokeAuthorization()` 不接收参数。小程序页面不能提交小程序 ID、黑盒用户 ID 或手势标记;Runtime 从 Host 启动上下文和用户操作状态注入可信值。调用缺少可信用户手势时返回 `USER_GESTURE_REQUIRED`。
281
288
 
282
- 撤销成功后,后续 `auth.login()` 会重新进入授权流程;无网络小程序再次调用 `user.getInfo()` 时会重新建立隔离身份。业务必须立即停止使用并清理此前保存的 `app_user_id`、用户资料和开发者服务端会话;SDK 不会删除业务自己的 localStorage、数据库或 cookie。
283
-
284
- ## 当前用户资料由服务端读取
285
-
286
- 已开通网络权限后,页面不再直接读取 Host 当前用户资料。`@heybox/hb-sdk` 根包实际导出的以下调用统一返回 `SERVER_API_REQUIRED`:
289
+ 撤销成功后,后续 `auth.login()` 会重新进入授权流程;再次调用 `user.getInfo()` 时会按现有用户授权规则读取身份。业务必须立即停止使用并清理此前保存的 `app_user_id`、用户资料和开发者服务端会话;SDK 不会删除业务自己的 localStorage、数据库或 cookie。
287
290
 
288
- - `user.getInfo()`
289
- - `user.getSteamGameList(options)`
291
+ ## 读取当前用户资料
290
292
 
291
- 其他 Host current-user capabilities 属于 Host/runtime 集成边界,不是小程序可从 SDK 根包调用的方法。需要对应数据时,先取得授权码,再由开发者服务端按后端 OpenAPI 文档换取 token 并调用对应接口。不要把 `SERVER_API_REQUIRED` 当作未登录或权限弹窗失败。
292
-
293
- `user.revokeAuthorization()` 是例外:它是授权变更操作,在两种网络模式下都可用,但始终要求可信用户手势。
294
-
295
- ## 未开通网络权限
296
-
297
- 未开通网络权限的小程序不走授权码流程。需要当前用户在本小程序内的稳定隔离身份和公开资料时调用:
293
+ 需要当前用户在本小程序内的稳定隔离身份和已授权资料时调用:
298
294
 
299
295
  ```ts
300
296
  import hbSDK from '@heybox/hb-sdk'
@@ -305,14 +301,16 @@ async function getCurrentUser() {
305
301
  }
306
302
  ```
307
303
 
308
- `user.getInfo()` 是静默本地用户接口,不展示授权弹窗,也不要求可信用户手势。未登录时返回 `isHeyboxAppLoggedIn: false`;已登录时隐式授予当前本地用户 scopes(`identity`、`profile`),建立并返回当前小程序隔离的 `app_user_id`、`profile.nickname` 和 `profile.avatar`。每次成功调用都会广播一次 `user_info_authorization_change`,其中 `identity` 与 `profile` 均为 `granted`,即使授权状态没有发生变化。撤销授权后再次调用仍会静默建立新的隔离身份并恢复无网络模式的隐式授权。
304
+ `user.getInfo()` 不依赖 `network`,但必须声明 `userInfo`。它延续现有交互:不展示授权弹窗,也不要求可信用户手势。未登录时返回 `isHeyboxAppLoggedIn: false`;已登录时按现有用户授权状态返回当前小程序隔离的 `app_user_id`,以及已授权的 `profile.nickname` 和 `profile.avatar` 等可用资料。用户授权与开发者权限声明是两层独立门禁,不能用声明代替用户同意。
309
305
 
310
- 这类小程序无法获取授权码,也无法调用 OpenAPI;在未开通网络权限时调用 `auth.login()` 同样返回 `SERVER_API_REQUIRED`。隐式 full-scope 仅适用于 Host 本地用户资料能力,不代表开放网络或服务端凭据;页面不能把其中的身份或资料发送到外部服务。
306
+ Steam 游戏库使用 `user.getSteamGameList(options)`,要求声明 `steamLibrary`,同样不依赖 `network`。Host current-user capabilities 的既有账号绑定和数据可用性规则保持不变。
311
307
 
312
308
  ## 网络请求边界
313
309
 
314
310
  `network.request()` 只用于访问开发者自己的业务服务。平台保留的 runtime auth 与 OpenAPI 内部路径不能通过该能力访问;身份交换必须由开发者服务端按公开后端文档完成。
315
311
 
312
+ 完整权限配置与错误区别见[权限声明](https://docs.xiaoheihe.cn/hb_sdk/guide/permissions)。
313
+
316
314
  ## CLI 登录与用户登录
317
315
 
318
316
  `hb-sdk login` 只给本地开发和远端管理命令建立 CLI 登录态,与小程序页面的 `auth.login()`、`user.getInfo()` 和用户授权完全无关。
@@ -370,7 +368,7 @@ onUnmounted(stopLifecycleEvents)
370
368
  - 当前握手状态只通过 `getHandshakeState()` 查询、通过 `onHandshakeStateChange()` 订阅;订阅会立即回放当前值并返回取消函数。
371
369
  - `ready` 事件只在握手成功瞬间派发,不会向晚订阅者重放,不能用于驱动按钮可用状态或替代握手状态 API。
372
370
  - UI 可见性相关逻辑放在 `show`、`hide`。
373
- - 使用 Host current-user APIs 的无网络小程序,可以在 `heybox_app_login_change` 后刷新本地状态。已开通网络权限的小程序使用 `auth.login()` 获取 code,并由开发者服务端维护业务会话。
371
+ - 使用 `user.getInfo()` 的小程序可以在 `heybox_app_login_change` 后刷新本地状态。需要开发者服务端会话时使用 `auth.login()` 获取 code;两者都不依赖公开 `network` 权限。
374
372
  - 组件或页面销毁时清理 `on` 注册的监听,避免重复响应。
375
373
  - 生命周期事件只派发给注册当时存在的监听器,不会重放;一次性 `launch` 信息应在页面启动阶段通过业务能力调用处理。
376
374
  - 收到 `unload` 后,当前 SDK 上下文不可恢复,未完成请求会失败;不要在同一页面上下文继续重试能力调用。
@@ -409,6 +407,8 @@ try {
409
407
 
410
408
  - 根据 `error.code` 区分运行环境、权限、超时和业务失败,不要只比对错误文案。
411
409
  - 权限失败时给出可理解的提示,不要将它当成未登录。
410
+ - `PERMISSION_NOT_DECLARED` 表示当前版本没有在 `package.json#heybox.permissions` 声明所需权限,应修改项目配置并重新构建、提交版本。
411
+ - `PERMISSION_DENIED` 表示权限已声明,但需要的平台批准缺失或批准配置不足。首期只有 `network` 需要平台批准。
412
412
  - `USER_GESTURE_REQUIRED` 表示潜在授权 UI 缺少可信用户手势,应让用户点击按钮后重试。
413
413
  - `AUTHORIZATION_CANCELLED` 表示用户取消、拒绝或关闭授权页面,应正常结束当前操作。
414
414
  - `SERVER_API_REQUIRED` 表示当前用户数据必须经开发者服务端 OpenAPI 获取,不应在页面重试对应 Host API。
@@ -473,7 +473,7 @@ picker 取消、授权撤销、文件缺失、quota、下载状态或取消,
473
473
 
474
474
  # 服务端登录门禁
475
475
 
476
- 已开通网络权限的小程序可以把“取得授权码并交给开发者服务端”收敛成一个用户操作函数:
476
+ 声明 `userInfo` 后,小程序可以把“取得授权码并交给开发者服务端”收敛成一个用户操作函数。`auth.login()` 本身不依赖 `network`;以下示例使用 `network.request()` 传递 code,因此还需声明并获得 `network` 的平台批准:
477
477
 
478
478
  示例中的 URL 是开发者自己的后端接口,不是黑盒 OpenAPI 地址。
479
479
 
@@ -523,7 +523,7 @@ async function handleSubmit() {
523
523
 
524
524
  ## 普通分享
525
525
 
526
- 普通分享只支持一个默认分区,并且配置 `post` 时不要同时指定站外 `channel`。
526
+ 普通分享的落地页固定为当前小程序的 `common_share`,不接受自定义 `url`。普通分享只支持一个默认分区,并且配置 `post` 时不要同时指定站外 `channel`。
527
527
 
528
528
  ```ts
529
529
  import { share } from '@heybox/hb-sdk'
@@ -555,7 +555,7 @@ await share.screenshot({
555
555
 
556
556
  ## 恢复分享页面状态
557
557
 
558
- 使用默认通用分享链接时,可以通过 `extra` 携带由小程序自行定义的页面状态。分享方只负责写入状态,接收方负责决定如何使用:
558
+ 通过固定通用分享链接分享时,可以使用 `extra` 携带由小程序自行定义的页面状态。分享方只负责写入状态,接收方负责决定如何使用:
559
559
 
560
560
  ```ts
561
561
  await share.showShareMenu({
@@ -588,7 +588,7 @@ if (sharedState && typeof sharedState === 'object' && !Array.isArray(sharedState
588
588
  }
589
589
  ```
590
590
 
591
- `getExtra()` 不依赖异步请求;没有有效数据时返回 `undefined`,业务应回退到默认首页。`extra` 只支持 JSON-compatible 数据,最多 8 层,单个数组或对象最多 64 项,内容不超过 128 字节;不能与自定义 `url` 同时使用,也不要存放 token、个人信息等敏感数据。`copyLink()` 不会继承当前启动链接中的 `extra`,只携带本次显式传入的数据。
591
+ `getExtra()` 不依赖异步请求;没有有效数据时返回 `undefined`,业务应回退到默认首页。`extra` 只支持 JSON-compatible 数据,最多 8 层,单个数组或对象最多 64 项,内容不超过 128 字节,也不要存放 token、个人信息等敏感数据。`copyLink()` 不会继承当前启动链接中的 `extra`,只携带本次显式传入的数据。
592
592
 
593
593
  ## 参数边界
594
594
 
@@ -611,7 +611,6 @@ if (sharedState && typeof sharedState === 'object' && !Array.isArray(sharedState
611
611
 
612
612
  ```ts
613
613
  import hbSDK from '@heybox/hb-sdk'
614
-
615
614
  ```
616
615
 
617
616
  ## 测试环境注入 window
@@ -15,16 +15,16 @@
15
15
  - `ready` 生命周期事件是不可重放的边沿通知,不代表可查询状态,也不能替代握手状态 API。
16
16
  - `auth.login({ scopes? })` 只返回 `{ code, expiresIn: 300, scopes }`;identity 隐式强制包含,code 只能提交给开发者服务端。
17
17
  - 需要授权 UI 时,`auth.login()` 必须来自可信用户手势,否则返回 `USER_GESTURE_REQUIRED`;用户取消或关闭时返回 `AUTHORIZATION_CANCELLED`。已授权时可以静默返回新 code。
18
- - `user.getSteamGameList()` 仅供未开通网络权限的小程序通过 Host 读取;已开通网络权限时返回 `SERVER_API_REQUIRED`,且授权码与 OpenAPI 不提供 Steam 游戏库 scope 或资源接口。
19
- - 已开通网络权限时,公开的 `user.getInfo()` 返回 `SERVER_API_REQUIRED`;对应身份和资料数据应由开发者服务端通过 OpenAPI 获取。
20
- - 未开通网络权限时,`user.getInfo()` 隐式授予 `identity` 与 `profile`,静默返回 `userInfo.app_user_id`、昵称和头像;这条路径仍不能获取 code 或调用 OpenAPI。
18
+ - `auth.login()`、`user.getInfo()` `user.getSteamGameList()` 的可用性不由 `network` 决定;它们分别要求 `userInfo` `steamLibrary`,并继续遵循各自的用户授权和数据规则。
21
19
  - `user.revokeAuthorization()` 在两种网络模式下都要求可信用户手势;成功后必须停止使用并清理业务缓存的旧身份、资料和服务端会话。
22
20
  - `on()` 返回取消监听函数,组件卸载或页面销毁时需要调用。
23
21
  - 业务网络请求使用 `network.request()`,不要依赖浏览器原生网络出口。
24
22
  - 公开 `network.request()` 与 `network.download()` 均使用 no-follow redirect policy;3xx 不会在 Host 内静默跳转。
25
23
  - `network.request()` 不能访问平台保留的 runtime auth 与 OpenAPI 内部路径;页面不得持有或交换服务端应用凭据。
26
24
  - `network.request` 不支持 `multipart/form-data`;App Host 仅支持 form(`application/x-www-form-urlencoded`)与 JSON。表单请用 `URLSearchParams#toString()` 作为 `data`,文件上传请走专用上传能力。
27
- - 当前 Host(包括旧版 PC、Mobile、Web Browser Dev Host)均未开放 `files` / `network.download()`;新版 PC 将按同一公开合同重新接入,期间不提供内存或 Blob 假实现。
25
+ - PC 开放持久 sandbox、单文件/目录 picker、精确 File 保存与公网 `network.download()`;多选和小黑盒域名流式下载暂不支持。Mobile、Web Browser Dev Host 仍未开放这些 primitive。
26
+ - 新 PC 支持 `network.request`,但会忽略 `withCredentials`;它不改变请求路由或凭据行为。其他平台继续遵循各自的 Host 语义。
27
+ - 新 PC 不支持振动、分享菜单和截图分享;`navigation.openAppPage` 仅支持 `game_detail`,用户/帖子详情会明确失败。调用平台相关能力时应处理 `METHOD_FORBIDDEN` 或 `REQUEST_UNSUPPORTED`。
28
28
  - picker 必须来自可信用户手势;取消选择返回 `FILE_PICKER_CANCELLED`。`saveFile()` 只接受 `suggestedName`,不接受 `accept`。
29
29
  - `pickFiles()` / `pickDirectory()` 默认只读;`pickFiles()` 仅在显式传入 `multiple: true` 时允许 Host 返回多个文件。要写入或作为下载目标时显式请求 `mode: 'readwrite'`。
30
30
  - `file()` / `directory()` 只接受以 `/` 分隔的规范相对路径;拒绝绝对路径、空 segment、`.`、`..`、反斜杠、尾分隔符和控制字符。
@@ -34,9 +34,13 @@
34
34
  - 单个 Runtime session 最多保留 1024 个 picker/saveFile direct handle;整批授权超限时返回 `FILE_QUOTA_EXCEEDED`,不会保留部分结果。
35
35
  - Host 文件错误只保留稳定 code 与 canonical 安全文案;原始 message、data、native path 和 credential 不会进入小程序。
36
36
  - `FileStat.size` 是非负 safe integer;`modifiedAt` / `createdAt` 若存在,单位为 Unix epoch milliseconds。
37
- - 仅当远端已启用 `network.request` 时,`hb-sdk dev` 与 `hb-sdk remote deploy` 构建才会跳过平台 CSP;其他构建继续注入平台 CSP。`useOfficialDomain` 不参与 CSP 跳过判定。
37
+ - 仅当当前版本声明且平台已批准 `network` 时,`hb-sdk dev` 与 `hb-sdk remote deploy` 构建才会跳过平台 CSP;其他构建继续注入平台 CSP。`useOfficialDomain` 不参与 CSP 跳过判定。
38
38
  - 构建必须启用 `miniappManifest({ platforms })` 并显式声明实际承诺适配的平台,推荐统一使用 `hb-sdk build`。新项目模板中的 `['android', 'ios', 'ohos']` 只是初始配置,不代表已经完成真机验收。
39
39
 
40
+ 私有 Page Channel 适配没有修改 `@heybox/hb-sdk` 公开 API 或 iframe wire;本次 files/download
41
+ Runtime 能力随 `0.8.0-alpha.11` release family 发布,部署 detail 前必须保证
42
+ `@heybox/hb-sdk`、`@heybox/hb-sdk-runtime` 与 `@heybox/hb-sdk-protocol` 三个 package 均可解析。
43
+
40
44
  ## Agent rules
41
45
 
42
46
  - Do not instruct mini-program code to read, extract, forward, store, or depend on token, cookie, phone number, or private credentials.
package/skill/skill.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "hb-sdk",
3
- "skillVersion": "0.8.0-alpha.3+skill.9d9d09c2d52c",
3
+ "skillVersion": "0.8.0+skill.7bb8515ab71e",
4
4
  "sdk": {
5
5
  "package": "@heybox/hb-sdk",
6
- "version": "0.8.0-alpha.3",
7
- "compatibility": "0.8.0-alpha.3"
6
+ "version": "0.8.0",
7
+ "compatibility": "0.8.0"
8
8
  },
9
9
  "distribution": {
10
10
  "type": "npm",
11
11
  "package": "@heybox/hb-sdk",
12
- "version": "0.8.0-alpha.3",
12
+ "version": "0.8.0",
13
13
  "path": "skill"
14
14
  },
15
- "integrity": "sha256-9d9d09c2d52ce6fca7e0ea48bcfa51bad464502d92e32e97dd0d1cad321fae52"
15
+ "integrity": "sha256-7bb8515ab71e004724601d7e09d0e5058888b53a7486b7ed15535ba9a8cdb93c"
16
16
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * iframe 与调试台页面的开发期 console 捕获转发。
3
+ *
4
+ * 双条件门控(iframe 转发):
5
+ * 1. 构建期:`hb-sdk dev`(vite serve)注入 `__HB_SDK_DEV_LOGGING__`;`hb-sdk build` /
6
+ * `vite build`(生产)永不注入或替换为 `false`,本模块被死代码消除。
7
+ * 2. 运行期:仅在 `window.name` 为 `__hb_sdk_mini_dev__` 的调试 iframe 中激活,
8
+ * 用户设备上的正式运行环境没有该标记。
9
+ *
10
+ * 无侵入 patch `console.log/debug/info/warn/error`:原样放行(DevTools 不受影响)+
11
+ * 通过 `window.parent.postMessage` 转发给调试台(CSP 的 connect-src 不约束 postMessage,
12
+ * 网络上报由调试台页面代理)。发送全程静默:失败不得回打 console,避免自触发死循环。
13
+ *
14
+ * 激活时机:由 SDK 默认单例初始化时调用(业务 `import '@heybox/hb-sdk'` 即生效);
15
+ * iframe 标记由调试台 `iframe.name` 与 vite dev 注入的无依赖 bootstrap 共同设置。
16
+ * 调试台页面自身(runtime 跑在这里,`[hb-sdk-runtime]` 日志不经过 iframe)另用
17
+ * `installMiniDevConsoleSelfCapture` 接入「日志」页签(source: 'host')。
18
+ */
19
+ export declare const MINI_DEV_IFRAME_WINDOW_NAME = "__hb_sdk_mini_dev__";
20
+ export declare const MINI_DEV_CONSOLE_EVENT_TYPE = "hb-sdk:mini-dev-console";
21
+ /** 对外契约:调试台监听该 postMessage 读取转发日志;message 结构必须稳定。 */
22
+ export interface MiniDevConsoleMessage {
23
+ type: typeof MINI_DEV_CONSOLE_EVENT_TYPE;
24
+ detail: MiniDevConsoleEventDetail;
25
+ }
26
+ export type MiniDevConsoleLevel = 'log' | 'debug' | 'info' | 'warn' | 'error';
27
+ export interface MiniDevConsoleEventDetail {
28
+ level: MiniDevConsoleLevel;
29
+ args: unknown[];
30
+ timestamp: number;
31
+ }
32
+ /** 调试台页面与 iframe bootstrap 共用的已安装标记(window/console 上的属性名)。 */
33
+ export declare const MINI_DEV_CONSOLE_INSTALL_FLAG = "__hb_sdk_mini_dev_console_installed__";
34
+ /** 调试台页面(非 iframe)自捕获:runtime/host 侧 console 直接进「日志」页签。 */
35
+ export declare function installMiniDevConsoleSelfCapture(onLog: (level: MiniDevConsoleLevel, args: unknown[]) => void, consoleRef?: Console): void;
36
+ export declare function installMiniDevConsoleForwarding(consoleRef?: Console): void;