@heybox/hb-sdk 0.9.1-alpha.8 → 0.9.1

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 (43) hide show
  1. package/CHANGELOG.md +123 -17
  2. package/README.md +18 -17
  3. package/dist/cli-chunks/_commonjs-dynamic-modules-DUamuDzp.cjs +7 -0
  4. package/dist/cli-chunks/browser-RAy8e8cV.cjs +635 -0
  5. package/dist/cli-chunks/{build-CQkpLf81.cjs → build-DCp9C9V4.cjs} +10 -14
  6. package/dist/cli-chunks/{context-CqJDNtEi.cjs → context-Bg6C5ow7.cjs} +10 -5
  7. package/dist/cli-chunks/{create-BRuR2M7y.cjs → create-D5bX2Xvw.cjs} +7 -12
  8. package/dist/cli-chunks/{dev-KJAFHEg_.cjs → dev-CFjwhG3q.cjs} +195 -82
  9. package/dist/cli-chunks/{doctor-DhiUViwk.cjs → doctor-CMz_Kf7-.cjs} +9 -13
  10. package/dist/cli-chunks/{index-9trmrUtW.cjs → index-B_KKGIBN.cjs} +13591 -10542
  11. package/dist/cli-chunks/{index-rxAV5dza.cjs → index-YnVa6S_p.cjs} +9 -10
  12. package/dist/cli-chunks/{index.esm-Cv9FlhXy.cjs → index.esm-CgZPnqEA.cjs} +43 -43
  13. package/dist/cli-chunks/{login-5DEcU9eL.cjs → login-Df_v6jNs.cjs} +13 -12
  14. package/dist/cli-chunks/{project-vite-CXWFwbEz.cjs → project-vite-By8cnIwP.cjs} +1 -1
  15. package/dist/cli-chunks/{remote-laUZaGpI.cjs → remote-ClIJz-e2.cjs} +42 -71
  16. package/dist/cli-chunks/{runtime-gate-DmJhPcNQ.cjs → runtime-gate-DKiyNwX2.cjs} +9 -13
  17. package/dist/cli-chunks/{runtime-permission-env-Gy2dH17F.cjs → runtime-permission-env-BNQ2fx4w.cjs} +82 -2794
  18. package/dist/cli-chunks/{skill--mZIZkyn.cjs → skill-Bk-EBSbJ.cjs} +10 -14
  19. package/dist/cli-chunks/{version-BMp9qmtC.cjs → version-CG_VNZGM.cjs} +1 -1
  20. package/dist/cli.cjs +11 -16
  21. package/dist/devtools/browser-dev-host/assets/browser-dev-host-C1g2tadT.js +102 -0
  22. package/dist/devtools/browser-dev-host/assets/{desktop-app-launch-BU-vzcs0.js → desktop-app-launch-sH2a_jbj.js} +1 -1
  23. package/dist/devtools/browser-dev-host/assets/{index-BRxbUJVN.css → index-7Cq8iNcD.css} +1 -1
  24. package/dist/devtools/browser-dev-host/assets/{index-BZBZfB4t.js → index-Bpxbe1lP.js} +4 -4
  25. package/dist/devtools/browser-dev-host/index.html +3 -3
  26. package/dist/index.cjs.js +16 -8
  27. package/dist/index.esm.js +16 -8
  28. package/dist/miniapp-publish.cjs.js +27 -0
  29. package/dist/miniapp-publish.esm.js +27 -0
  30. package/dist/protocol.cjs.js +1 -1
  31. package/dist/protocol.esm.js +1 -1
  32. package/dist/vite.cjs.js +36 -1
  33. package/dist/vite.esm.js +36 -1
  34. package/docs/runtime-host-migration.md +4 -2
  35. package/package.json +6 -5
  36. package/skill/SKILL.md +10 -0
  37. package/skill/skill.json +5 -5
  38. package/types/index.d.ts +124 -94
  39. package/types/miniapp-publish/index.d.ts +3 -0
  40. package/types/protocol.d.ts +47 -39
  41. package/types/vite/index.d.ts +1 -0
  42. package/dist/cli-chunks/browser-YVckob3g.cjs +0 -15
  43. package/dist/devtools/browser-dev-host/assets/browser-dev-host-COdk-39n.js +0 -102
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-9trmrUtW.cjs');
4
- var context = require('./context-CqJDNtEi.cjs');
3
+ var index = require('./index-B_KKGIBN.cjs');
4
+ var context = require('./context-Bg6C5ow7.cjs');
5
5
  var node_crypto = require('node:crypto');
6
6
  var fs = require('node:fs');
7
- var runtimePermissionEnv = require('./runtime-permission-env-Gy2dH17F.cjs');
7
+ var runtimePermissionEnv = require('./runtime-permission-env-BNQ2fx4w.cjs');
8
8
  var os = require('node:os');
9
9
  var path = require('node:path');
10
10
  var net = require('node:net');
@@ -15,9 +15,9 @@ var undici = require('undici');
15
15
  var fs$1 = require('node:fs/promises');
16
16
  var require$$0$1 = require('crypto');
17
17
  var require$$0 = require('buffer');
18
- var browser = require('./browser-YVckob3g.cjs');
19
- var projectVite = require('./project-vite-CXWFwbEz.cjs');
20
- var version = require('./version-BMp9qmtC.cjs');
18
+ var browser = require('./browser-RAy8e8cV.cjs');
19
+ var projectVite = require('./project-vite-By8cnIwP.cjs');
20
+ var version = require('./version-CG_VNZGM.cjs');
21
21
  require('node:zlib');
22
22
 
23
23
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -1238,6 +1238,8 @@ function createDevSessionRoutes(options) {
1238
1238
  }
