@heybox/hb-sdk 0.5.16 → 0.5.18

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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-B-lA2GE7.cjs');
3
+ var index = require('./index-CsOcGUzF.cjs');
4
4
  var node_crypto = require('node:crypto');
5
5
  var path = require('node:path');
6
6
  var require$$0$2 = require('fs');
@@ -67,57 +67,6 @@ function isTruthyEnvFlag(value) {
67
67
  .toLowerCase());
68
68
  }
69
69
 
70
- const globalHeyboxCliRequestConfig = {};
71
- function readHeyboxCliRequestConfig() {
72
- return normalizeHeyboxRylaiServiceTagConfig(globalHeyboxCliRequestConfig);
73
- }
74
- function resolveHeyboxRylaiServiceTag(pathWithQuery, config = readHeyboxCliRequestConfig()) {
75
- const normalized = normalizeHeyboxRylaiServiceTagConfig(config);
76
- const pathname = pathWithQuery ? getPathname(pathWithQuery) : '';
77
- const specialTag = normalized.special_tag;
78
- if (pathname && specialTag?.path_prefix_list.some((prefix) => pathname.startsWith(prefix))) {
79
- return specialTag.tag_name;
80
- }
81
- return normalized.default_tag;
82
- }
83
- function normalizeHeyboxRylaiServiceTagConfig(config) {
84
- const defaultTag = normalizeHeyboxRylaiServiceTag(config.default_tag);
85
- const specialTagName = normalizeHeyboxRylaiServiceTag(config.special_tag?.tag_name);
86
- const pathPrefixList = config.special_tag?.path_prefix_list?.filter(isValidPathPrefix) || [];
87
- return {
88
- ...(defaultTag ? { default_tag: defaultTag } : {}),
89
- ...(specialTagName && pathPrefixList.length
90
- ? {
91
- special_tag: {
92
- tag_name: specialTagName,
93
- path_prefix_list: pathPrefixList,
94
- },
95
- }
96
- : {}),
97
- };
98
- }
99
- function normalizeHeyboxRylaiServiceTag(value) {
100
- const tag = String(value ?? '').trim();
101
- if (!tag) {
102
- return undefined;
103
- }
104
- if (/[\r\n]/.test(tag)) {
105
- throw new Error('x-rylai-service-tag 不允许包含换行符');
106
- }
107
- return tag;
108
- }
109
- function isValidPathPrefix(value) {
110
- return typeof value === 'string' && value.startsWith('/') && !/[\r\n]/.test(value);
111
- }
112
- function getPathname(pathWithQuery) {
113
- try {
114
- return new URL(pathWithQuery, 'https://api.xiaoheihe.cn').pathname;
115
- }
116
- catch {
117
- return '';
118
- }
119
- }
120
-
121
70
  const SIGN_VERSION = '999.0.4';
122
71
  const SIGN_CHARSET = 'AB45STUVWZEFGJ6CH01D237IXYPQRKLMN89';
