@heybox/hb-sdk 0.8.0-alpha.9 → 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 (48) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +25 -19
  3. package/dist/cli-chunks/{build-DkokKVNy.cjs → build-PYCNacya.cjs} +8 -5
  4. package/dist/cli-chunks/{context-CKzZxKbF.cjs → context-m2W2XbL0.cjs} +42 -59
  5. package/dist/cli-chunks/{create-BQy5Jdmu.cjs → create-BdAg3WGA.cjs} +1 -1
  6. package/dist/cli-chunks/{dev-Dik2zr5R.cjs → dev-CyZuw7Yn.cjs} +26 -15
  7. package/dist/cli-chunks/{doctor-Byr4uwKG.cjs → doctor-DU8rCfUF.cjs} +1 -1
  8. package/dist/cli-chunks/{index-Cl0XaX8e.cjs → index-DATObqzK.cjs} +2 -2
  9. package/dist/cli-chunks/{index-BwGBr1ZA.cjs → index-MMW2ibQm.cjs} +15 -15
  10. package/dist/cli-chunks/{index.esm-BYifBABc.cjs → index.esm-BiAaAUFC.cjs} +8 -8
  11. package/dist/cli-chunks/{login-Dr9lclu3.cjs → login-B3TThMss.cjs} +2 -2
  12. package/dist/cli-chunks/{project-vite-BIPrOtRO.cjs → project-vite-BQj8YLI4.cjs} +1 -1
  13. package/dist/cli-chunks/{remote-DXxyA14a.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-B6Mo9eYW.cjs → session-DjBkjaF8.cjs} +1 -1
  17. package/dist/cli-chunks/{skill-f90sxv28.cjs → skill-cR_wnaw2.cjs} +2 -2
  18. package/dist/cli-chunks/{version-DDB_btOG.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-DaqmTjSF.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-wHgWRb7I.js → workbench-state-BwV7bm4n.js} +2 -2
  24. package/dist/devtools/browser-dev-host/index.html +3 -3
  25. package/dist/index.cjs.js +99 -1
  26. package/dist/index.esm.js +99 -1
  27. package/dist/protocol.cjs.js +189 -47
  28. package/dist/protocol.esm.js +189 -47
  29. package/dist/templates/vanilla-vite-js/package.json.ejs +2 -1
  30. package/dist/vite.cjs.js +281 -13
  31. package/dist/vite.esm.js +281 -13
  32. package/package.json +5 -5
  33. package/skill/SKILL.md +8 -9
  34. package/skill/references/api-protocol.md +1 -1
  35. package/skill/references/api-root.md +63 -21
  36. package/skill/references/cli.md +6 -6
  37. package/skill/references/examples.md +3 -3
  38. package/skill/references/recipes.md +35 -38
  39. package/skill/references/safety-boundaries.md +9 -5
  40. package/skill/skill.json +5 -5
  41. package/types/core/mini-dev-console.d.ts +36 -0
  42. package/types/miniapp-manifest/index.d.ts +1 -0
  43. package/types/miniapp-manifest/node.d.ts +3 -0
  44. package/types/miniapp-manifest/permissions.d.ts +37 -0
  45. package/types/miniapp-manifest/schema.d.ts +5 -0
  46. package/types/vite/index.d.ts +3 -1
  47. package/dist/devtools/browser-dev-host/assets/browser-dev-host-CbhA4h8z.js +0 -97
  48. package/dist/devtools/browser-dev-host/assets/index-DJFB5ySU.css +0 -1
@@ -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.9`
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
 
@@ -441,7 +481,7 @@ hb-sdk build [--env <name>] [--verbose]
441
481
 
442
482
  新项目可先运行 `hb-sdk create my-miniapp` 获得 Vanilla JavaScript + Vite HelloWorld;模板只演示 Toast,业务代码可以直接替换。
443
483
 
444
- 如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;业务只需根据小程序是否开通网络权限选择身份流程。
484
+ 如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;使用受保护能力前需在 `package.json#heybox.permissions` 声明对应权限,详见[权限声明](https://docs.xiaoheihe.cn/hb_sdk/guide/permissions)。
445
485
 
446
486
  ## 交互控件与握手状态