1239
1239
  function createContextFields(context) {
1240
1240
  return {
1241
+ development_snapshot: context.developmentSnapshot,
1242
+ development_snapshot_digest: context.developmentSnapshotDigest,
1241
1243
  local_dev_grant: context.localDevGrant,
1242
1244
  context_revision: context.contextRevision,
1243
1245
  declared_permissions: context.declaredPermissions,
@@ -1992,8 +1994,8 @@ const DEFAULT_CHANNEL_TRANSPORT = {
1992
1994
  };
1993
1995
  const IPV4_LOOPBACK_ADDRESSES = new net.BlockList();
1994
1996
  IPV4_LOOPBACK_ADDRESSES.addSubnet('127.0.0.0', 8, 'ipv4');
1995
- const { WebSocket, WebSocketServer } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-KJAFHEg_.cjs', document.baseURI).href)))('ws');
1996
- const { parse: parseHtml } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-KJAFHEg_.cjs', document.baseURI).href)))('parse5');
1997
+ const { WebSocket, WebSocketServer } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-CFjwhG3q.cjs', document.baseURI).href)))('ws');
1998
+ const { parse: parseHtml } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-CFjwhG3q.cjs', document.baseURI).href)))('parse5');
1997
1999
  const BROWSER_DEV_HOST_ROOT_CANDIDATES = [
1998
2000
  path.resolve(__dirname, 'devtools/browser-dev-host'),
1999
2001
  path.resolve(__dirname, '../devtools/browser-dev-host'),
@@ -2022,6 +2024,7 @@ async function startBrowserDevHostServer(options) {
2022
2024
  csrfToken: sessionController.csrfToken,
2023
2025
  defaultLanAddressId: options.defaultLanAddressId,
2024
2026
  devContext: options.devContext,
2027
+ browserDevContext: options.browserDevContext,
2025
2028
  devSessionId: options.devSessionId,
2026
2029
  lanAddresses: options.lanAddresses ?? [],
2027
2030
  loginAvailable: options.loginService !== undefined,
@@ -2053,6 +2056,7 @@ async function startBrowserDevHostServer(options) {
2053
2056
  loginAvailable: options.loginService !== undefined,
2054
2057
  cliAuth: options.cliAuth,
2055
2058
  readDevContext: () => bootstrap.devContext ?? { source: 'anonymous', runtimePermissions: bootstrap.runtimePermissions },
2059
+ readBrowserDevContext: () => bootstrap.browserDevContext ?? bootstrap.devContext ?? { source: 'anonymous', runtimePermissions: bootstrap.runtimePermissions },
2056
2060
  readMobileDevSessionContext: options.readMobileDevSessionContext ??
2057
2061
  (() => Promise.resolve(bootstrap.devContext ?? { source: 'anonymous', runtimePermissions: bootstrap.runtimePermissions })),
2058
2062
  readLanAddresses: () => bootstrap.lanAddresses,
@@ -2103,6 +2107,8 @@ async function startBrowserDevHostServer(options) {
2103
2107
  bootstrap.devReadiness = next.devReadiness;
2104
2108
  if (next.devContext !== undefined)
2105
2109
  bootstrap.devContext = next.devContext;
2110
+ if (next.browserDevContext !== undefined)
2111
+ bootstrap.browserDevContext = next.browserDevContext;
2106
2112
  if (next.lanAddresses !== undefined) {
2107
2113
  bootstrap.lanAddresses = next.lanAddresses;
2108
2114
  const launch = readDefaultMobileLaunch(next.lanAddresses, bootstrap.defaultLanAddressId);
@@ -2838,7 +2844,7 @@ function createBrowserDevHostChannelServer(server, options) {
2838
2844
  }
2839
2845
  async function readBrowserDevHostChannelSnapshot(runtime, cookieHeader) {
2840
2846
  const status = await readAccountStatus(runtime, cookieHeader);
2841
- const permissions = context.createBrowserDevPermissionState(context.resolveBrowserFakeContext(runtime.readDevContext()));
2847
+ const permissions = context.createBrowserDevPermissionState(context.resolveBrowserFakeContext(runtime.readBrowserDevContext()));
2842
2848
  return {
2843
2849
  account: {
2844
2850
  revision: status.revision,
@@ -11093,7 +11099,7 @@ function assertNodeRuntimeCapabilities(ctx, settings) {
11093
11099
  function createNodeRealtimeDriverRegistry() {
11094
11100
  const registry = createRuntimeRealtimeDriverRegistry();
11095
11101
  registry.registerLazy('node-websocket', async () => {
11096
- const mod = await Promise.resolve().then(function () { return require('./index.esm-Cv9FlhXy.cjs'); });
11102
+ const mod = await Promise.resolve().then(function () { return require('./index.esm-CgZPnqEA.cjs'); });
11097
11103
  return mod.createNodeWebSocketRuntimeDriver();
11098
11104
  });
11099
11105
  return registry;
@@ -15141,7 +15147,12 @@ function createLaunchSnapshot(source, requestOrigin) {
15141
15147
  if (source.expiresAt)
15142
15148
  snapshot.expires_at = source.expiresAt;
15143
15149
  const context = source.devContext;
15150
+ // await 续票后 source 可能已切换快照;禁止把旧票据与新描述拼成一个响应。
15151
+ if (context?.developmentSnapshot && localDevLaunchToken !== context.localDevLaunchToken)
15152
+ throw new Error('开发票据与快照已变化,请重试');
15144
15153
  if (context) {
15154
+ snapshot.development_snapshot = context.developmentSnapshot;
15155
+ snapshot.development_snapshot_digest = context.developmentSnapshotDigest;
15145
15156
  snapshot.context_revision = context.contextRevision;
15146
15157
  snapshot.local_dev_grant = context.localDevGrant;
15147
15158
  snapshot.declared_permissions = context.declaredPermissions;
@@ -15498,7 +15509,14 @@ function createDevContextCache(options) {
15498
15509
  entries.delete(key);
15499
15510
  inflight.delete(key);
15500
15511
  },
15501
- peek: (key) => entries.get(key)?.value,
15512
+ peek: (key) => {
15513
+ const cached = entries.get(key);
15514
+ if (!cached || cached.expiresAt <= now()) {
15515
+ entries.delete(key);
15516
+ return undefined;
15517
+ }
15518
+ return cached.value;
15519
+ },
15502
15520
  read: (key) => {
15503
15521
  if (cacheEnabled) {
15504
15522
  const cached = entries.get(key);
@@ -15608,12 +15626,9 @@ async function runDevCommand(options, runtime = {}) {
15608
15626
  * 立即重新请求远端,而不是等缓存过期后才发现换了小程序。
15609
15627
  * 读绑定是本地文件读取,不打远端,符合「只读本地快照」的高频读范式。
15610
15628
  */
15611
- const readDevLaunchContext = async (reuseSnapshot = false) => {
15629
+ const readDevLaunchContext = async () => {
15612
15630
  const boundMiniProgramId = await context.readBoundMiniProgramId(projectRoot).catch(() => undefined);
15613
15631
  const key = `${projectRoot}\0${boundMiniProgramId ?? ''}`;
15614
- const snapshot = reuseSnapshot ? devContextCache.peek(key) : undefined;
15615
- if (snapshot?.context.source === 'remote' && !snapshot.context.rateLimit?.limited)
15616
- return snapshot;
15617
15632
  return devContextCache.read(key);
15618
15633
  };
15619
15634
  let { context: devLaunchContext, miniProgram } = await readDevLaunchContext();
@@ -15650,29 +15665,48 @@ async function runDevCommand(options, runtime = {}) {
15650
15665
  }
15651
15666
  }
15652
15667
  let currentDevContext = devLaunchContext;
15668
+ // 候选输入不对真机公开;签票成功后才替换整份运行上下文。
15669
+ let candidateDevContext = devLaunchContext;
15653
15670
  let devContextGeneration = 0;
15671
+ let preparedDevContext;
15654
15672
  let contextRevision = 1;
15655
15673
  let launchRuntimePermissions = context.createMobileDevSessionRuntimePermissions(devLaunchContext);
15656
15674
  const mobileDevContextUrlsByOrigin = new Map();
15657
15675
  let currentCompanionManifestSha256;
15676
+ let snapshotCompanionManifestSha256;
15658
15677
  let currentLocalDevLaunchToken = devLaunchContext.localDevLaunchToken;
15659
15678
  let currentExpiresAt;
15660
15679
  let refreshDevCsp;
15661
15680
  let lastTemporaryWarning = '';
15662
15681
  let hasReportedBindingChange = false;
15663
15682
  let tokenSession;
15664
- const updateLocalContext = (allowedTemporaryKeys) => {
15683
+ // Browser Mock 即时消费本地输入,不等待真机签票,也不持有真实凭据。
15684
+ const readBrowserDevContext = () => ({
15685
+ source: candidateDevContext.source,
15686
+ miniProgramId: candidateDevContext.miniProgramId,
15687
+ declaredPermissions: candidateDevContext.declaredPermissions,
15688
+ platforms: candidateDevContext.platforms,
15689
+ runtimePermissions: candidateDevContext.runtimePermissions,
15690
+ publishedRuntimePermissions: candidateDevContext.publishedRuntimePermissions,
15691
+ warning: candidateDevContext.warning,
15692
+ contextRevision, devSessionId, expiresAt: Date.now() + 24 * 60 * 60 * 1000,
15693
+ localDevGrant: runtimePermissionEnv.createMiniProgramLocalDevGrant({
15694
+ declaredPermissions: candidateDevContext.declaredPermissions ?? {},
15695
+ runtimePermissions: candidateDevContext.runtimePermissions, revision: contextRevision,
15696
+ eligiblePermissionKeys: runtimePermissionEnv.hasMiniappDesktopPlatform(candidateDevContext.platforms)
15697
+ ? ['network', 'companion', 'appProtocol'] : ['network', 'appProtocol'],
15698
+ }),
15699
+ });
15700
+ const updateLocalContext = () => {
15665
15701
  const grant = runtimePermissionEnv.createMiniProgramLocalDevGrant({ declaredPermissions: currentDevContext.declaredPermissions ?? {},
15666
- runtimePermissions: currentDevContext.runtimePermissions, revision: contextRevision,
15702
+ runtimePermissions: currentDevContext.runtimePermissions, revision: currentDevContext.contextRevision ?? contextRevision,
15667
15703
  eligiblePermissionKeys: currentCompanionManifestSha256 && runtimePermissionEnv.hasMiniappDesktopPlatform(currentDevContext.platforms)
15668
15704
  ? ['network', 'companion', 'appProtocol'] : ['network', 'appProtocol'] });
15669
- if (allowedTemporaryKeys)
15670
- grant.entries = grant.entries.filter(entry => allowedTemporaryKeys.has(entry.key));
15671
15705
  const temporaryWarning = grant.entries.map(entry => entry.key).join('、');
15672
15706
  if (temporaryWarning && temporaryWarning !== lastTemporaryWarning)
15673
15707
  logger.warn(`本地临时权限:${temporaryWarning};线上未批准,发布后不可用。`);
15674
15708
  lastTemporaryWarning = temporaryWarning;
15675
- currentDevContext = { ...currentDevContext, localDevGrant: grant, contextRevision, devSessionId,
15709
+ currentDevContext = { ...currentDevContext, localDevGrant: grant, contextRevision: grant.revision, devSessionId,
15676
15710
  companionManifestSha256: currentCompanionManifestSha256,
15677
15711
  expiresAt: currentExpiresAt ?? Date.now() + 24 * 60 * 60 * 1000 };
15678
15712
  launchRuntimePermissions = context.createMobileDevSessionRuntimePermissions(currentDevContext);
@@ -15782,7 +15816,8 @@ async function runDevCommand(options, runtime = {}) {
15782
15816
  await runDevStartupTask(logger, '正在启动 Vite 开发服务器', () => appServer.listen(appPort), 'Vite 开发服务器已启动');
15783
15817
  let skipDevCsp = context.shouldSkipMiniappPlatformCsp(currentDevContext.runtimePermissions) || currentDevContext.localDevGrant?.entries.some(entry => entry.key === 'network') === true;
15784
15818
  refreshDevCsp = () => {
15785
- const next = context.shouldSkipMiniappPlatformCsp(currentDevContext.runtimePermissions) || currentDevContext.localDevGrant?.entries.some(entry => entry.key === 'network') === true;
15819
+ const browser = readBrowserDevContext();
15820
+ const next = context.shouldSkipMiniappPlatformCsp(browser.runtimePermissions) || browser.localDevGrant?.entries.some(entry => entry.key === 'network') === true;
15786
15821
  if (next === skipDevCsp || !appServer.restart)
15787
15822
  return;
15788
15823
  skipDevCsp = next;
@@ -15813,6 +15848,7 @@ async function runDevCommand(options, runtime = {}) {
15813
15848
  appUrl,
15814
15849
  devSessionId,
15815
15850
  devContext: currentDevContext,
15851
+ browserDevContext: readBrowserDevContext(),
15816
15852
  fetchImpl,
15817
15853
  getPort: getPortImpl,
15818
15854
  defaultLanAddressId: lanAddresses[0]?.id,
@@ -15834,7 +15870,7 @@ async function runDevCommand(options, runtime = {}) {
15834
15870
  readMobileDevSessionContext: async () => {
15835
15871
  if (currentDevContext.source === 'remote') {
15836
15872
  const generation = devContextGeneration;
15837
- const refreshed = await readDevLaunchContext(true);
15873
+ const refreshed = await readDevLaunchContext();
15838
15874
  if (generation !== devContextGeneration)
15839
15875
  return currentDevContext;
15840
15876
  if (refreshed.context.rateLimit?.limited) {
@@ -15842,11 +15878,17 @@ async function runDevCommand(options, runtime = {}) {
15842
15878
  applyRateLimitState(refreshed.context.rateLimit);
15843
15879
  }
15844
15880
  else if (refreshed.context.source === 'remote' && refreshed.context.miniProgramId === currentDevContext.miniProgramId) {
15845
- if (JSON.stringify(currentDevContext.runtimePermissions) !== JSON.stringify(refreshed.context.runtimePermissions))
15846
- contextRevision++;
15847
- currentDevContext = withCurrentLocalDevLaunchToken(refreshed.context, currentLocalDevLaunchToken);
15848
- updateLocalContext();
15849
- browserDevHost.updateBootstrap({ devContext: currentDevContext });
15881
+ if (developmentInputsDiffer(currentDevContext, refreshed.context)) {
15882
+ preparedDevContext = { generation, result: refreshed };
15883
+ tokenSession?.refresh({ invalidateContext: false });
15884
+ await tokenSession?.waitForCurrent();
15885
+ }
15886
+ else {
15887
+ // 线上发布状态仅供诊断,不改变本次运行授权。
15888
+ currentDevContext = { ...currentDevContext, publishedRuntimePermissions: refreshed.context.publishedRuntimePermissions };
15889
+ candidateDevContext = { ...candidateDevContext, publishedRuntimePermissions: refreshed.context.publishedRuntimePermissions };
15890
+ browserDevHost.updateBootstrap({ devContext: currentDevContext, browserDevContext: readBrowserDevContext() });
15891
+ }
15850
15892
  hasReportedBindingChange = false;
15851
15893
  }
15852
15894
  else if (refreshed.context.source === 'remote' && refreshed.context.miniProgramId !== currentDevContext.miniProgramId) {
@@ -15881,7 +15923,9 @@ async function runDevCommand(options, runtime = {}) {
15881
15923
  miniProgramId: devLaunchContext.miniProgramId,
15882
15924
  refreshPermissions: async () => {
15883
15925
  const generation = devContextGeneration;
15884
- const refreshed = await readDevLaunchContext();
15926
+ const prepared = preparedDevContext;
15927
+ preparedDevContext = undefined;
15928
+ const refreshed = prepared?.generation === generation ? prepared.result : await readDevLaunchContext();
15885
15929
  if (generation !== devContextGeneration)
15886
15930
  throw new DevContextSupersededError();
15887
15931
  // 限流必须原样抛出,才能走 30s 起、上限 300s 的退避与「请勿反复重启」提示。
@@ -15892,23 +15936,32 @@ async function runDevCommand(options, runtime = {}) {
15892
15936
  throw new Error('项目绑定已变更,请重新启动调试');
15893
15937
  if (refreshed.context.source !== 'remote')
15894
15938
  throw new Error('远端权限刷新暂不可用');
15895
- if (JSON.stringify(currentDevContext.runtimePermissions) !== JSON.stringify(refreshed.context.runtimePermissions))
15939
+ if (developmentInputsDiffer(candidateDevContext, refreshed.context))
15896
15940
  contextRevision++;
15897
- currentDevContext = withCurrentLocalDevLaunchToken(refreshed.context, currentLocalDevLaunchToken);
15898
- updateLocalContext();
15899
- browserDevHost.updateBootstrap({ devContext: currentDevContext });
15900
- return currentDevContext.declaredPermissions?.companion?.enabled === true && runtimePermissionEnv.hasMiniappDesktopPlatform(currentDevContext.platforms);
15941
+ candidateDevContext = refreshed.context;
15942
+ return candidateDevContext.declaredPermissions?.companion?.enabled === true && runtimePermissionEnv.hasMiniappDesktopPlatform(candidateDevContext.platforms);
15901
15943
  },
15902
- onToken: (localDevLaunchToken, companionManifestSha256, expiresAt) => {
15903
- if (currentCompanionManifestSha256 !== companionManifestSha256 || Boolean(currentLocalDevLaunchToken) !== Boolean(localDevLaunchToken))
15944
+ readDevelopmentSnapshot: companionManifestSha256 => {
15945
+ if (snapshotCompanionManifestSha256 !== companionManifestSha256)
15904
15946
  contextRevision++;
15947
+ snapshotCompanionManifestSha256 = companionManifestSha256;
15948
+ return runtimePermissionEnv.serializeMiniProgramDevelopmentSnapshot({ schema_version: 1, session_id: devSessionId,
15949
+ revision: contextRevision, sdk_version: version.HB_SDK_VERSION, declared_permissions: candidateDevContext.declaredPermissions ?? {},
15950
+ platforms: candidateDevContext.platforms ?? [], ...(companionManifestSha256 ? { companion_manifest_sha256: companionManifestSha256 } : {}) });
15951
+ },
15952
+ onToken: (localDevLaunchToken, companionManifestSha256, expiresAt, developmentSnapshot) => {
15953
+ // 单一发布出口:候选声明/批准、描述、票据、revision 同步切换。
15954
+ currentDevContext = { ...(localDevLaunchToken ? candidateDevContext : currentDevContext), localDevLaunchToken,
15955
+ contextRevision: developmentSnapshot ? JSON.parse(developmentSnapshot).revision : currentDevContext.contextRevision ?? contextRevision,
15956
+ developmentSnapshot,
15957
+ developmentSnapshotDigest: developmentSnapshot ? node_crypto.createHash('sha256').update(developmentSnapshot).digest('hex') : undefined };
15905
15958
  currentExpiresAt = expiresAt ?? (currentLocalDevLaunchToken ? Date.now() : undefined);
15906
15959
  currentCompanionManifestSha256 = companionManifestSha256;
15907
15960
  currentLocalDevLaunchToken = localDevLaunchToken;
15908
- currentDevContext = withCurrentLocalDevLaunchToken(currentDevContext, localDevLaunchToken);
15909
15961
  updateLocalContext();
15910
15962
  browserDevHost.updateBootstrap({
15911
15963
  devContext: currentDevContext,
15964
+ browserDevContext: readBrowserDevContext(),
15912
15965
  lanAddresses: buildLanAddresses(),
15913
15966
  });
15914
15967
  },
@@ -15916,6 +15969,7 @@ async function runDevCommand(options, runtime = {}) {
15916
15969
  // 主动失效:项目绑定变更或开发者点重试后立即重新请求(key 会带上新绑定,这里清空全部)。
15917
15970
  onInvalidate: () => {
15918
15971
  devContextGeneration += 1;
15972
+ preparedDevContext = undefined;
15919
15973
  devContextCache.invalidate();
15920
15974
  },
15921
15975
  // 限流状态与恢复都要透出给调试页与宿主页。
@@ -15962,7 +16016,6 @@ async function runDevCommand(options, runtime = {}) {
15962
16016
  }
15963
16017
  const onDeclarationChange = () => {
15964
16018
  try {
15965
- const previouslyTemporary = new Set(currentDevContext.localDevGrant?.entries.map(entry => entry.key));
15966
16019
  const local = runtimePermissionEnv.readMiniappPermissionsFromPackageJson(projectRoot, runtimePermissionEnv.resolveMiniappSdkVersion());
15967
16020
  const platforms = runtimePermissionEnv.readMiniappPlatformsFromPackageJson(projectRoot);
15968
16021
  // 与调试声明无关的写入(版本号、依赖等)不触发远端刷新。
@@ -15971,19 +16024,27 @@ async function runDevCommand(options, runtime = {}) {
15971
16024
  return;
15972
16025
  lastDeclarationFingerprint = fingerprint;
15973
16026
  contextRevision++;
15974
- currentDevContext = { ...currentDevContext, declaredPermissions: local.permissions ?? {}, platforms,
15975
- runtimePermissions: runtimePermissionEnv.composeMiniappRuntimePermissions(local, currentDevContext.runtimePermissions).runtimePermissions };
15976
- // 新声明必须重新取得远端基线;已有票据期间只允许删除,不因刷新失败扩权。
15977
- currentCompanionManifestSha256 = undefined;
15978
- updateLocalContext(currentDevContext.source === 'remote' ? previouslyTemporary : undefined);
15979
- tokenSession?.refresh();
15980
- browserDevHost.updateBootstrap({ devContext: currentDevContext, miniProgramSupportsDesktop: runtimePermissionEnv.hasMiniappDesktopPlatform(platforms) });
16027
+ candidateDevContext = { ...candidateDevContext, declaredPermissions: local.permissions ?? {}, platforms,
16028
+ runtimePermissions: runtimePermissionEnv.composeMiniappRuntimePermissions(local, candidateDevContext.runtimePermissions).runtimePermissions };
16029
+ if (tokenSession) {
16030
+ tokenSession.refresh();
16031
+ }
16032
+ else {
16033
+ // 无真实客户端凭据的 Browser Mock 不依赖远端签票。
16034
+ currentDevContext = { ...candidateDevContext, contextRevision };
16035
+ updateLocalContext();
16036
+ browserDevHost.updateBootstrap({ devContext: currentDevContext });
16037
+ }
16038
+ browserDevHost.updateBootstrap({ browserDevContext: readBrowserDevContext(), miniProgramSupportsDesktop: runtimePermissionEnv.hasMiniappDesktopPlatform(platforms) });
16039
+ refreshDevCsp?.();
15981
16040
  }
15982
16041
  catch {
15983
16042
  contextRevision++;
15984
16043
  currentDevContext = { ...currentDevContext, declaredPermissions: {}, localDevGrant: undefined,
15985
16044
  contextRevision, platforms: [], localDevLaunchToken: undefined, expiresAt: undefined,
16045
+ developmentSnapshot: undefined, developmentSnapshotDigest: undefined,
15986
16046
  runtimePermissions: { schema_version: 1, entries: [] }, warning: '本地权限声明无效,调试权限已撤销' };
16047
+ candidateDevContext = currentDevContext;
15987
16048
  launchRuntimePermissions = { schema_version: 1, entries: [] };
15988
16049
  currentCompanionManifestSha256 = undefined;
15989
16050
  currentLocalDevLaunchToken = undefined;
@@ -15992,6 +16053,7 @@ async function runDevCommand(options, runtime = {}) {
15992
16053
  tokenSession?.refresh();
15993
16054
  browserDevHost.updateBootstrap({
15994
16055
  devContext: currentDevContext,
16056
+ browserDevContext: readBrowserDevContext(),
15995
16057
  devReadiness: {
15996
16058
  browser: { state: 'ready' },
15997
16059
  mobile: { state: 'blocked', reason: '本地权限声明无效,已撤销真实客户端调试' },
@@ -16012,14 +16074,8 @@ async function runDevCommand(options, runtime = {}) {
16012
16074
  }
16013
16075
  installShutdownHandlers(closers, runtime.process ?? process);
16014
16076
  }
16015
- function withCurrentLocalDevLaunchToken(context, localDevLaunchToken) {
16016
- return {
16017
- ...context,
16018
- ...(context.miniProgramId ? { miniProgramId: context.miniProgramId } : {}),
16019
- localDevLaunchToken,
16020
- ...(context.runtimePermissions !== undefined ? { runtimePermissions: context.runtimePermissions } : {}),
16021
- ...(context.warning ? { warning: context.warning } : {}),
16022
- };
16077
+ function developmentInputsDiffer(left, right) {
16078
+ return ['miniProgramId', 'declaredPermissions', 'platforms', 'runtimePermissions'].some(key => JSON.stringify(left[key]) !== JSON.stringify(right[key]));
16023
16079
  }
16024
16080
  function runDevStartupTask(logger, text, action, successText) {
16025
16081
  if (!logger.verbose) {
@@ -16063,6 +16119,7 @@ async function loadDevLaunchContext(options) {
16063
16119
  source: 'remote',
16064
16120
  miniProgramId: verifiedMiniProgramId,
16065
16121
  runtimePermissions: effectivePermissions,
16122
+ ...(runtimePermissionEnv.parseMiniProgramRuntimePermissions(detail.effective_runtime_permissions).valid ? { publishedRuntimePermissions: detail.effective_runtime_permissions } : {}),
16066
16123
  declaredPermissions: localPermissions.permissions ?? {},
16067
16124
  platforms,
16068
16125
  },
@@ -16369,7 +16426,11 @@ function startLocalDevLaunchTokenSession(options) {
16369
16426
  let permissionsRefreshFailure;
16370
16427
  let permissionsGeneration = 0;
16371
16428
  let validatedPermissionsGeneration = 0;
16429
+ let publishedPermissionsGeneration = -1;
16430
+ let refreshFailure;
16372
16431
  let publishedToken;
16432
+ let publishedTicket;
16433
+ let refreshPromise;
16373
16434
  let publishedDigest;
16374
16435
  let ticket;
16375
16436
  let pending;
@@ -16378,16 +16439,18 @@ function startLocalDevLaunchTokenSession(options) {
16378
16439
  const acquireTicket = async (companionManifestSha256) => {
16379
16440
  if (closed)
16380
16441
  return undefined;
16381
- if (ticket && (companionManifestSha256 === undefined || ticket.companionManifestSha256 === companionManifestSha256) && ticket.refreshAt > Date.now())
16442
+ const snapshot = options.readDevelopmentSnapshot?.(companionManifestSha256);
16443
+ if (ticket && ticket.developmentSnapshot === snapshot && (companionManifestSha256 === undefined || ticket.companionManifestSha256 === companionManifestSha256) && ticket.refreshAt > Date.now())
16382
16444
  return ticket;
16383
- return issueTicket(companionManifestSha256);
16445
+ return issueTicket(companionManifestSha256, snapshot);
16384
16446
  };
16385
- const issueTicket = (companionManifestSha256) => {
16386
- if (pending && pending.digest === companionManifestSha256)
16447
+ const issueTicket = (companionManifestSha256, developmentSnapshot) => {
16448
+ if (pending && pending.digest === companionManifestSha256 && pending.snapshot === developmentSnapshot)
16387
16449
  return pending.promise;
16388
16450
  const generation = permissionsGeneration;
16389
16451
  const promise = issueLocalDevLaunchToken({
16390
16452
  ...options,
16453
+ developmentSnapshot,
16391
16454
  ...(companionManifestSha256 ? { companionManifestSha256 } : {}),
16392
16455
  })
16393
16456
  .then(issued => {
@@ -16397,26 +16460,61 @@ function startLocalDevLaunchTokenSession(options) {
16397
16460
  return issued;
16398
16461
  }).finally(() => { if (pending?.promise === promise)
16399
16462
  pending = undefined; });
16400
- pending = { digest: companionManifestSha256, promise };
16463
+ pending = { digest: companionManifestSha256, snapshot: developmentSnapshot, promise };
16401
16464
  return promise;
16402
16465
  };
16466
+ // 票据与它绑定的描述通过同一出口原子发布;主动读取和后台续票不能发布不同快照。
16467
+ const publishTicket = (issued) => {
16468
+ if (publishedToken === issued.token && publishedDigest === issued.companionManifestSha256 && publishedPermissionsGeneration === permissionsGeneration)
16469
+ return;
16470
+ publishedPermissionsGeneration = permissionsGeneration;
16471
+ publishedToken = issued.token;
16472
+ publishedTicket = issued;
16473
+ publishedDigest = issued.companionManifestSha256;
16474
+ if (issued.developmentSnapshot)
16475
+ options.onToken(issued.token, publishedDigest, issued.expiresAt, issued.developmentSnapshot);
16476
+ else
16477
+ options.onToken(issued.token, publishedDigest, issued.expiresAt);
16478
+ scheduleExpiration(Math.max(0, issued.expiresAt - Date.now()));
16479
+ };
16480
+ const waitForCurrent = async () => {
16481
+ const generation = permissionsGeneration;
16482
+ if (publishedPermissionsGeneration === generation)
16483
+ return;
16484
+ if (refreshFailure && !refreshPromise)
16485
+ throw refreshFailure;
16486
+ await ensureRefresh();
16487
+ if (closed)
16488
+ return;
16489
+ if (generation !== permissionsGeneration)
16490
+ throw new DevContextSupersededError();
16491
+ // 输入变化可能发生在上一代请求期间;等上一代退出后处理当前候选。
16492
+ if (validatedPermissionsGeneration !== generation && !refreshFailure)
16493
+ await ensureRefresh();
16494
+ if (generation !== permissionsGeneration)
16495
+ throw new DevContextSupersededError();
16496
+ if (publishedPermissionsGeneration !== generation)
16497
+ throw refreshFailure ?? new Error('开发快照尚未就绪,请稍后重试');
16498
+ };
16403
16499
  const getTicket = async (companionManifestSha256) => {
16404
16500
  if (closed)
16405
16501
  return undefined;
16406
16502
  const generation = permissionsGeneration;
16407
- const assertPermissionsReady = () => {
16408
- if (generation !== permissionsGeneration)
16409
- throw new DevContextSupersededError();
16410
- if (permissionsRefreshFailure)
16411
- throw permissionsRefreshFailure.error;
16412
- if (validatedPermissionsGeneration !== permissionsGeneration)
16413
- throw new Error('远端上下文正在刷新,请稍后再试');
16414
- };
16415
- assertPermissionsReady();
16416
- const issued = await acquireTicket(companionManifestSha256);
16503
+ await waitForCurrent();
16417
16504
  if (closed)
16418
16505
  return undefined;
16419
- assertPermissionsReady();
16506
+ if ((!publishedTicket || publishedTicket.refreshAt <= Date.now() ||
16507
+ (companionManifestSha256 !== undefined && publishedTicket.companionManifestSha256 !== companionManifestSha256)) && (!refreshFailure || refreshPromise))
16508
+ await ensureRefresh();
16509
+ if (closed)
16510
+ return undefined;
16511
+ if (generation !== permissionsGeneration || publishedPermissionsGeneration !== generation)
16512
+ throw new DevContextSupersededError();
16513
+ const issued = publishedTicket;
16514
+ if (!issued || issued.expiresAt <= Date.now())
16515
+ throw refreshFailure ?? new Error('开发票据暂不可用,请稍后重试');
16516
+ if (companionManifestSha256 !== undefined && issued.companionManifestSha256 !== companionManifestSha256)
16517
+ throw new Error('开发产物已变化,请重新打开调试');
16420
16518
  return issued;
16421
16519
  };
16422
16520
  const refresh = async () => {
@@ -16425,7 +16523,7 @@ function startLocalDevLaunchTokenSession(options) {
16425
16523
  refreshing = true;
16426
16524
  const generation = permissionsGeneration;
16427
16525
  try {
16428
- if (options.refreshPermissions && permissionsRefreshPending) {
16526
+ if (options.refreshPermissions && (permissionsRefreshPending || permissionsRefreshFailure)) {
16429
16527
  permissionsRefreshPending = false;
16430
16528
  try {
16431
16529
  const nextCompanionDeclared = await options.refreshPermissions();
@@ -16464,11 +16562,9 @@ function startLocalDevLaunchTokenSession(options) {
16464
16562
  return;
16465
16563
  if (closed || generation !== permissionsGeneration)
16466
16564
  return;
16467
- if (!permissionsRefreshFailure && (publishedToken !== issued.token || publishedDigest !== digest)) {
16468
- publishedToken = issued.token;
16469
- publishedDigest = digest;
16470
- options.onToken(issued.token, digest, issued.expiresAt);
16471
- }
16565
+ if (!permissionsRefreshFailure)
16566
+ publishTicket(issued);
16567
+ refreshFailure = undefined;
16472
16568
  options.onReadiness({
16473
16569
  browser: { state: 'ready' },
16474
16570
  mobile: permissionsRefreshFailure
@@ -16487,8 +16583,6 @@ function startLocalDevLaunchTokenSession(options) {
16487
16583
  if (snapshotError)
16488
16584
  options.logger.debug(`PC Companion 快照校验失败:${index.readRedactedErrorMessage(snapshotError, { verbose: true })}`);
16489
16585
  const ttl = Math.max(0, issued.expiresAt - Date.now());
16490
- if (!permissionsRefreshFailure)
16491
- scheduleExpiration(ttl);
16492
16586
  schedule(Math.min(2_000, Math.max(1_000, Math.floor(ttl * 0.8))));
16493
16587
  if (hasReportedTokenFailure && !permissionsRefreshFailure) {
16494
16588
  hasReportedTokenFailure = false;
@@ -16502,6 +16596,7 @@ function startLocalDevLaunchTokenSession(options) {
16502
16596
  catch (error) {
16503
16597
  if (closed || generation !== permissionsGeneration || error instanceof DevContextSupersededError)
16504
16598
  return;
16599
+ refreshFailure = error;
16505
16600
  // A2 + A3:限流类错误走 30s 起、上限 300s 的退避,并明确提示不要反复重启。
16506
16601
  const rateLimitState = index.readCliRateLimitState(error);
16507
16602
  consecutiveFailures += 1;
@@ -16534,9 +16629,20 @@ function startLocalDevLaunchTokenSession(options) {
16534
16629
  }
16535
16630
  }
16536
16631
  };
16632
+ // 包括主动读取在内的所有续票都复用同一轮刷新,不能并行签发相互覆盖的描述。
16633
+ const ensureRefresh = () => {
16634
+ if (refreshPromise)
16635
+ return refreshPromise;
16636
+ const promise = refresh().finally(() => { if (refreshPromise === promise)
16637
+ refreshPromise = undefined; });
16638
+ refreshPromise = promise;
16639
+ return promise;
16640
+ };
16537
16641
  const schedule = (delayMs) => {
16642
+ if (refreshTimer)
16643
+ clearTimeout(refreshTimer);
16538
16644
  refreshTimer = setTimeout(() => {
16539
- void refresh();
16645
+ void ensureRefresh();
16540
16646
  }, Math.max(1, delayMs));
16541
16647
  refreshTimer.unref?.();
16542
16648
  };
@@ -16547,6 +16653,8 @@ function startLocalDevLaunchTokenSession(options) {
16547
16653
  if (!closed) {
16548
16654
  options.onToken(undefined, undefined);
16549
16655
  publishedToken = undefined;
16656
+ publishedTicket = undefined;
16657
+ publishedPermissionsGeneration = -1;
16550
16658
  options.onReadiness({ browser: { state: 'ready' }, mobile: { state: 'blocked', reason: '调试凭证已过期' },
16551
16659
  companion: { state: 'blocked', reason: '调试凭证已过期' } });
16552
16660
  }
@@ -16554,20 +16662,24 @@ function startLocalDevLaunchTokenSession(options) {
16554
16662
  expirationTimer.unref?.();
16555
16663
  };
16556
16664
  options.onReadiness({ browser: { state: 'ready' }, mobile: { state: 'pending' }, companion: { state: 'pending' } });
16557
- const ready = refresh();
16665
+ const ready = ensureRefresh();
16558
16666
  return {
16559
16667
  getTicket,
16560
- refresh: () => {
16668
+ waitForCurrent,
16669
+ refresh: (request = {}) => {
16561
16670
  // 项目绑定变更或开发者主动重试:主动失效 Dev Context 缓存后立即重新请求。
16562
- options.onInvalidate?.();
16671
+ if (request.invalidateContext !== false)
16672
+ options.onInvalidate?.();
16563
16673
  permissionsGeneration += 1;
16564
16674
  permissionsRefreshPending = true;
16675
+ refreshFailure = undefined;
16565
16676
  ticket = undefined;
16566
16677
  pending = undefined;
16567
16678
  publishedDigest = undefined;
16568
16679
  if (refreshTimer)
16569
16680
  clearTimeout(refreshTimer);
16570
- void refresh();
16681
+ options.onReadiness({ browser: { state: 'ready' }, mobile: { state: 'pending' }, companion: { state: 'pending' } });
16682
+ void ensureRefresh();
16571
16683
  },
16572
16684
  ready,
16573
16685
  close: () => {
@@ -16589,6 +16701,7 @@ async function issueLocalDevLaunchToken(options) {
16589
16701
  env: options.env,
16590
16702
  fetchImpl,
16591
16703
  miniProgramId: options.miniProgramId,
16704
+ developmentSnapshot: options.developmentSnapshot,
16592
16705
  ...(options.companionManifestSha256
16593
16706
  ? { companionManifestSha256: options.companionManifestSha256 }
16594
16707
  : {}),
@@ -16604,7 +16717,7 @@ async function issueLocalDevLaunchToken(options) {
16604
16717
  if (!expiresAt || expiresAt <= Date.now()) {
16605
16718
  throw new Error('响应缺少有效 expires_at');
16606
16719
  }
16607
- return { ...(options.companionManifestSha256 ? { companionManifestSha256: options.companionManifestSha256 } : {}), expiresAt, token,
16720
+ return { developmentSnapshot: options.developmentSnapshot, ...(options.companionManifestSha256 ? { companionManifestSha256: options.companionManifestSha256 } : {}), expiresAt, token,
16608
16721
  refreshAt: Date.now() + Math.max(1, (expiresAt - Date.now()) * 0.8) };
16609
16722
  }
16610
16723
  function safeCompanionFailureReason(error) {
@@ -3,26 +3,22 @@
3
3
  var fs = require('node:fs/promises');
4
4
  var os = require('node:os');
5
5
  var path = require('node:path');
6
- var index = require('./index-9trmrUtW.cjs');
6
+ var index = require('./index-B_KKGIBN.cjs');
7
7
  require('node:module');
8
- require('path');
9
- require('os');
10
- require('fs');
11
- require('constants');
12
- require('stream');
13
- require('util');
14
- require('assert');
15
8
  require('readline');
9
+ require('os');
16
10
  require('tty');
11
+ require('assert');
17
12
  require('events');
13
+ require('stream');
18
14
  require('buffer');
15
+ require('util');
19
16
  require('node:crypto');
20
- require('node:process');
21
- require('node:buffer');
22
- require('node:url');
23
- require('node:util');
24
- require('node:child_process');
17
+ require('path');
18
+ require('fs');
19
+ require('constants');
25
20
  require('node:fs');
21
+ require('node:zlib');
26
22
 
27
23
  const CANDIDATES = [
28
24
  path.resolve(__dirname, '..', 'package.json'),