123
72
  function createHeyboxOpenPlatformSignParams(pathname, options = {}) {
@@ -217,7 +166,7 @@ function resolveHeyboxId(session) {
217
166
  ?.slice('heybox_id='.length);
218
167
  }
219
168
  function createHeyboxAuthHeaders(session, options = {}) {
220
- const serviceTag = resolveHeyboxRylaiServiceTag(options.pathWithQuery);
169
+ const serviceTag = index.resolveHeyboxRylaiServiceTag(options.pathWithQuery);
221
170
  return {
222
171
  Cookie: session.cookieHeader,
223
172
  Referer: HEYBOX_WEB_REFERER,
@@ -227,7 +176,7 @@ function createHeyboxAuthHeaders(session, options = {}) {
227
176
  }
228
177
  function createHeyboxRequestContext(session, options = {}) {
229
178
  const heyboxId = resolveHeyboxId(session);
230
- const serviceTag = resolveHeyboxRylaiServiceTag(options.pathWithQuery);
179
+ const serviceTag = index.resolveHeyboxRylaiServiceTag(options.pathWithQuery);
231
180
  return {
232
181
  baseUrl: HEYBOX_API_BASE_URL,
233
182
  headers: createHeyboxAuthHeaders(session, options),
@@ -286,7 +235,7 @@ async function readHeyboxApiEnvelope(response, options) {
286
235
  rawBody,
287
236
  pathWithQuery: options.pathWithQuery,
288
237
  });
289
- throw new index.CliError(message, verboseMessage);
238
+ throw new index.CliError(message, verboseMessage, { httpStatus: response.status });
290
239
  }
291
240
  function formatHeyboxEnvelopeError(input) {
292
241
  const parts = [`Heybox API ${input.pathWithQuery} failed`, `HTTP ${input.response.status}`];
@@ -3184,6 +3133,14 @@ function requireLib () {
3184
3133
  var libExports = /*@__PURE__*/ requireLib();
3185
3134
  var fs = /*@__PURE__*/index.getDefaultExportFromCjs(libExports);
3186
3135
 
3136
+ class AuthSessionError extends Error {
3137
+ code;
3138
+ constructor(message, code) {
3139
+ super(message);
3140
+ this.code = code;
3141
+ this.name = 'AuthSessionError';
3142
+ }
3143
+ }
3187
3144
  const AUTH_CACHE_VERSION = 1;
3188
3145
  const paths = index.envPaths('hb-sdk', { suffix: '' });
3189
3146
  function getAuthCacheFilePath(options = {}) {
@@ -3221,7 +3178,7 @@ async function readHeyboxAuthSession(options = {}) {
3221
3178
  async function requireHeyboxAuthSession(options = {}) {
3222
3179
  const session = await readHeyboxAuthSession(options);
3223
3180
  if (!session) {
3224
- throw new Error('未发现 Heybox 登录态,请先运行 hb-sdk login');
3181
+ throw new AuthSessionError('未发现 Heybox 登录态,请先运行 hb-sdk login', 'AUTH_SESSION_MISSING');
3225
3182
  }
3226
3183
  const expectedLoginBaseUrl = resolveHeyboxLoginBaseUrl(options);
3227
3184
  if (session.loginBaseUrl !== expectedLoginBaseUrl) {
@@ -3256,7 +3213,7 @@ function createSelectedDeveloperEntitySnapshot(input, options = {}) {
3256
3213
  async function setSelectedDeveloperEntitySnapshot(input, options = {}) {
3257
3214
  const cache = await readAuthCache(options);
3258
3215
  if (!cache.heybox) {
3259
- throw new Error('未发现 Heybox 登录态,请先运行 hb-sdk login');
3216
+ throw new AuthSessionError('未发现 Heybox 登录态,请先运行 hb-sdk login', 'AUTH_SESSION_MISSING');
3260
3217
  }
3261
3218
  const selectedEntity = createSelectedDeveloperEntitySnapshot(input, options);
3262
3219
  return writeAuthCache({
package/dist/cli.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./cli-chunks/index-B-lA2GE7.cjs');
3
+ var index = require('./cli-chunks/index-CsOcGUzF.cjs');
4
4
  require('node:module');
5
5
  require('node:fs');
6
6
  require('node:fs/promises');
@@ -4938,6 +4938,95 @@ function isNavigationBarForegroundStyle(value) {
4938
4938
  return value === 'light' || value === 'dark';
4939
4939
  }
4940
4940
 
4941
+ const OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS = [
4942
+ 'xiaoheihe.cn',
4943
+ 'max-c.com',
4944
+ 'debugmode.cn',
4945
+ 'maxjia.com',
4946
+ ];
4947
+ /** 使用 URL 解析后的 hostname 判断黑盒官方根域及其子域。 */
4948
+ function isOfficialMiniProgramNetworkUrl(value) {
4949
+ try {
4950
+ const url = new URL(value);
4951
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
4952
+ return false;
4953
+ }
4954
+ const hostname = url.hostname.toLowerCase().replace(/\.$/, '');
4955
+ return OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS.some(rootDomain => hostname === rootDomain || hostname.endsWith(`.${rootDomain}`));
4956
+ }
4957
+ catch {
4958
+ return false;
4959
+ }
4960
+ }
4961
+
4962
+ const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
4963
+ /** 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。 */
4964
+ function parseMiniProgramRuntimePermissions(snapshot) {
4965
+ if (!isRecord$1(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
4966
+ return { valid: false, permissions: {} };
4967
+ }
4968
+ if (snapshot.revision !== undefined &&
4969
+ (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
4970
+ return { valid: false, permissions: {} };
4971
+ }
4972
+ const seenKeys = new Set();
4973
+ const permissions = {};
4974
+ for (const rawEntry of snapshot.entries) {
4975
+ if (!isRecord$1(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
4976
+ return { valid: false, permissions: {} };
4977
+ }
4978
+ const key = rawEntry.key.trim();
4979
+ if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord$1(rawEntry.config)) {
4980
+ return { valid: false, permissions: {} };
4981
+ }
4982
+ seenKeys.add(key);
4983
+ if (!MANAGED_RUNTIME_PERMISSION_KEYS.has(key)) {
4984
+ continue;
4985
+ }
4986
+ if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
4987
+ return { valid: false, permissions: {} };
4988
+ }
4989
+ permissions[key] = {
4990
+ key,
4991
+ status: rawEntry.status,
4992
+ config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
4993
+ };
4994
+ }
4995
+ return { valid: true, permissions };
4996
+ }
4997
+ /** 创建只依赖启动快照的纯权限 evaluator,供正式 Runtime 与 Mock Host 共用。 */
4998
+ function createMiniProgramRuntimePermissionEvaluator(snapshot) {
4999
+ const parsed = parseMiniProgramRuntimePermissions(snapshot);
5000
+ return (permissionKey, payload) => {
5001
+ if (!MANAGED_RUNTIME_PERMISSION_KEYS.has(permissionKey)) {
5002
+ return { allowed: true };
5003
+ }
5004
+ const entry = parsed.valid ? parsed.permissions[permissionKey] : undefined;
5005
+ if (!entry || entry.status !== 'enabled') {
5006
+ return {
5007
+ allowed: false,
5008
+ code: 'PERMISSION_DENIED',
5009
+ message: permissionKey === 'network.request' ? '当前小程序暂不支持网络请求' : '当前小程序暂不支持该能力',
5010
+ };
5011
+ }
5012
+ if (permissionKey === 'network.request' &&
5013
+ isRecord$1(payload) &&
5014
+ typeof payload.url === 'string' &&
5015
+ isOfficialMiniProgramNetworkUrl(payload.url) &&
5016
+ entry.config.useOfficialDomain !== true) {
5017
+ return {
5018
+ allowed: false,
5019
+ code: 'PERMISSION_DENIED',
5020
+ message: '当前小程序暂不支持访问官方域名',
5021
+ };
5022
+ }
5023
+ return { allowed: true };
5024
+ };
5025
+ }
5026
+ function isRecord$1(value) {
5027
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
5028
+ }
5029
+
4941
5030
  const MOCK_DEFAULT_LEADERBOARD_KEY = 'default';
4942
5031
  const MOCK_LEADERBOARD_CACHE_MAX_ENTRIES = 500;
4943
5032
  const MOCK_LEADERBOARD_CURRENT_ENTRY_RANK_COUNT_LIMIT = 5000;
@@ -5129,6 +5218,7 @@ class MiniProgramMockRuntime {
5129
5218
  this.toasts = [];
5130
5219
  this.vibrates = [];
5131
5220
  this.openAppPages = [];
5221
+ this.evaluateRuntimePermission = createMiniProgramRuntimePermissionEvaluator(options.runtimePermissions);
5132
5222
  }
5133
5223
  async runMethod(method, payload) {
5134
5224
  if (!isMiniProgramMockRuntimeMethod(method)) {
@@ -5137,6 +5227,13 @@ class MiniProgramMockRuntime {
5137
5227
  if (this.closed && method !== NAVIGATION_CLOSE_METHOD) {
5138
5228
  throw createMockBridgeError('INVALID_STATE', '当前小程序容器已关闭');
5139
5229
  }
5230
+ const capability = MINI_PROGRAM_PROTOCOL_CAPABILITIES.find(item => item.method === method);
5231
+ if (capability) {
5232
+ const policyResult = this.evaluateRuntimePermission(capability.permission, payload);
5233
+ if (!policyResult.allowed) {
5234
+ throw createMockBridgeError(policyResult.code, policyResult.message);
5235
+ }
5236
+ }
5140
5237
  const handler = MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLERS[method];
5141
5238
  return handler(this, payload);
5142
5239
  }
@@ -6112,16 +6209,14 @@ function isPlainRecord(value) {
6112
6209
  return typeof value === 'object' && value !== null && !Array.isArray(value);
6113
6210
  }
6114
6211
 
6115
- const MINI_PROGRAM_URL_QUERY_PARAM = 'mini_url';
6116
- const MINI_PROGRAM_DEV_SHELL_URL = 'heybox-mini-dev://sandbox';
6117
- const OPEN_IN_APP_URL = 'https://api.xiaoheihe.cn/open_inapp/';
6118
6212
  const NETWORK_PROXY_PATH = '/__hb_sdk_mock_network__';
6119
- const LAN_ADDRESSES_PATH = '/__hb_sdk_lan_addresses__';
6213
+ const MOCK_HOST_BOOTSTRAP_PATH = '/__hb_sdk_bootstrap__';
6120
6214
  const params = new URL(location.href).searchParams;
6121
6215
  const miniUrl = params.get('mini_url');
6122
6216
  let lanAddresses = [];
6123
6217
  let defaultLanAddressId;
6124
6218
  let macAppProtocol;
6219
+ let nativeAppLaunchUnavailableReason;
6125
6220
  const nonce = createNonce();
6126
6221
  const storage = new Map();
6127
6222
  const logs = [];
@@ -6154,6 +6249,8 @@ if (!miniUrl) {
6154
6249
  throw new Error('Missing mini_url query.');
6155
6250
  }
6156
6251
  const miniProgramUrl = miniUrl;
6252
+ const bootstrap = await loadMockHostBootstrap();
6253
+ const runtimePermissions = bootstrap.runtimePermissions;
6157
6254
  const iframe = document.createElement('iframe');
6158
6255
  iframe.src = appendNonce(miniProgramUrl, nonce);
6159
6256
  iframe.allow = 'clipboard-read; clipboard-write';
@@ -6183,6 +6280,7 @@ const runtime = new MiniProgramMockRuntime(createBrowserMockRuntimePlatformAdapt
6183
6280
  },
6184
6281
  }), {
6185
6282
  onAuthChange: (result) => postEvent('authChange', result),
6283
+ runtimePermissions,
6186
6284
  });
6187
6285
  updateUserStatus();
6188
6286
  updateStorageSnapshot();
@@ -6388,23 +6486,30 @@ async function copyDebugPageUrl() {
6388
6486
  }
6389
6487
  }
6390
6488
  async function setupMobileAppQr() {
6391
- const config = await loadMobileLanConfig();
6392
- lanAddresses = config.lanAddresses;
6393
- defaultLanAddressId = config.defaultLanAddressId;
6394
- macAppProtocol = config.macAppProtocol;
6489
+ lanAddresses = bootstrap.lanAddresses;
6490
+ defaultLanAddressId = bootstrap.defaultLanAddressId;
6491
+ macAppProtocol = bootstrap.macAppProtocol;
6492
+ nativeAppLaunchUnavailableReason = bootstrap.nativeAppLaunchUnavailableReason;
6493
+ updateNativeAppLaunchAvailability();
6395
6494
  setupMobileLanSelect();
6396
6495
  await updateMobileQrCode();
6397
6496
  }
6398
6497
  async function openMacApp() {
6399
6498
  await mobileAppQrReady.catch(() => undefined);
6400
- const protocol = macAppProtocol ?? createMacAppProtocol(miniProgramUrl);
6499
+ if (nativeAppLaunchUnavailableReason) {
6500
+ return;
6501
+ }
6502
+ const protocol = macAppProtocol;
6503
+ if (!protocol) {
6504
+ return;
6505
+ }
6401
6506
  console.log('[hb-sdk] Mac App launch protocol:', protocol);
6402
6507
  showMacAppLaunchHint();
6403
6508
  window.location.href = protocol;
6404
6509
  }
6405
- async function loadMobileLanConfig() {
6510
+ async function loadMockHostBootstrap() {
6406
6511
  try {
6407
- const response = await fetch(LAN_ADDRESSES_PATH, {
6512
+ const response = await fetch(MOCK_HOST_BOOTSTRAP_PATH, {
6408
6513
  cache: 'no-store',
6409
6514
  });
6410
6515
  const payload = await response.json();
@@ -6415,11 +6520,17 @@ async function loadMobileLanConfig() {
6415
6520
  defaultLanAddressId: typeof payload.defaultLanAddressId === 'string' ? payload.defaultLanAddressId : undefined,
6416
6521
  lanAddresses: Array.isArray(payload.lanAddresses) ? payload.lanAddresses.filter(isLanAddress) : [],
6417
6522
  macAppProtocol: typeof payload.macAppProtocol === 'string' ? payload.macAppProtocol : undefined,
6523
+ nativeAppLaunchUnavailableReason: typeof payload.nativeAppLaunchUnavailableReason === 'string'
6524
+ ? payload.nativeAppLaunchUnavailableReason
6525
+ : undefined,
6526
+ runtimePermissions: payload.runtimePermissions,
6418
6527
  };
6419
6528
  }
6420
6529
  catch {
6421
- elements.mobileQrStatus.textContent = '二维码初始化失败';
6422
- return { lanAddresses: [] };
6530
+ return {
6531
+ lanAddresses: [],
6532
+ nativeAppLaunchUnavailableReason: '真机调试配置加载失败,请重启 hb-sdk dev 后重试。',
6533
+ };
6423
6534
  }
6424
6535
  }
6425
6536
  function setupMobileLanSelect() {
@@ -6438,6 +6549,12 @@ function setupMobileLanSelect() {
6438
6549
  elements.mobileLanSelect.disabled = !hasLanAddress;
6439
6550
  }
6440
6551
  async function updateMobileQrCode() {
6552
+ if (nativeAppLaunchUnavailableReason) {
6553
+ elements.mobileQrImage.removeAttribute('src');
6554
+ elements.mobileQrImage.hidden = true;
6555
+ elements.mobileQrStatus.textContent = nativeAppLaunchUnavailableReason;
6556
+ return;
6557
+ }
6441
6558
  const selected = readSelectedLanAddress();
6442
6559
  if (!selected) {
6443
6560
  elements.mobileQrImage.removeAttribute('src');
@@ -6445,7 +6562,13 @@ async function updateMobileQrCode() {
6445
6562
  elements.mobileQrStatus.textContent = '';
6446
6563
  return;
6447
6564
  }
6448
- const payload = selected.mobileAppQrPayload ?? createMobileAppQrPayload(selected.appUrl);
6565
+ const payload = selected.mobileAppQrPayload;
6566
+ if (!payload) {
6567
+ elements.mobileQrImage.removeAttribute('src');
6568
+ elements.mobileQrImage.hidden = true;
6569
+ elements.mobileQrStatus.textContent = '真机调试配置不可用,请重启 hb-sdk dev 后重试。';
6570
+ return;
6571
+ }
6449
6572
  elements.mobileQrStatus.textContent = '正在生成二维码...';
6450
6573
  try {
6451
6574
  elements.mobileQrImage.src = await browserExports.toDataURL(payload, {
@@ -6462,40 +6585,16 @@ async function updateMobileQrCode() {
6462
6585
  elements.mobileQrStatus.textContent = '二维码生成失败';
6463
6586
  }
6464
6587
  }
6465
- function readSelectedLanAddress() {
6466
- return lanAddresses.find((address) => address.id === elements.mobileLanSelect.value) ?? lanAddresses[0];
6467
- }
6468
- function createMacAppProtocol(appUrl) {
6469
- return createHeyboxProtocol(createMiniProgramDevShellOpenWindowPayload(appUrl));
6470
- }
6471
- function createMobileAppQrPayload(appUrl) {
6472
- return `${OPEN_IN_APP_URL}#${createHeyboxProtocol(createMiniProgramDevShellOpenWindowPayload(appUrl, { encodeMiniUrl: false }))}`;
6473
- }
6474
- function createMiniProgramDevShellOpenWindowPayload(appUrl, options = {}) {
6475
- return {
6476
- protocol_type: 'openWindow',
6477
- full_screen: true,
6478
- mini_program: '1',
6479
- navigation_bar: {
6480
- title: '',
6481
- },
6482
- webview: {
6483
- url: createMiniProgramDevShellUrl(appUrl, options),
6484
- pull: false,
6485
- refresh: false,
6486
- },
6487
- };
6488
- }
6489
- function createMiniProgramDevShellUrl(appUrl, options) {
6490
- if (options.encodeMiniUrl === false) {
6491
- return `${MINI_PROGRAM_DEV_SHELL_URL}?${MINI_PROGRAM_URL_QUERY_PARAM}=${appUrl}`;
6588
+ function updateNativeAppLaunchAvailability() {
6589
+ const isUnavailable = Boolean(nativeAppLaunchUnavailableReason);
6590
+ elements.macAppButton.disabled = isUnavailable;
6591
+ if (nativeAppLaunchUnavailableReason) {
6592
+ elements.macAppStatus.textContent = nativeAppLaunchUnavailableReason;
6593
+ elements.macAppStatus.hidden = false;
6492
6594
  }
6493
- const devShellUrl = new URL(MINI_PROGRAM_DEV_SHELL_URL);
6494
- devShellUrl.searchParams.set(MINI_PROGRAM_URL_QUERY_PARAM, appUrl);
6495
- return devShellUrl.toString();
6496
6595
  }
6497
- function createHeyboxProtocol(payload) {
6498
- return `heybox://${encodeURIComponent(JSON.stringify(payload))}`;
6596
+ function readSelectedLanAddress() {
6597
+ return lanAddresses.find((address) => address.id === elements.mobileLanSelect.value) ?? lanAddresses[0];
6499
6598
  }
6500
6599
  function isLanAddress(value) {
6501
6600
  return (isRecord(value) &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heybox/hb-sdk",
3
- "version": "0.5.16",
3
+ "version": "0.5.18",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
package/skill/SKILL.md CHANGED
@@ -49,7 +49,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
49
49
  ## Step 5: Use CLI workflows
50
50
 
51
51
  1. Use `hb-sdk create <project-name>` to scaffold a standalone external mini-program template.
52
- 2. Use `hb-sdk dev` for local browser debugging through the built-in mock runtime host.
52
+ 2. Use `hb-sdk dev` for local browser debugging through the built-in mock runtime host. The project must bind `package.json.heybox.miniProgramId` and the CLI must be logged in so dev can load the real Runtime permission snapshot before starting Vite.
53
53
  3. Use `hb-sdk remote ...` for developer-owned remote mini-program management. Top-level `hb-sdk deploy` has been hard-cut and must not be recommended as a compatibility alias.
54
54
  4. Use `hb-sdk remote entity list`, `hb-sdk remote entity current`, and `hb-sdk remote entity switch <entity-id>` to inspect or change the developer platform server-side current entity before remote management commands.
55
55
  5. Use `hb-sdk remote create` to create a remote mini-program under the server-side current entity and bind the returned id into `package.json.heybox.miniProgramId`; use `hb-sdk remote bind <mini-program-id>` to bind an existing remote mini-program after current-entity manageability is verified. Mini-program name, icon, and cover images belong in `package.json.heybox.miniProgramProfile` and are submitted with version audit.
@@ -19,7 +19,7 @@
19
19
  ## Package metadata
20
20
 
21
21
  - Package: `@heybox/hb-sdk`
22
- - Version at generation time: `0.5.14`
22
+ - Version at generation time: `0.5.17`
23
23
  - Public root export: `@heybox/hb-sdk`
24
24
  - Protocol export: `@heybox/hb-sdk/protocol`
25
25
  - Vite plugin export: `@heybox/hb-sdk/vite`
@@ -347,7 +347,7 @@ SDK 需要在黑盒小程序 iframe 容器内运行。父容器会为页面注
347
347
  npm run dev
348
348
  ```
349
349
 
350
- 调试页会通过 iframe 加载本地页面并补齐小程序 bridge 环境;如果需要真实黑盒小程序容器加载同一页面,可以点击调试页里的「在 Mac 版 APP 中启动」按钮,或在「Mobile App」区域选择局域网网卡后用手机小黑盒 APP 扫码。真实容器加载开发 `mini_url` 前会提示“即将打开未经验证的开发网页。该页面可能由本机或局域网服务提供,请确认来源可信后继续。”,用户确认后才继续加载。Codex、VSCode 等内嵌浏览器可能无法唤起系统 APP;遇到这种情况时,请在系统浏览器中打开同一个调试页后重试。
350
+ 调试页会通过 iframe 加载本地页面并补齐小程序 bridge 环境;浏览器 Mock 从 mock host 的同源只读 bootstrap 接口读取服务端保留的 Runtime 权限快照,URL query 不能提供或覆盖权限。项目已绑定 `heybox.miniProgramId` 时,可以点击调试页里的「在 Mac 版 APP 中启动」按钮,或在「Mobile App」区域选择局域网网卡后用手机小黑盒 APP 扫码;真机 dev shell 只携带绑定的小程序 ID 和本地页面地址,H5 宿主按 ID 从公开详情接口读取可信 Runtime 权限快照。项目未绑定时仍可使用默认拒绝受管能力的浏览器 Mock,但 Mac 和手机真机入口会明确提示先运行 `hb-sdk remote create` 或 `hb-sdk remote bind <mini-program-id>`,并保持不可用。真实容器加载开发 `mini_url` 前会提示“即将打开未经验证的开发网页。该页面可能由本机或局域网服务提供,请确认来源可信后继续。”,用户确认后才继续加载。Codex、VSCode 等内嵌浏览器可能无法唤起系统 APP;遇到这种情况时,请在系统浏览器中打开同一个调试页后重试。
351
351
 
352
352
  在未使用脚手架的 Vite 项目中,可以把命令加到 `package.json`:
353
353
 
@@ -638,6 +638,8 @@ await network.request({
638
638
 
639
639
  支持的 HTTP method 为 `GET`、`POST`、`PUT`、`PATCH`、`DELETE`、`HEAD`、`OPTIONS`。
640
640
 
641
+ `network.request` 由平台 Runtime 权限控制。未授权时 SDK 会收到 `PERMISSION_DENIED` 和“当前小程序暂不支持网络请求”;即使已开启网络请求,访问黑盒官方域名仍需要运营侧单独开启官方域名配置。业务代码不能自行请求或注入 Cookie、pkey 等官方凭据。
642
+
641
643
  ## 生命周期事件
642
644
 
643
645
  SDK 实例创建后会自动开始与父容器握手。使用 `on()` 监听父容器派发的小程序事件时,默认单例会被懒创建并自动开始握手;`on()` 会返回取消监听函数,组件卸载或页面销毁时应及时调用。
@@ -103,7 +103,7 @@ Agent rules:
103
103
  hb-sdk dev
104
104
  ```
105
105
 
106
- `hb-sdk dev` 会从当前目录向上查找最近的 `package.json` 作为项目根目录,加载该项目安装的 `vite`,并优先使用项目内的 Vite 配置文件。命令会同时启动内置 mock runtime host 并默认打开调试页;如果需要真实黑盒小程序容器加载同一页面,点击调试页里的「在 Mac APP 中启动」按钮,或在「Mobile App」区域选择局域网网卡后用手机小黑盒 APP 扫码。手机需要与电脑处在同一局域网,并使用支持小程序调试壳的新版小黑盒 APP。Codex、VSCode 等内嵌浏览器可能无法转交 `heybox://` 协议;需要从调试页唤起 Mac 版 APP 时,请先在系统浏览器中打开调试页。项目没有安装 Vite 时,命令会提示安装依赖或把 Vite 放到 `devDependencies`。
106
+ `hb-sdk dev` 会从当前目录向上查找最近的 `package.json`,并在项目已绑定且 CLI 已登录时尝试查询真实 Runtime 权限快照。项目未绑定、未登录或远端暂时不可用时,命令会输出警告并继续启动 Vite 和内置 mock runtime host;此时使用缺失权限快照,`network.request` 等受管能力默认拒绝。远端权限读取成功时,mock host 使用真实快照,远端明确关闭的能力仍稳定返回 `PERMISSION_DENIED`。本地 `package.json` 无法解析等配置错误不会被降级,命令会直接报错。调试页默认自动打开;需要真实黑盒小程序容器加载同一页面时,点击调试页里的「在 Mac 版 APP 中启动」按钮,或在「Mobile App」区域选择局域网网卡后用手机小黑盒 APP 扫码。
107
107
 
108
108
  常用参数:
109
109
 
@@ -116,11 +116,11 @@ hb-sdk dev
116
116
 
117
117
  ## Mock runtime 边界
118
118
 
119
- `hb-sdk dev` 会把实际小程序页面地址编码到 mock host 的 `mini_url` query 中,由 mock host iframe 加载页面并补齐小程序 bridge 环境。mock host 内置登录/登出、`show`、`hide`、在 Mac 版 APP 中启动、手机扫码调试等调试入口,并通过同一份 devtools-only runtime adapter 处理 SDK 能力调用。真实 Mac / Mobile App dev shell 加载开发 `mini_url` 前会提示“即将打开未经验证的开发网页。该页面可能由本机或局域网服务提供,请确认来源可信后继续。”,用户确认后才继续加载。Codex、VSCode 等内嵌浏览器可能无法唤起系统 APP;遇到这种情况时,请在系统浏览器中打开同一个调试页后重试。
119
+ `hb-sdk dev` 会把实际小程序页面地址编码到 mock host 的 `mini_url` query 中,由 mock host iframe 加载页面并补齐小程序 bridge 环境;浏览器 Mock 的 Runtime 权限只从 mock host 同源只读 bootstrap 接口读取,URL query 不能提供或覆盖权限。项目已绑定时,Mac 协议和手机二维码会把 `package.json.heybox.miniProgramId` 与本地页面地址交给 dev shell;H5 宿主按该 ID 从公开详情接口读取可信 Runtime 权限快照。项目未绑定时仍可使用默认拒绝受管能力的浏览器 Mock,但 Mac 和手机真机入口会明确提示先运行 `hb-sdk remote create` 或 `hb-sdk remote bind <mini-program-id>`,并保持不可用。mock host 内置登录/登出、`show`、`hide`、在 Mac 版 APP 中启动、手机扫码调试等调试入口,并通过同一份 devtools-only runtime adapter 处理 SDK 能力调用。真实 Mac / Mobile App dev shell 加载开发 `mini_url` 前会提示“即将打开未经验证的开发网页。该页面可能由本机或局域网服务提供,请确认来源可信后继续。”,用户确认后才继续加载。Codex、VSCode 等内嵌浏览器可能无法唤起系统 APP;遇到这种情况时,请在系统浏览器中打开同一个调试页后重试。
120
120
 
121
121
  不要为了本地调试再创建独立 mock runtime 包。CLI、模板和 mock host 都归属 `@heybox/hb-sdk`。
122
122
 
123
- 本地 mock runtime 下的 `network.request()` 会通过 `hb-sdk` 本地 mock network proxy 转发真实 HTTP(S) 请求,用于避免浏览器 CORS 影响本地调试。proxy 不会把黑盒客户端私有协议字段暴露给 iframe 业务代码。
123
+ 本地 mock runtime 下已授权的 `network.request()` 会通过 `hb-sdk` 本地 mock network proxy 转发真实 HTTP(S) 请求,用于避免浏览器 CORS 影响本地调试。远端权限可用时 Mock Host 使用远端详情返回的同一份权限快照;不可用时使用缺失快照并默认拒绝受管能力。proxy 不会把黑盒客户端私有协议字段暴露给 iframe 业务代码。
124
124
 
125
125
  Use `hb-sdk dev` for local browser SDK debugging. Use the Mock runtime host's "在 Mac 版 APP 中启动" button for Mac App debugging, or the "Mobile App" QR code after selecting a LAN interface for phone App debugging. The phone must be on the same LAN and use a Heybox App version that supports the mini-program dev shell. If the mock host is open inside Codex, VSCode, or another embedded browser, ask the user to open the same debug page in the system browser before retrying because embedded browsers may block the `heybox://` protocol handoff.
126
126
 
@@ -169,10 +169,27 @@ HB_SDK_ALLOW_UNSAFE_API_BASE_URL=1 hb-sdk remote deploy --api-base-url http://12
169
169
 
170
170
  默认发布策略是 `auto_publish=false`:运营审核通过后使用 `hb-sdk remote versions` 查看版本状态,再用 `hb-sdk remote release <version>` 发布。需要审核通过后自动发布并下架旧线上版本时,使用 `--auto-publish`。如需让指定用户预览未发布候选版本,使用 `hb-sdk remote allowlist add <heybox_id>` 管理预览白名单。
171
171
 
172
- 内部测试或预发环境可通过 `HB_SDK_API_BASE_URL` / `HB_SDK_LOGIN_BASE_URL` 设置默认后台环境,也可以用 `--api-base-url` / `--login-base-url` 覆盖单次命令。自定义地址只接受 origin,不允许包含 path、query 或 hash;API origin 默认还必须是 Heybox 受信 HTTPS 域名,只有本地联调等场景可显式使用 `--allow-unsafe-api-base-url` 或 `HB_SDK_ALLOW_UNSAFE_API_BASE_URL=1` 放开。`apiBaseUrl` 影响 `hb-sdk remote` 里的远端平台后台 API,包括预检、CDN 上传凭证/回调、提交审核、版本、发布、撤回、下架、重新上架、详情和白名单等调用;`loginBaseUrl` 用于 `hb-sdk login` 的登录入口,以及 remote 命令前校验当前 CLI 登录态是否属于同一个登录环境。开发环境如需给后台请求带 `x-rylai-service-tag` 和 `special_tag`,可在 `packages/hb-sdk/src/cli/config.ts` 里按 `@heybox/hb-types` 的 `RylaiServiceTagConfig` 配置 `default_tag` 或 path-prefix 级 `special_tag`。日志只输出 origin,不输出带身份和签名参数的完整请求 URL。
172
+ 内部测试或预发环境可通过 `HB_SDK_API_BASE_URL` / `HB_SDK_LOGIN_BASE_URL` 设置默认后台环境,也可以用 `--api-base-url` / `--login-base-url` 覆盖单次命令。自定义地址只接受 origin,不允许包含 path、query 或 hash;API origin 默认还必须是 Heybox 受信 HTTPS 域名,只有本地联调等场景可显式使用 `--allow-unsafe-api-base-url` 或 `HB_SDK_ALLOW_UNSAFE_API_BASE_URL=1` 放开。`apiBaseUrl` 影响 `hb-sdk remote` 里的远端平台后台 API,包括预检、CDN 上传凭证/回调、提交审核、版本、发布、撤回、下架、重新上架、详情和白名单等调用;`loginBaseUrl` 用于 `hb-sdk login` 的登录入口,以及 remote 命令前校验当前 CLI 登录态是否属于同一个登录环境。开发环境如需给后台请求带 `x-rylai-service-tag` 和 `special_tag`,优先用 `--service-tag <tag>` 或 `HB_SDK_SERVICE_TAG`;path-prefix 级 `special_tag` 仍可在 `packages/hb-sdk/src/cli/config.ts` 里按 `@heybox/hb-types` 的 `RylaiServiceTagConfig` 配置。日志只输出 origin,不输出带身份和签名参数的完整请求 URL。
173
173
 
174
174
  `hb-sdk doctor`、npm latest 检查、mock host 的 `network.request()` 不受这些配置影响。
175
175
 
176
+ 切换测试环境时往往需要同时配置后台 host、登录 host 和 service tag。CLI 支持加载 `.env.<name>` 预设,把其中的 `HB_SDK_*` 变量一次性注入当前进程(不覆盖已有值):`--env <name>` 读取项目根下的 `.env.<name>`,`--env-file <path>` 显式指定路径且优先级更高,也可用 `HB_SDK_ENV=<name>` 触发。文件仅支持 `KEY=VALUE`、空行和 `#` 注释,不做变量插值;文件不存在时静默跳过。
177
+
178
+ ```bash
179
+ # 项目根准备 .env.test
180
+ # HB_SDK_API_BASE_URL=https://api.test.xiaoheihe.cn
181
+ # HB_SDK_LOGIN_BASE_URL=https://login.test.xiaoheihe.cn
182
+ # HB_SDK_SERVICE_TAG=my-test-tag
183
+
184
+ hb-sdk remote deploy --env test --release-note "测试环境验证"
185
+ hb-sdk remote info --env test
186
+ HB_SDK_ENV=test hb-sdk remote versions
187
+ hb-sdk remote deploy --env-file .env.local --release-note "本地联调"
188
+ hb-sdk remote deploy --env test --service-tag other-tag --release-note "灰度" # 单项 flag 覆盖预设
189
+ ```
190
+
191
+ `--env-file` 优先于 `--env`,`--service-tag` / `--api-base-url` / `--login-base-url` 优先于 `.env` 预设注入的同名变量,而预设又不会覆盖进程已有的环境变量,因此优先级为 flag > 进程 env > `.env` 预设 > 默认值。
192
+
176
193
  Before precheck, build, upload, or submit audit, `hb-sdk remote deploy` must verify that the current project's bound mini-program belongs to the server-side current entity. If `detail.entity_id` differs from the current entity, the command fails with both entity ids/names and suggests `hb-sdk remote entity switch <entity-id>`. It must not auto-switch entities and must not continue into precheck/build/upload/submit on mismatch.
177
194
 
178
195
  Agent rules:
package/skill/skill.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "hb-sdk",
3
- "skillVersion": "0.5.14+skill.2ed2b210acdb",
3
+ "skillVersion": "0.5.17+skill.0ad061ac9da8",
4
4
  "sdk": {
5
5
  "package": "@heybox/hb-sdk",
6
- "version": "0.5.14",
7
- "compatibility": "0.5.14"
6
+ "version": "0.5.17",
7
+ "compatibility": "0.5.17"
8
8
  },
9
9
  "source": "https://open.xiaoheihe.cn/agent-skills/hb-sdk",
10
- "integrity": "sha256-2ed2b210acdb606888cde6fb80559ce26500c105b143914910b88b3d6b06eaf9"
10
+ "integrity": "sha256-0ad061ac9da821bfea0db71d30404999c1bd3eb5fcddfb0c22211bd9939db2ae"
11
11
  }