447
487
 
@@ -462,9 +502,9 @@ onUnmounted(stopHandshakeState)
462
502
 
463
503
  把 `sdkReady` 用作按钮的 `disabled` 条件。订阅会立即回放当前状态,因此晚挂载组件也能得到 `ready` 或 `failed` 终态。`ready` 生命周期事件只在握手成功瞬间派发且不会重放,不能作为当前状态来源。
464
504
 
465
- ## 未开通网络权限
505
+ ## 读取当前用户
466
506
 
467
- 无网络小程序可以在本地能力中使用按小程序隔离的 `app_user_id` 和公开资料:
507
+ 声明 `userInfo` 后,可以读取按小程序隔离的 `app_user_id` 和已授权资料。该能力不依赖公开 `network` 权限:
468
508
 
469
509
  ```ts
470
510
  import hbSDK from '@heybox/hb-sdk'
@@ -477,9 +517,9 @@ async function getCurrentUser() {
477
517
 
478
518
  `user.getInfo()` 不展示授权弹窗,也不要求可信用户手势。未登录时返回未登录状态;已登录时隐式授予当前本地用户 scopes(`identity`、`profile`),静默返回 `userInfo.app_user_id`、`profile.nickname` 和 `profile.avatar`。这条路径不能获取授权码或调用 OpenAPI,也不能把身份或资料发送到外部服务。
479
519
 
480
- ## 已开通网络权限
520
+ ## 获取服务端授权码
481
521
 
482
- 有网络小程序通过 `auth.login()` 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作:
522
+ 声明 `userInfo` 后,可通过 `auth.login()` 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作。该调用内部使用 Host 受信请求,不依赖公开 `network` 权限;示例中的 `network.request()` 另需声明并获得 `network` 平台批准。
483
523
 
484
524
  SDK 会自动完成握手。登录按钮应按上面的持久握手状态启用;点击回调直接调用 `auth.login()`,不要先等待其他异步任务,以免首次操作丢失可信手势。
485
525
 
@@ -548,15 +588,14 @@ try {
548
588
 
549
589
  | Host | `files.sandbox` | 外部 picker | `network.download()` |
550
590
  | ---------------- | --------------- | ----------- | -------------------- |
551
- | 旧版 PC | 不支持 | 不支持 | 不支持 |
591
+ | PC | 支持 | 支持 | 支持 |
552
592
  | Mobile | 不支持 | 不支持 | 不支持 |
553
593
  | Web | 不支持 | 不支持 | 不支持 |
554
594
  | Browser Dev Host | 不支持 | 不支持 | 不支持 |
555
595
 
556
596
  当前 Runtime 认识但 Host 未实现时返回 `METHOD_FORBIDDEN`;旧 Runtime 收到新 method 时返回
557
- `METHOD_NOT_FOUND`。此前曾实现面向旧版小黑盒 PC Host 适配;由于新版 PC 即将启用,该适配
558
- 不再发布。文件/下载 API、协议和 Runtime 体系保持不变,后续直接按照新版 PC Host 架构接入。
559
- 当前任何 Host 都不提供 Blob 或内存假下载。
597
+ `METHOD_NOT_FOUND`。新 PC V1 注入真实文件与流式下载 primitive;其他当前 Host 未实现时仍不
598
+ 提供 throwing stub、Blob 或内存假下载。
560
599
 
561
600
  ### 路径与创建
562
601
 
@@ -582,9 +621,10 @@ async function saveFromUserAction(text: string) {
582
621
  }
583
622
  ```
584
623
 
585
- `saveFile()` 打开系统保存对话框并返回精确 File 授权,不会同时创建或保留 Directory
586
- 授权。V1 不接受 `accept`;扩展名过滤只属于 `pickFiles({ accept: ['.json'] })`。外部授权仅在当前
587
- Runtime session 有效。
624
+ `saveFile()` 请求 Host 返回精确 File 授权,不会同时创建或保留 Directory 授权。新 PC 当前先让
625
+ 用户选择保存目录,再以 `suggestedName` 独占创建空文件;同名文件抛出 `FILE_ALREADY_EXISTS`,
626
+ 不会静默覆盖。V1 不接受 `accept`;扩展名过滤只属于 `pickFiles({ accept: ['.json'] })`。外部授权
627
+ 仅在当前 Runtime session 有效。
588
628
 
589
629
  `pickFiles()` / `pickDirectory()` 默认返回 `mode: 'read'` 的授权;只有显式传入
590
630
  `multiple: true` 时,`pickFiles()` 才允许 Host 返回多个文件。需要修改文件、在所选目录中创建
@@ -640,6 +680,8 @@ try {
640
680
 
641
681
  - 根据 `error.code` 区分运行环境、权限、超时和业务失败,不要只比对错误文案。
642
682
  - 权限失败时给出可理解的提示,不要将它当成未登录。
683
+ - `PERMISSION_NOT_DECLARED` 表示当前版本没有在 `package.json#heybox.permissions` 声明所需权限,应修改项目配置并重新构建、提交版本。
684
+ - `PERMISSION_DENIED` 表示权限已声明,但需要的平台批准缺失或批准配置不足。首期只有 `network` 需要平台批准。
643
685
  - `USER_GESTURE_REQUIRED` 表示潜在授权 UI 缺少可信用户手势,应让用户点击按钮后重试。
644
686
  - `AUTHORIZATION_CANCELLED` 表示用户取消、拒绝或关闭授权页面,应正常结束当前操作。
645
687
  - `SERVER_API_REQUIRED` 表示当前用户数据必须经开发者服务端 OpenAPI 获取,不应在页面重试对应 Host API。
@@ -772,7 +814,7 @@ onUnmounted(stopLifecycleEvents)
772
814
  - 当前握手状态只通过 `getHandshakeState()` 查询、通过 `onHandshakeStateChange()` 订阅;订阅会立即回放当前值并返回取消函数。
773
815
  - `ready` 事件只在握手成功瞬间派发,不会向晚订阅者重放,不能用于驱动按钮可用状态或替代握手状态 API。
774
816
  - UI 可见性相关逻辑放在 `show`、`hide`。
775
- - 使用 Host current-user APIs 的无网络小程序,可以在 `heybox_app_login_change` 后刷新本地状态。已开通网络权限的小程序使用 `auth.login()` 获取 code,并由开发者服务端维护业务会话。
817
+ - 使用 `user.getInfo()` 的小程序可以在 `heybox_app_login_change` 后刷新本地状态。需要开发者服务端会话时使用 `auth.login()` 获取 code;两者都不依赖公开 `network` 权限。
776
818
  - 组件或页面销毁时清理 `on` 注册的监听,避免重复响应。
777
819
  - 生命周期事件只派发给注册当时存在的监听器,不会重放;一次性 `launch` 信息应在页面启动阶段通过业务能力调用处理。
778
820
  - 收到 `unload` 后,当前 SDK 上下文不可恢复,未完成请求会失败;不要在同一页面上下文继续重试能力调用。
@@ -76,7 +76,7 @@ Top-level `hb-sdk deploy` has been hard-cut and must not be documented as a vali
76
76
  新项目可以直接从模板开始:
77
77
 
78
78
  ```bash
79
- npx @heybox/hb-sdk@alpha create my-miniapp
79
+ npx @heybox/hb-sdk@latest create my-miniapp
80
80
  cd my-miniapp
81
81
  npm install
82
82
  npm run dev
@@ -84,7 +84,7 @@ npm run dev
84
84
 
85
85
  默认产物是 Vanilla JavaScript + Vite HelloWorld,只演示一次 `ui.showToast()` 调用,并预填 `android`、`ios`、`ohos` 三个平台。Demo 可以整体删除,项目不绑定 Vue、TypeScript 或测试框架。
86
86
 
87
- 当前 `0.8` 模板位于 alpha 发布线;稳定版本发布后,本页会将创建命令切回 `@latest`。
87
+ 当前 `0.8` 已发布为稳定版本,本页使用 `@latest` 创建项目。
88
88
 
89
89
  已有项目则在项目目录运行 `npm run dev` 或 `hb-sdk dev`。
90
90
 
@@ -105,7 +105,7 @@ Agent rules:
105
105
  hb-sdk dev
106
106
  ```
107
107
 
108
- CLI 会启动页面服务并自动打开 Vue 3 本地调试台。左侧集中放置调用日志、Storage 和问题三个诊断页签,右侧保持稳定的小程序内容与设备预览。
108
+ CLI 会启动页面服务并自动打开 Vue 3 本地调试台。左侧集中放置日志、Storage 和问题三个诊断页签,右侧保持稳定的小程序内容与设备预览。
109
109
 
110
110
  默认终端只显示启动结果、调试页地址、小程序地址和手机调试入口。排查启动问题时使用 `hb-sdk dev --verbose` 查看完整启动阶段;手机扫码调试凭证连续重试失败时,默认只在失败和恢复两次状态变化时提示。
111
111
 
@@ -113,7 +113,7 @@ CLI 会启动页面服务并自动打开 Vue 3 本地调试台。左侧集中放
113
113
 
114
114
  浏览器调试入口不要求 CLI 登录或项目绑定。没有账号、绑定小程序或远端 Dev Context 时,页面和基础 Browser Mock 仍可启动;依赖这些上下文的能力会返回明确失败。远端管理、部署和发布仍要求完成登录与绑定。
115
115
 
116
- `hb-sdk dev` 在远端权限快照可用时读取它。只有快照有效且 `network.request.status=enabled` 时才跳过平台 CSP;`useOfficialDomain` 不参与该判定。调试台不提供权限修改入口,远端权限快照始终是 Runtime 的规范输入;快照缺失或无效时保留平台 CSP。
116
+ `hb-sdk dev` 从项目 `package.json` 读取开发者声明,并在远端权限快照可用时读取平台批准结果。只有当前版本声明且平台已批准 `network` 时才跳过平台 CSP;`useOfficialDomain` 不参与该判定。调试台不提供权限或平台批准修改入口;快照缺失或无效时保留平台 CSP。
117
117
 
118
118
  Browser Mock 会自动在实时与兼容链路间切换。手机调试使用 `open_inapp`/`openWindow` 包裹的 LAN 短链接二维码(先开普通 H5 跳转页,再进入小程序并关闭中间页)。
119
119
 
@@ -125,7 +125,7 @@ Browser Mock 会自动在实时与兼容链路间切换。手机调试使用 `op
125
125
  - SDK 初始化与用户身份授权流程
126
126
  - 生命周期、Storage 和排行榜等能力
127
127
 
128
- 调试台提供紧凑的调用日志、当前小程序隔离作用域内的 Storage 只读快照和问题聚合。日志与网络记录只保留脱敏的诊断字段,不展示 payload、结果、查询参数、凭据或原始错误信息。工坊小程序默认不能进行网络请求,网络权限暂未开放申请,不要把本地结果当成线上能力。
128
+ 调试台提供紧凑的日志、当前小程序隔离作用域内的 Storage 只读快照和问题聚合。日志与网络记录只保留脱敏的诊断字段,不展示 payload、结果、查询参数、凭据或原始错误信息。工坊小程序默认不能进行网络请求,网络权限暂未开放申请,不要把本地结果当成线上能力。
129
129
 
130
130
  右侧可切换 iPhone 16 Pro Max(`387 x 821`)与 Pixel 9 Pro(`322 x 716`)两个设备预设。尺寸对应固定上游设备外框的真实屏幕 opening;预设同时决定外框、状态栏、安全区和 viewport。切换设备不会重新加载小程序或重启 Runtime,页面状态和调试会话保持不变。授权与操作弹窗、Toast、Loading 和振动反馈均显示在设备预览内部,不会覆盖整个调试台。
131
131
 
@@ -164,7 +164,7 @@ hb-sdk build [--env <name>] [--verbose]
164
164
 
165
165
  `hb-sdk build` 直接使用项目安装的 Vite,先清理再生成固定的 `dist/`,并校验小程序入口、Manifest 和可上传产物。它不执行类型检查,不要求 CLI 登录或绑定小程序,也不访问远端服务。项目必须在 `vite.config.ts` 中显式注册 `miniappManifest()`。
166
166
 
167
- 直接运行 `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 构建检查始终保留。
168
168
 
169
169
  推荐由项目的 `scripts.build` 保留类型检查:
170
170
 
@@ -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.
@@ -28,7 +28,7 @@
28
28
 
29
29
  新项目可先运行 `hb-sdk create my-miniapp` 获得 Vanilla JavaScript + Vite HelloWorld;模板只演示 Toast,业务代码可以直接替换。
30
30
 
31
- 如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;业务只需根据小程序是否开通网络权限选择身份流程。
31
+ 如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;使用受保护能力前需在 `package.json#heybox.permissions` 声明对应权限,详见[权限声明](https://docs.xiaoheihe.cn/hb_sdk/guide/permissions)。
32
32
 
33
33
  ## 交互控件与握手状态
34
34
 
@@ -49,9 +49,9 @@ onUnmounted(stopHandshakeState)
49
49
 
50
50
  把 `sdkReady` 用作按钮的 `disabled` 条件。订阅会立即回放当前状态,因此晚挂载组件也能得到 `ready` 或 `failed` 终态。`ready` 生命周期事件只在握手成功瞬间派发且不会重放,不能作为当前状态来源。
51
51
 
52
- ## 未开通网络权限
52
+ ## 读取当前用户
53
53
 
54
- 无网络小程序可以在本地能力中使用按小程序隔离的 `app_user_id` 和公开资料:
54
+ 声明 `userInfo` 后,可以读取按小程序隔离的 `app_user_id` 和已授权资料。该能力不依赖公开 `network` 权限:
55
55
 
56
56
  ```ts
57
57
  import hbSDK from '@heybox/hb-sdk'
@@ -64,9 +64,9 @@ async function getCurrentUser() {
64
64
 
65
65
  `user.getInfo()` 不展示授权弹窗,也不要求可信用户手势。未登录时返回未登录状态;已登录时隐式授予当前本地用户 scopes(`identity`、`profile`),静默返回 `userInfo.app_user_id`、`profile.nickname` 和 `profile.avatar`。这条路径不能获取授权码或调用 OpenAPI,也不能把身份或资料发送到外部服务。
66
66
 
67
- ## 已开通网络权限
67
+ ## 获取服务端授权码
68
68
 
69
- 有网络小程序通过 `auth.login()` 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作:
69
+ 声明 `userInfo` 后,可通过 `auth.login()` 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作。该调用内部使用 Host 受信请求,不依赖公开 `network` 权限;示例中的 `network.request()` 另需声明并获得 `network` 平台批准。
70
70
 
71
71
  SDK 会自动完成握手。登录按钮应按上面的持久握手状态启用;点击回调直接调用 `auth.login()`,不要先等待其他异步任务,以免首次操作丢失可信手势。
72
72
 
@@ -135,15 +135,14 @@ try {
135
135
 
136
136
  | Host | `files.sandbox` | 外部 picker | `network.download()` |
137
137
  | ---------------- | --------------- | ----------- | -------------------- |
138
- | 旧版 PC | 不支持 | 不支持 | 不支持 |
138
+ | PC | 支持 | 支持 | 支持 |
139
139
  | Mobile | 不支持 | 不支持 | 不支持 |
140
140
  | Web | 不支持 | 不支持 | 不支持 |
141
141
  | Browser Dev Host | 不支持 | 不支持 | 不支持 |
142
142
 
143
143
  当前 Runtime 认识但 Host 未实现时返回 `METHOD_FORBIDDEN`;旧 Runtime 收到新 method 时返回
144
- `METHOD_NOT_FOUND`。此前曾实现面向旧版小黑盒 PC Host 适配;由于新版 PC 即将启用,该适配
145
- 不再发布。文件/下载 API、协议和 Runtime 体系保持不变,后续直接按照新版 PC Host 架构接入。
146
- 当前任何 Host 都不提供 Blob 或内存假下载。
144
+ `METHOD_NOT_FOUND`。新 PC V1 注入真实文件与流式下载 primitive;其他当前 Host 未实现时仍不
145
+ 提供 throwing stub、Blob 或内存假下载。
147
146
 
148
147
  ### 路径与创建
149
148
 
@@ -169,9 +168,10 @@ async function saveFromUserAction(text: string) {
169
168
  }
170
169
  ```
171
170
 
172
- `saveFile()` 打开系统保存对话框并返回精确 File 授权,不会同时创建或保留 Directory
173
- 授权。V1 不接受 `accept`;扩展名过滤只属于 `pickFiles({ accept: ['.json'] })`。外部授权仅在当前
174
- Runtime session 有效。
171
+ `saveFile()` 请求 Host 返回精确 File 授权,不会同时创建或保留 Directory 授权。新 PC 当前先让
172
+ 用户选择保存目录,再以 `suggestedName` 独占创建空文件;同名文件抛出 `FILE_ALREADY_EXISTS`,
173
+ 不会静默覆盖。V1 不接受 `accept`;扩展名过滤只属于 `pickFiles({ accept: ['.json'] })`。外部授权
174
+ 仅在当前 Runtime session 有效。
175
175
 
176
176
  `pickFiles()` / `pickDirectory()` 默认返回 `mode: 'read'` 的授权;只有显式传入
177
177
  `multiple: true` 时,`pickFiles()` 才允许 Host 返回多个文件。需要修改文件、在所选目录中创建
@@ -202,16 +202,17 @@ Runtime session 有效。
202
202
 
203
203
  `app_user_id` 是平台为“用户 × 当前小程序”生成的稳定、隔离标识,不是黑盒用户 ID。它是不透明字符串,只能在当前小程序内使用,不能解析或跨小程序关联。
204
204
 
205
- 用户身份接入取决于小程序是否已开通 `network.request`。两类小程序使用不同的身份入口:
205
+ 身份能力与公开 `network` 权限相互独立。根据业务需要选择入口,并在 `package.json#heybox.permissions` 声明对应权限:
206
206
 
207
- | 小程序类型 | 身份入口 | 结果用途 |
208
- | -------------- | ------------------------- | ------------------------------------------------------ |
209
- | 已开通网络权限 | `auth.login({ scopes? })` | 获取短期授权码,由开发者服务端换取 OpenAPI token |
210
- | 未开通网络权限 | `user.getInfo()` | 静默读取隔离身份及昵称头像,隐式授予本地用户 scopes |
207
+ | 身份入口 | 权限 | 结果用途 |
208
+ | ------------------------- | -------------- | ------------------------------------------------ |
209
+ | `auth.login({ scopes? })` | `userInfo` | 获取短期授权码,由开发者服务端换取 OpenAPI token |
210
+ | `user.getInfo()` | `userInfo` | 读取当前用户的隔离身份及已授权资料 |
211
+ | `user.getSteamGameList()` | `steamLibrary` | 读取当前用户的 Steam 游戏库 |
211
212
 
212
213
  不要混用两条路径。SDK 不向小程序页面提供 token、cookie、黑盒用户 ID 或平台私有凭据。
213
214
 
214
- ## 已开通网络权限
215
+ ## 获取服务端授权码
215
216
 
216
217
  在登录、绑定账号或读取资料等明确的用户操作中调用 `auth.login()`:
217
218
 
@@ -265,9 +266,13 @@ async function loginFromUserAction() {
265
266
 
266
267
  用户已授权所需 scope 时,`auth.login()` 可以不展示 UI,静默返回新的短期授权码。业务仍应把可能出现的授权 UI 设计在明确的用户操作之后,不要在页面初始化阶段自动调用。
267
268
 
269
+ `auth.login()` 内部使用 Host 的受信请求,不会调用小程序公开的 `network.request()`,因此不要求声明 `network`。如果页面需要把 code 通过 `network.request()` 发给开发者服务端,则该请求本身仍需声明并获得 `network` 的平台批准。
270
+
271
+ OpenAPI 凭据、授权码和 access token 的生命周期也不由 `network` 权限开启或关闭。
272
+
268
273
  ## 撤销当前小程序授权
269
274
 
270
- 已开通和未开通网络权限的小程序都可以在明确的用户操作中撤销授权:
275
+ 任何小程序都可以在明确的用户操作中撤销授权;`user.revokeAuthorization()` 位于无需声明白名单:
271
276
 
272
277
  ```ts
273
278
  async function revokeAuthorizationFromUserAction() {
@@ -281,22 +286,11 @@ async function revokeAuthorizationFromUserAction() {
281
286
 
282
287
  `user.revokeAuthorization()` 不接收参数。小程序页面不能提交小程序 ID、黑盒用户 ID 或手势标记;Runtime 从 Host 启动上下文和用户操作状态注入可信值。调用缺少可信用户手势时返回 `USER_GESTURE_REQUIRED`。
283
288
 
284
- 撤销成功后,后续 `auth.login()` 会重新进入授权流程;无网络小程序再次调用 `user.getInfo()` 时会重新建立隔离身份。业务必须立即停止使用并清理此前保存的 `app_user_id`、用户资料和开发者服务端会话;SDK 不会删除业务自己的 localStorage、数据库或 cookie。
285
-
286
- ## 当前用户资料由服务端读取
287
-
288
- 已开通网络权限后,页面不再直接读取 Host 当前用户资料。`@heybox/hb-sdk` 根包实际导出的以下调用统一返回 `SERVER_API_REQUIRED`:
289
-
290
- - `user.getInfo()`
291
- - `user.getSteamGameList(options)`
289
+ 撤销成功后,后续 `auth.login()` 会重新进入授权流程;再次调用 `user.getInfo()` 时会按现有用户授权规则读取身份。业务必须立即停止使用并清理此前保存的 `app_user_id`、用户资料和开发者服务端会话;SDK 不会删除业务自己的 localStorage、数据库或 cookie。
292
290
 
293
- 其他 Host current-user capabilities 属于 Host/runtime 集成边界,不是小程序可从 SDK 根包调用的方法。需要对应数据时,先取得授权码,再由开发者服务端按后端 OpenAPI 文档换取 token 并调用对应接口。不要把 `SERVER_API_REQUIRED` 当作未登录或权限弹窗失败。
291
+ ## 读取当前用户资料
294
292
 
295
- `user.revokeAuthorization()` 是例外:它是授权变更操作,在两种网络模式下都可用,但始终要求可信用户手势。
296
-
297
- ## 未开通网络权限
298
-
299
- 未开通网络权限的小程序不走授权码流程。需要当前用户在本小程序内的稳定隔离身份和公开资料时调用:
293
+ 需要当前用户在本小程序内的稳定隔离身份和已授权资料时调用:
300
294
 
301
295
  ```ts
302
296
  import hbSDK from '@heybox/hb-sdk'
@@ -307,14 +301,16 @@ async function getCurrentUser() {
307
301
  }
308
302
  ```
309
303
 
310
- `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` 等可用资料。用户授权与开发者权限声明是两层独立门禁,不能用声明代替用户同意。
311
305
 
312
- 这类小程序无法获取授权码,也无法调用 OpenAPI;在未开通网络权限时调用 `auth.login()` 同样返回 `SERVER_API_REQUIRED`。隐式 full-scope 仅适用于 Host 本地用户资料能力,不代表开放网络或服务端凭据;页面不能把其中的身份或资料发送到外部服务。
306
+ Steam 游戏库使用 `user.getSteamGameList(options)`,要求声明 `steamLibrary`,同样不依赖 `network`。Host current-user capabilities 的既有账号绑定和数据可用性规则保持不变。
313
307
 
314
308
  ## 网络请求边界
315
309
 
316
310
  `network.request()` 只用于访问开发者自己的业务服务。平台保留的 runtime auth 与 OpenAPI 内部路径不能通过该能力访问;身份交换必须由开发者服务端按公开后端文档完成。
317
311
 
312
+ 完整权限配置与错误区别见[权限声明](https://docs.xiaoheihe.cn/hb_sdk/guide/permissions)。
313
+
318
314
  ## CLI 登录与用户登录
319
315
 
320
316
  `hb-sdk login` 只给本地开发和远端管理命令建立 CLI 登录态,与小程序页面的 `auth.login()`、`user.getInfo()` 和用户授权完全无关。
@@ -372,7 +368,7 @@ onUnmounted(stopLifecycleEvents)
372
368
  - 当前握手状态只通过 `getHandshakeState()` 查询、通过 `onHandshakeStateChange()` 订阅;订阅会立即回放当前值并返回取消函数。
373
369
  - `ready` 事件只在握手成功瞬间派发,不会向晚订阅者重放,不能用于驱动按钮可用状态或替代握手状态 API。
374
370
  - UI 可见性相关逻辑放在 `show`、`hide`。
375
- - 使用 Host current-user APIs 的无网络小程序,可以在 `heybox_app_login_change` 后刷新本地状态。已开通网络权限的小程序使用 `auth.login()` 获取 code,并由开发者服务端维护业务会话。
371
+ - 使用 `user.getInfo()` 的小程序可以在 `heybox_app_login_change` 后刷新本地状态。需要开发者服务端会话时使用 `auth.login()` 获取 code;两者都不依赖公开 `network` 权限。
376
372
  - 组件或页面销毁时清理 `on` 注册的监听,避免重复响应。
377
373
  - 生命周期事件只派发给注册当时存在的监听器,不会重放;一次性 `launch` 信息应在页面启动阶段通过业务能力调用处理。
378
374
  - 收到 `unload` 后,当前 SDK 上下文不可恢复,未完成请求会失败;不要在同一页面上下文继续重试能力调用。
@@ -411,6 +407,8 @@ try {
411
407
 
412
408
  - 根据 `error.code` 区分运行环境、权限、超时和业务失败,不要只比对错误文案。
413
409
  - 权限失败时给出可理解的提示,不要将它当成未登录。
410
+ - `PERMISSION_NOT_DECLARED` 表示当前版本没有在 `package.json#heybox.permissions` 声明所需权限,应修改项目配置并重新构建、提交版本。
411
+ - `PERMISSION_DENIED` 表示权限已声明,但需要的平台批准缺失或批准配置不足。首期只有 `network` 需要平台批准。
414
412
  - `USER_GESTURE_REQUIRED` 表示潜在授权 UI 缺少可信用户手势,应让用户点击按钮后重试。
415
413
  - `AUTHORIZATION_CANCELLED` 表示用户取消、拒绝或关闭授权页面,应正常结束当前操作。
416
414
  - `SERVER_API_REQUIRED` 表示当前用户数据必须经开发者服务端 OpenAPI 获取,不应在页面重试对应 Host API。
@@ -475,7 +473,7 @@ picker 取消、授权撤销、文件缺失、quota、下载状态或取消,
475
473
 
476
474
  # 服务端登录门禁
477
475
 
478
- 已开通网络权限的小程序可以把“取得授权码并交给开发者服务端”收敛成一个用户操作函数:
476
+ 声明 `userInfo` 后,小程序可以把“取得授权码并交给开发者服务端”收敛成一个用户操作函数。`auth.login()` 本身不依赖 `network`;以下示例使用 `network.request()` 传递 code,因此还需声明并获得 `network` 的平台批准:
479
477
 
480
478
  示例中的 URL 是开发者自己的后端接口,不是黑盒 OpenAPI 地址。
481
479
 
@@ -613,7 +611,6 @@ if (sharedState && typeof sharedState === 'object' && !Array.isArray(sharedState
613
611
 
614
612
  ```ts
615
613
  import hbSDK from '@heybox/hb-sdk'
616
-
617
614
  ```
618
615
 
619
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.9+skill.94bc5bff8aed",
3
+ "skillVersion": "0.8.0+skill.7bb8515ab71e",
4
4
  "sdk": {
5
5
  "package": "@heybox/hb-sdk",
6
- "version": "0.8.0-alpha.9",
7
- "compatibility": "0.8.0-alpha.9"
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.9",
12
+ "version": "0.8.0",
13
13
  "path": "skill"
14
14
  },
15
- "integrity": "sha256-94bc5bff8aed44fa6e6ca3066e8ccfc864b57631c64e76763717a36e5672f2df"
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;
@@ -1,2 +1,3 @@
1
1
  export * from './schema';
2
2
  export * from './node';
3
+ export * from './permissions';
@@ -1 +1,4 @@
1
+ import { type ParsedMiniappPermissions } from './permissions';
1
2
  export declare function readMiniappVersionFromPackageJson(root: string): string;
3
+ export declare function readMiniappPermissionsFromPackageJson(root: string, sdkVersion: string): ParsedMiniappPermissions;
4
+ export declare function resolveMiniappSdkVersion(): string;