@heybox/hb-sdk 0.2.0-alpha.2 → 0.3.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.
package/dist/cli.cjs CHANGED
@@ -22,14 +22,14 @@ var require$$4$3 = require('https');
22
22
  var require$$5$2 = require('zlib');
23
23
  var require$$1$4 = require('querystring');
24
24
  var require$$3$3 = require('tls');
25
- var node_http = require('node:http');
25
+ var node_crypto = require('node:crypto');
26
26
  var node_url = require('node:url');
27
27
  var net = require('node:net');
28
28
  var os = require('node:os');
29
+ var node_http = require('node:http');
29
30
  var process$1 = require('node:process');
30
31
  var node_buffer = require('node:buffer');
31
32
  var node_util = require('node:util');
32
- var node_crypto = require('node:crypto');
33
33
 
34
34
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
35
35
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
@@ -5162,15 +5162,55 @@ function quoteShellPath(value) {
5162
5162
  return `'${value.replace(/'/g, "'\\''")}'`;
5163
5163
  }
5164
5164
 
5165
+ const MINIAPP_TEMPLATE_VERSION = '0.0.0';
5166
+ const PUBLISH_VERSION_RE = /^\d+\.\d+\.\d+$/;
5167
+ function isValidMiniappManifestVersion(version) {
5168
+ return PUBLISH_VERSION_RE.test(String(version || '').trim());
5169
+ }
5170
+ function parseMiniappManifestJson(raw, sourceLabel = 'manifest.json') {
5171
+ const hadBom = raw.charCodeAt(0) === 0xfeff;
5172
+ const text = hadBom ? raw.slice(1) : raw;
5173
+ let parsed;
5174
+ try {
5175
+ parsed = JSON.parse(text);
5176
+ }
5177
+ catch (error) {
5178
+ throw new Error(`${sourceLabel} 不是合法 JSON:${formatReason(error)}`);
5179
+ }
5180
+ if (!isManifestRecord(parsed)) {
5181
+ throw new Error(`${sourceLabel} 必须是 JSON 对象`);
5182
+ }
5183
+ return {
5184
+ manifest: {
5185
+ version: parsed.version,
5186
+ },
5187
+ hadBom,
5188
+ };
5189
+ }
5190
+ function validateMiniappManifestForDeploy(manifest) {
5191
+ if (manifest.version === MINIAPP_TEMPLATE_VERSION) {
5192
+ throw new Error('manifest.version 仍是模板默认的 0.0.0,请改成实际版本号后再发布');
5193
+ }
5194
+ if (typeof manifest.version !== 'string' || !isValidMiniappManifestVersion(manifest.version)) {
5195
+ throw new Error(`manifest.version 格式错误,必须是 x.y.z:${String(manifest.version)}`);
5196
+ }
5197
+ return manifest.version;
5198
+ }
5199
+ function isManifestRecord(value) {
5200
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
5201
+ }
5202
+ function formatReason(error) {
5203
+ if (error instanceof Error && error.message) {
5204
+ return error.message;
5205
+ }
5206
+ return String(error);
5207
+ }
5208
+
5165
5209
  const MINIAPP_UPLOAD_SCOPE = 'activity';
5166
5210
  const ACTIVITY_UPLOAD_KEY_MAX_LENGTH = 64;
5167
5211
  const PUBLISH_USER_MINIPROGRAM_API_PATH = '/mall/developer/user_miniprogram/publish';
5168
- const VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
5169
5212
  const FNV_OFFSET = 0x811c9dc5;
5170
5213
  const FNV_PRIME = 0x01000193;
5171
- function isValidVersion(version) {
5172
- return VERSION_PATTERN.test(String(version || '').trim());
5173
- }
5174
5214
  function normalizeRelativePath(relativePath) {
5175
5215
  return String(relativePath || '')
5176
5216
  .replace(/\\/g, '/')
@@ -6596,30 +6636,47 @@ function requireUtimes () {
6596
6636
  const u = requireUniversalify().fromPromise;
6597
6637
 
6598
6638
  async function utimesMillis (path, atime, mtime) {
6599
- // if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
6600
6639
  const fd = await fs.open(path, 'r+');
6601
6640
 
6602
- let closeErr = null;
6641
+ let error = null;
6603
6642
 
6604
6643
  try {
6605
6644
  await fs.futimes(fd, atime, mtime);
6645
+ } catch (futimesErr) {
6646
+ error = futimesErr;
6606
6647
  } finally {
6607
6648
  try {
6608
6649
  await fs.close(fd);
6609
- } catch (e) {
6610
- closeErr = e;
6650
+ } catch (closeErr) {
6651
+ if (!error) error = closeErr;
6611
6652
  }
6612
6653
  }
6613
6654
 
6614
- if (closeErr) {
6615
- throw closeErr
6655
+ if (error) {
6656
+ throw error
6616
6657
  }
6617
6658
  }
6618
6659
 
6619
6660
  function utimesMillisSync (path, atime, mtime) {
6620
6661
  const fd = fs.openSync(path, 'r+');
6621
- fs.futimesSync(fd, atime, mtime);
6622
- return fs.closeSync(fd)
6662
+
6663
+ let error = null;
6664
+
6665
+ try {
6666
+ fs.futimesSync(fd, atime, mtime);
6667
+ } catch (futimesErr) {
6668
+ error = futimesErr;
6669
+ } finally {
6670
+ try {
6671
+ fs.closeSync(fd);
6672
+ } catch (closeErr) {
6673
+ if (!error) error = closeErr;
6674
+ }
6675
+ }
6676
+
6677
+ if (error) {
6678
+ throw error
6679
+ }
6623
6680
  }
6624
6681
 
6625
6682
  utimes = {
@@ -7383,14 +7440,14 @@ function requireLink () {
7383
7440
  async function createLink (srcpath, dstpath) {
7384
7441
  let dstStat;
7385
7442
  try {
7386
- dstStat = await fs.lstat(dstpath);
7443
+ dstStat = await fs.lstat(dstpath, { bigint: true });
7387
7444
  } catch {
7388
7445
  // ignore error
7389
7446
  }
7390
7447
 
7391
7448
  let srcStat;
7392
7449
  try {
7393
- srcStat = await fs.lstat(srcpath);
7450
+ srcStat = await fs.lstat(srcpath, { bigint: true });
7394
7451
  } catch (err) {
7395
7452
  err.message = err.message.replace('lstat', 'ensureLink');
7396
7453
  throw err
@@ -7412,11 +7469,11 @@ function requireLink () {
7412
7469
  function createLinkSync (srcpath, dstpath) {
7413
7470
  let dstStat;
7414
7471
  try {
7415
- dstStat = fs.lstatSync(dstpath);
7472
+ dstStat = fs.lstatSync(dstpath, { bigint: true });
7416
7473
  } catch {}
7417
7474
 
7418
7475
  try {
7419
- const srcStat = fs.lstatSync(srcpath);
7476
+ const srcStat = fs.lstatSync(srcpath, { bigint: true });
7420
7477
  if (dstStat && areIdentical(srcStat, dstStat)) return
7421
7478
  } catch (err) {
7422
7479
  err.message = err.message.replace('lstat', 'ensureLink');
@@ -7620,18 +7677,18 @@ function requireSymlink () {
7620
7677
  // (standard symlink behavior) or fall back to cwd if that doesn't exist
7621
7678
  let srcStat;
7622
7679
  if (path.isAbsolute(srcpath)) {
7623
- srcStat = await fs.stat(srcpath);
7680
+ srcStat = await fs.stat(srcpath, { bigint: true });
7624
7681
  } else {
7625
7682
  const dstdir = path.dirname(dstpath);
7626
7683
  const relativeToDst = path.join(dstdir, srcpath);
7627
7684
  try {
7628
- srcStat = await fs.stat(relativeToDst);
7685
+ srcStat = await fs.stat(relativeToDst, { bigint: true });
7629
7686
  } catch {
7630
- srcStat = await fs.stat(srcpath);
7687
+ srcStat = await fs.stat(srcpath, { bigint: true });
7631
7688
  }
7632
7689
  }
7633
7690
 
7634
- const dstStat = await fs.stat(dstpath);
7691
+ const dstStat = await fs.stat(dstpath, { bigint: true });
7635
7692
  if (areIdentical(srcStat, dstStat)) return
7636
7693
  }
7637
7694
 
@@ -7657,18 +7714,18 @@ function requireSymlink () {
7657
7714
  // (standard symlink behavior) or fall back to cwd if that doesn't exist
7658
7715
  let srcStat;
7659
7716
  if (path.isAbsolute(srcpath)) {
7660
- srcStat = fs.statSync(srcpath);
7717
+ srcStat = fs.statSync(srcpath, { bigint: true });
7661
7718
  } else {
7662
7719
  const dstdir = path.dirname(dstpath);
7663
7720
  const relativeToDst = path.join(dstdir, srcpath);
7664
7721
  try {
7665
- srcStat = fs.statSync(relativeToDst);
7722
+ srcStat = fs.statSync(relativeToDst, { bigint: true });
7666
7723
  } catch {
7667
- srcStat = fs.statSync(srcpath);
7724
+ srcStat = fs.statSync(srcpath, { bigint: true });
7668
7725
  }
7669
7726
  }
7670
7727
 
7671
- const dstStat = fs.statSync(dstpath);
7728
+ const dstStat = fs.statSync(dstpath, { bigint: true });
7672
7729
  if (areIdentical(srcStat, dstStat)) return
7673
7730
  }
7674
7731
 
@@ -8224,7 +8281,7 @@ function createCookie(name, value) {
8224
8281
  return `${name}=${value}`;
8225
8282
  }
8226
8283
  function isHeyboxAuthSession(value) {
8227
- if (!isRecord$1(value)) {
8284
+ if (!isRecord$2(value)) {
8228
8285
  return false;
8229
8286
  }
8230
8287
  return (typeof value.heyboxId === 'string' &&
@@ -8233,7 +8290,7 @@ function isHeyboxAuthSession(value) {
8233
8290
  typeof value.cookieHeader === 'string' &&
8234
8291
  typeof value.loggedInAt === 'string');
8235
8292
  }
8236
- function isRecord$1(value) {
8293
+ function isRecord$2(value) {
8237
8294
  return Object.prototype.toString.call(value) === '[object Object]';
8238
8295
  }
8239
8296
 
@@ -59248,11 +59305,6 @@ function requireCompile () {
59248
59305
  , defaultsHash = {}
59249
59306
  , customRules = [];
59250
59307
 
59251
- function patternCode(i, patterns) {
59252
- var regExpCode = opts.regExp ? 'regExp' : 'new RegExp';
59253
- return 'var pattern' + i + ' = ' + regExpCode + '(' + util.toQuotedString(patterns[i]) + ');';
59254
- }
59255
-
59256
59308
  root = root || { schema: schema, refVal: refVal, refs: refs };
59257
59309
 
59258
59310
  var c = checkCompiling.call(this, schema, root, baseId);
@@ -59339,7 +59391,6 @@ function requireCompile () {
59339
59391
  'equal',
59340
59392
  'ucs2length',
59341
59393
  'ValidationError',
59342
- 'regExp',
59343
59394
  sourceCode
59344
59395
  );
59345
59396
 
@@ -59353,8 +59404,7 @@ function requireCompile () {
59353
59404
  customRules,
59354
59405
  equal,
59355
59406
  ucs2length,
59356
- ValidationError,
59357
- opts.regExp
59407
+ ValidationError
59358
59408
  );
59359
59409
 
59360
59410
  refVal[0] = validate;
@@ -59571,6 +59621,11 @@ function requireCompile () {
59571
59621
  }
59572
59622
 
59573
59623
 
59624
+ function patternCode(i, patterns) {
59625
+ return 'var pattern' + i + ' = new RegExp(' + util.toQuotedString(patterns[i]) + ');';
59626
+ }
59627
+
59628
+
59574
59629
  function defaultCode(i) {
59575
59630
  return 'var default' + i + ' = defaults[' + i + '];';
59576
59631
  }
@@ -61594,7 +61649,6 @@ function requirePattern () {
61594
61649
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
61595
61650
  var $breakOnError = !it.opts.allErrors;
61596
61651
  var $data = 'data' + ($dataLvl || '');
61597
- var $valid = 'valid' + $lvl;
61598
61652
  var $isData = it.opts.$data && $schema && $schema.$data,
61599
61653
  $schemaValue;
61600
61654
  if ($isData) {
@@ -61603,21 +61657,12 @@ function requirePattern () {
61603
61657
  } else {
61604
61658
  $schemaValue = $schema;
61605
61659
  }
61606
- var $regExpCode = it.opts.regExp ? 'regExp' : 'new RegExp';
61660
+ var $regexp = $isData ? '(new RegExp(' + $schemaValue + '))' : it.usePattern($schema);
61661
+ out += 'if ( ';
61607
61662
  if ($isData) {
61608
- out += ' var ' + ($valid) + ' = true; try { ' + ($valid) + ' = ' + ($regExpCode) + '(' + ($schemaValue) + ').test(' + ($data) + '); } catch(e) { ' + ($valid) + ' = false; } if ( ';
61609
- if ($isData) {
61610
- out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || ';
61611
- }
61612
- out += ' !' + ($valid) + ') {';
61613
- } else {
61614
- var $regexp = it.usePattern($schema);
61615
- out += ' if ( ';
61616
- if ($isData) {
61617
- out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || ';
61618
- }
61619
- out += ' !' + ($regexp) + '.test(' + ($data) + ') ) {';
61663
+ out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || ';
61620
61664
  }
61665
+ out += ' !' + ($regexp) + '.test(' + ($data) + ') ) { ';
61621
61666
  var $$outStack = $$outStack || [];
61622
61667
  $$outStack.push(out);
61623
61668
  out = ''; /* istanbul ignore else */
@@ -75090,8 +75135,105 @@ function requireCosNodejsSdkV5 () {
75090
75135
  var cosNodejsSdkV5Exports = requireCosNodejsSdkV5();
75091
75136
  var COS = /*@__PURE__*/getDefaultExportFromCjs(cosNodejsSdkV5Exports);
75092
75137
 
75138
+ const SIGN_VERSION = '999.0.4';
75139
+ const SIGN_CHARSET = 'AB45STUVWZEFGJ6CH01D237IXYPQRKLMN89';
75140
+ function createHeyboxOpenPlatformSignParams(pathname, options = {}) {
75141
+ const now = options.now ?? new Date();
75142
+ const time = Math.trunc(now.getTime() / 1000);
75143
+ const nonce = options.nonce ?? md5(`${time}${Date.now()}${node_crypto.randomBytes(16).toString('hex')}`).toUpperCase();
75144
+ return {
75145
+ version: SIGN_VERSION,
75146
+ hkey: generateSignature(pathname, time + 1, nonce),
75147
+ _time: time,
75148
+ nonce,
75149
+ };
75150
+ }
75151
+ function generateSignature(path, time, nonce) {
75152
+ const normalizedPath = `/${path
75153
+ .split('/')
75154
+ .filter(Boolean)
75155
+ .join('/')}/`;
75156
+ const transformedTime = transformWithOffset(String(time), SIGN_CHARSET, -2);
75157
+ const transformedPath = transform(normalizedPath, SIGN_CHARSET);
75158
+ const transformedNonce = transform(nonce, SIGN_CHARSET);
75159
+ const combined = interleave([transformedTime, transformedPath, transformedNonce]).slice(0, 20);
75160
+ const hash = md5(combined);
75161
+ let sign = `${mixColumns(hash.slice(-6).split('').map((char) => char.charCodeAt(0))).reduce((sum, value) => sum + value, 0) % 100}`;
75162
+ sign = sign.length < 2 ? `0${sign}` : sign;
75163
+ return `${transformWithOffset(hash.substring(0, 5), SIGN_CHARSET, -4)}${sign}`;
75164
+ }
75165
+ function transformWithOffset(str, charset, offset) {
75166
+ return transform(str, charset.slice(0, offset));
75167
+ }
75168
+ function transform(str, charset) {
75169
+ let output = '';
75170
+ for (let index = 0; index < str.length; index += 1) {
75171
+ output += charset[str.charCodeAt(index) % charset.length];
75172
+ }
75173
+ return output;
75174
+ }
75175
+ function interleave(strings) {
75176
+ let output = '';
75177
+ const maxLength = Math.max(...strings.map((str) => str.length));
75178
+ for (let index = 0; index < maxLength; index += 1) {
75179
+ for (const str of strings) {
75180
+ if (index < str.length) {
75181
+ output += str[index];
75182
+ }
75183
+ }
75184
+ }
75185
+ return output;
75186
+ }
75187
+ function mixColumns(column) {
75188
+ const output = [...column];
75189
+ output[0] = multiplyE(column[0]) ^ multiply4(column[1]) ^ multiply3(column[2]) ^ multiply2(column[3]);
75190
+ output[1] = multiply2(column[0]) ^ multiplyE(column[1]) ^ multiply4(column[2]) ^ multiply3(column[3]);
75191
+ output[2] = multiply3(column[0]) ^ multiply2(column[1]) ^ multiplyE(column[2]) ^ multiply4(column[3]);
75192
+ output[3] = multiply4(column[0]) ^ multiply3(column[1]) ^ multiply2(column[2]) ^ multiplyE(column[3]);
75193
+ return output;
75194
+ }
75195
+ function multiply1(value) {
75196
+ if (value & 0x80) {
75197
+ return ((value << 1) ^ 0x1b) & 0xff;
75198
+ }
75199
+ return value << 1;
75200
+ }
75201
+ function multiply2(value) {
75202
+ return multiply1(value) ^ value;
75203
+ }
75204
+ function multiply3(value) {
75205
+ return multiply2(multiply1(value));
75206
+ }
75207
+ function multiply4(value) {
75208
+ return multiply3(multiply2(multiply1(value)));
75209
+ }
75210
+ function multiplyE(value) {
75211
+ return multiply4(value) ^ multiply3(value) ^ multiply2(value);
75212
+ }
75213
+ function md5(input) {
75214
+ return node_crypto.createHash('md5').update(input).digest('hex');
75215
+ }
75216
+
75217
+ const DEFAULT_PLATFORM_PARAMS = {
75218
+ os_type: 'web',
75219
+ app: 'heybox',
75220
+ x_client_type: 'web',
75221
+ x_os_type: 'Mac',
75222
+ x_app: 'heybox',
75223
+ x_client_version: '999.999.999',
75224
+ };
75093
75225
  const HEYBOX_API_BASE_URL = 'https://api.xiaoheihe.cn';
75094
75226
  const HEYBOX_WEB_REFERER = 'https://www.xiaoheihe.cn/';
75227
+ function resolveHeyboxId(session) {
75228
+ if (session.heyboxId.trim()) {
75229
+ return session.heyboxId.trim();
75230
+ }
75231
+ return session.cookieHeader
75232
+ .split(';')
75233
+ .map((part) => part.trim())
75234
+ .find((part) => part.startsWith('heybox_id='))
75235
+ ?.slice('heybox_id='.length);
75236
+ }
75095
75237
  function createHeyboxAuthHeaders(session, options = {}) {
75096
75238
  return {
75097
75239
  Cookie: session.cookieHeader,
@@ -75099,6 +75241,94 @@ function createHeyboxAuthHeaders(session, options = {}) {
75099
75241
  ...(options.contentType ? { 'Content-Type': options.contentType } : {}),
75100
75242
  };
75101
75243
  }
75244
+ function createHeyboxRequestContext(session, options = {}) {
75245
+ const heyboxId = resolveHeyboxId(session);
75246
+ return {
75247
+ baseUrl: HEYBOX_API_BASE_URL,
75248
+ headers: createHeyboxAuthHeaders(session, options),
75249
+ platformParams: {
75250
+ ...DEFAULT_PLATFORM_PARAMS,
75251
+ ...options.platformParams,
75252
+ ...(heyboxId ? { heybox_id: heyboxId } : {}),
75253
+ },
75254
+ };
75255
+ }
75256
+ function createHeyboxOpenPlatformRequestContext(session, pathWithQuery, options = {}) {
75257
+ return createHeyboxRequestContext(session, {
75258
+ ...options,
75259
+ platformParams: {
75260
+ x_app: 'heybox_website',
75261
+ ...createHeyboxOpenPlatformSignParams(getApiPathname(pathWithQuery)),
75262
+ ...options.platformParams,
75263
+ },
75264
+ });
75265
+ }
75266
+ function createHeyboxApiUrl(baseUrl, pathWithQuery, params) {
75267
+ const url = new URL(pathWithQuery, baseUrl);
75268
+ for (const [key, value] of Object.entries(params)) {
75269
+ if (value !== '') {
75270
+ url.searchParams.set(key, String(value));
75271
+ }
75272
+ }
75273
+ return url.toString();
75274
+ }
75275
+ function getApiPathname(pathWithQuery) {
75276
+ return new URL(pathWithQuery, HEYBOX_API_BASE_URL).pathname;
75277
+ }
75278
+ const ENVELOPE_BODY_PREVIEW_LENGTH = 1000;
75279
+ async function readHeyboxApiEnvelope(response, options) {
75280
+ const rawBody = await response.text();
75281
+ let envelope = null;
75282
+ let parseError = null;
75283
+ if (rawBody) {
75284
+ try {
75285
+ envelope = JSON.parse(rawBody);
75286
+ }
75287
+ catch (error) {
75288
+ parseError = error;
75289
+ }
75290
+ }
75291
+ const envelopeOk = envelope?.status === 'ok' && (!options.requireResult || envelope?.result !== undefined);
75292
+ if (response.ok && envelopeOk) {
75293
+ return envelope.result;
75294
+ }
75295
+ throw new Error(formatHeyboxEnvelopeError({ response, envelope, parseError, rawBody, pathWithQuery: options.pathWithQuery }));
75296
+ }
75297
+ function formatHeyboxEnvelopeError(input) {
75298
+ const parts = [`Heybox API ${input.pathWithQuery} failed`, `HTTP ${input.response.status}`];
75299
+ if (input.envelope) {
75300
+ const msg = typeof input.envelope.msg === 'string' ? input.envelope.msg.trim() : '';
75301
+ parts.push(`msg=${msg || '(empty)'}`);
75302
+ if (typeof input.envelope.status === 'string') {
75303
+ parts.push(`envelope.status=${input.envelope.status}`);
75304
+ }
75305
+ const { status: _s, msg: _m, result: _r, ...extras } = input.envelope;
75306
+ if (Object.keys(extras).length > 0) {
75307
+ parts.push(`extras=${truncate(safeJsonStringify(extras), ENVELOPE_BODY_PREVIEW_LENGTH)}`);
75308
+ }
75309
+ if (input.envelope.result !== undefined) {
75310
+ parts.push(`result=${truncate(safeJsonStringify(input.envelope.result), ENVELOPE_BODY_PREVIEW_LENGTH)}`);
75311
+ }
75312
+ return parts.join(' ');
75313
+ }
75314
+ if (input.parseError) {
75315
+ const parseMsg = input.parseError instanceof Error ? input.parseError.message : String(input.parseError);
75316
+ parts.push(`parseError=${parseMsg}`);
75317
+ }
75318
+ parts.push(input.rawBody ? `body=${truncate(input.rawBody, ENVELOPE_BODY_PREVIEW_LENGTH)}` : 'body=(empty)');
75319
+ return parts.join(' ');
75320
+ }
75321
+ function truncate(value, max) {
75322
+ return value.length > max ? `${value.slice(0, max)}...(+${value.length - max} chars)` : value;
75323
+ }
75324
+ function safeJsonStringify(value) {
75325
+ try {
75326
+ return JSON.stringify(value);
75327
+ }
75328
+ catch {
75329
+ return String(value);
75330
+ }
75331
+ }
75102
75332
 
75103
75333
  async function getCDNUploadInfo(options, runtime = {}) {
75104
75334
  const body = new URLSearchParams();
@@ -75110,7 +75340,7 @@ async function getCDNUploadInfo(options, runtime = {}) {
75110
75340
  async function getCDNUploadToken(options, runtime = {}) {
75111
75341
  const body = new URLSearchParams();
75112
75342
  body.set('bucket', options.bucket);
75113
- body.set('path', options.path);
75343
+ body.set('keys', JSON.stringify(options.keys));
75114
75344
  body.set('mimetypes', JSON.stringify(options.mimetypes));
75115
75345
  body.set('is_multipart_upload', String(options.isMultipartUpload));
75116
75346
  return postHeyboxApi('/bbs/app/api/qcloud/cos/upload/token/v2', body, options.session, runtime);
@@ -75123,17 +75353,14 @@ async function postCDNUploadCallback(options, runtime = {}) {
75123
75353
  }
75124
75354
  async function postHeyboxApi(pathWithQuery, body, session, runtime) {
75125
75355
  const fetchImpl = runtime.fetchImpl ?? fetch;
75126
- const baseUrl = runtime.baseUrl ?? HEYBOX_API_BASE_URL;
75127
- const response = await fetchImpl(`${baseUrl}${pathWithQuery}`, {
75356
+ const context = createHeyboxRequestContext(session, { contentType: 'application/x-www-form-urlencoded' });
75357
+ const response = await fetchImpl(createHeyboxApiUrl(runtime.baseUrl ?? context.baseUrl, pathWithQuery, context.platformParams), {
75128
75358
  method: 'POST',
75129
- headers: createHeyboxAuthHeaders(session, { contentType: 'application/x-www-form-urlencoded' }),
75359
+ headers: context.headers,
75130
75360
  body: body.toString(),
75131
75361
  });
75132
- const envelope = (await response.json());
75133
- if (envelope.status !== 'ok' || envelope.result === undefined) {
75134
- throw new Error(envelope.msg || `Heybox API ${pathWithQuery} returned status ${envelope.status}`);
75135
- }
75136
- return envelope.result;
75362
+ const result = await readHeyboxApiEnvelope(response, { pathWithQuery, requireResult: true });
75363
+ return result;
75137
75364
  }
75138
75365
 
75139
75366
  const DEFAULT_CONCURRENCY = 4;
@@ -75146,8 +75373,6 @@ async function runUpload(options, runtime = {}) {
75146
75373
  const getCDNUploadToken$1 = runtime.getCDNUploadToken ?? getCDNUploadToken;
75147
75374
  const postCDNUploadCallback$1 = runtime.postCDNUploadCallback ?? postCDNUploadCallback;
75148
75375
  const createReadStream = runtime.createReadStream ?? fs$4.createReadStream;
75149
- const createCosClient = runtime.createCosClient ??
75150
- (({ session }) => createDefaultCosClient(session, { baseUrl: runtime.baseUrl, fetchImpl, getCDNUploadToken: getCDNUploadToken$1 }));
75151
75376
  const orderedFiles = [...options.files].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
75152
75377
  const fileInfos = orderedFiles.map((file) => ({
75153
75378
  name: file.relativePath.split('/').pop() ?? file.relativePath,
@@ -75161,8 +75386,16 @@ async function runUpload(options, runtime = {}) {
75161
75386
  }));
75162
75387
  log(`[hb-sdk] Uploading ${orderedFiles.length} files (${concurrency} concurrent) ...`);
75163
75388
  const uploadInfo = await getCDNUploadInfo$1({ session: options.session, scope: MINIAPP_UPLOAD_SCOPE, fileInfos, needCache: false }, { baseUrl: runtime.baseUrl, fetchImpl });
75164
- const cos = createCosClient({ session: options.session });
75165
75389
  const indexedFiles = orderedFiles.map((file, index) => ({ file, key: uploadInfo.keys[index] }));
75390
+ const cos = runtime.createCosClient
75391
+ ? runtime.createCosClient({ session: options.session })
75392
+ : createDefaultCosClient(await getCDNUploadToken$1({
75393
+ session: options.session,
75394
+ bucket: uploadInfo.bucket,
75395
+ keys: indexedFiles.map(({ key }) => key),
75396
+ mimetypes: indexedFiles.map(({ file }) => file.mimeType),
75397
+ isMultipartUpload: 0,
75398
+ }, { baseUrl: runtime.baseUrl, fetchImpl }));
75166
75399
  const queue = indexedFiles.slice();
75167
75400
  let completed = 0;
75168
75401
  const workers = Array.from({ length: Math.min(concurrency, indexedFiles.length) }, async () => {
@@ -75185,7 +75418,7 @@ async function runUpload(options, runtime = {}) {
75185
75418
  }
75186
75419
  catch (error) {
75187
75420
  completed += 1;
75188
- log(`[hb-sdk] [${String(completed).padStart(2)}/${indexedFiles.length}] error ${task.file.relativePath} -> ${readErrorMessage$1(error)}`);
75421
+ log(`[hb-sdk] [${String(completed).padStart(2)}/${indexedFiles.length}] error ${task.file.relativePath} -> ${readErrorMessage$2(error)}`);
75189
75422
  throw error;
75190
75423
  }
75191
75424
  }
@@ -75194,28 +75427,15 @@ async function runUpload(options, runtime = {}) {
75194
75427
  await postCDNUploadCallback$1({ session: options.session, keys: uploadInfo.keys, isFinished: true }, { baseUrl: runtime.baseUrl, fetchImpl });
75195
75428
  log(`[hb-sdk] Upload finished: ${indexedFiles.length} files.`);
75196
75429
  }
75197
- function createDefaultCosClient(session, options) {
75430
+ function createDefaultCosClient(uploadToken) {
75198
75431
  const cos = new COS({
75199
- getAuthorization(input, callback) {
75200
- options
75201
- .getCDNUploadToken({
75202
- session,
75203
- bucket: input.Bucket ?? '',
75204
- path: input.Key ?? '',
75205
- mimetypes: [],
75206
- isMultipartUpload: 0,
75207
- }, { baseUrl: options.baseUrl, fetchImpl: options.fetchImpl })
75208
- .then((token) => {
75209
- callback({
75210
- TmpSecretId: token.credentials.tmpSecretId,
75211
- TmpSecretKey: token.credentials.tmpSecretKey,
75212
- XCosSecurityToken: token.credentials.sessionToken,
75213
- StartTime: token.startTime,
75214
- ExpiredTime: token.expiredTime,
75215
- });
75216
- })
75217
- .catch((error) => {
75218
- callback({ error });
75432
+ getAuthorization(_input, callback) {
75433
+ callback({
75434
+ TmpSecretId: uploadToken.credentials.tmpSecretId,
75435
+ TmpSecretKey: uploadToken.credentials.tmpSecretKey,
75436
+ XCosSecurityToken: uploadToken.credentials.sessionToken,
75437
+ StartTime: uploadToken.startTime,
75438
+ ExpiredTime: uploadToken.expiredTime,
75219
75439
  });
75220
75440
  },
75221
75441
  });
@@ -75224,7 +75444,7 @@ function createDefaultCosClient(session, options) {
75224
75444
  return new Promise((resolve, reject) => {
75225
75445
  cos.putObject(params, (err, result) => {
75226
75446
  if (err) {
75227
- reject(err instanceof Error ? err : new Error(String(err)));
75447
+ reject(err instanceof Error ? err : new Error(readErrorMessage$2(err)));
75228
75448
  return;
75229
75449
  }
75230
75450
  resolve(result);
@@ -75242,26 +75462,51 @@ function formatSize(bytes) {
75242
75462
  }
75243
75463
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
75244
75464
  }
75245
- function readErrorMessage$1(error) {
75246
- return error instanceof Error ? error.message : String(error);
75465
+ function readErrorMessage$2(error) {
75466
+ if (error instanceof Error && error.message) {
75467
+ return error.message;
75468
+ }
75469
+ if (isRecord$1(error)) {
75470
+ const primary = readStringField(error, ['message', 'Message', 'errorMessage', 'msg']);
75471
+ const code = readStringField(error, ['code', 'Code', 'errorCode', 'name']);
75472
+ const statusCode = readStringField(error, ['statusCode']);
75473
+ return [code, statusCode, primary].filter(Boolean).join(' ') || JSON.stringify(error);
75474
+ }
75475
+ return String(error);
75476
+ }
75477
+ function readStringField(record, fields) {
75478
+ for (const field of fields) {
75479
+ const value = record[field];
75480
+ if (typeof value === 'string' && value.trim()) {
75481
+ return value.trim();
75482
+ }
75483
+ if (typeof value === 'number') {
75484
+ return String(value);
75485
+ }
75486
+ }
75487
+ return '';
75488
+ }
75489
+ function isRecord$1(value) {
75490
+ return Object.prototype.toString.call(value) === '[object Object]';
75247
75491
  }
75248
75492
 
75249
75493
  async function publishUserMiniprogram(options, runtime = {}) {
75250
75494
  const fetchImpl = runtime.fetchImpl ?? fetch;
75251
- const baseUrl = runtime.baseUrl ?? HEYBOX_API_BASE_URL;
75495
+ const context = createHeyboxOpenPlatformRequestContext(options.session, PUBLISH_USER_MINIPROGRAM_API_PATH, {
75496
+ contentType: 'application/x-www-form-urlencoded',
75497
+ });
75252
75498
  const body = new URLSearchParams();
75253
75499
  body.set('mini_program_id', options.miniProgramId);
75254
75500
  body.set('manifest', JSON.stringify(options.manifest));
75255
- const response = await fetchImpl(`${baseUrl}${PUBLISH_USER_MINIPROGRAM_API_PATH}`, {
75501
+ const response = await fetchImpl(createHeyboxApiUrl(runtime.baseUrl ?? context.baseUrl, PUBLISH_USER_MINIPROGRAM_API_PATH, context.platformParams), {
75256
75502
  method: 'POST',
75257
- headers: createHeyboxAuthHeaders(options.session, { contentType: 'application/x-www-form-urlencoded' }),
75503
+ headers: context.headers,
75258
75504
  body: body.toString(),
75259
75505
  });
75260
- const envelope = (await response.json());
75261
- if (envelope.status !== 'ok') {
75262
- throw new Error(envelope.msg || 'publish API returned non-ok status');
75263
- }
75264
- return envelope.result ?? {};
75506
+ const result = await readHeyboxApiEnvelope(response, {
75507
+ pathWithQuery: PUBLISH_USER_MINIPROGRAM_API_PATH,
75508
+ });
75509
+ return result ?? {};
75265
75510
  }
75266
75511
 
75267
75512
  const SUPPORTED_PACKAGE_MANAGERS = [
@@ -75314,14 +75559,11 @@ async function runDeployCommand(options, runtime = {}) {
75314
75559
  if (!fs$4.existsSync(entryHtmlPath)) {
75315
75560
  throw new Error('未找到 dist/index.html,dist 目录残缺,请重新 build');
75316
75561
  }
75317
- const { manifest, hadBom } = await readManifest(manifestPath);
75562
+ const { manifest, hadBom } = parseMiniappManifestJson(await fs$3.readFile(manifestPath, 'utf8'), 'dist/manifest.json');
75318
75563
  if (hadBom) {
75319
75564
  output.log('[hb-sdk] dist/manifest.json 包含 BOM,已自动剥离');
75320
75565
  }
75321
- const version = manifest.version;
75322
- if (typeof version !== 'string' || !isValidVersion(version)) {
75323
- throw new Error(`manifest.version 格式错误,必须是 x.y.z:${String(version)}`);
75324
- }
75566
+ const version = validateMiniappManifestForDeploy(manifest);
75325
75567
  const allFiles = await walkDistFiles(distDir);
75326
75568
  const blocked = allFiles.find((entry) => relativePathContainsNodeModulesSegment(entry.relativePath));
75327
75569
  if (blocked) {
@@ -75332,8 +75574,14 @@ async function runDeployCommand(options, runtime = {}) {
75332
75574
  if (pathError) {
75333
75575
  throw new Error(pathError);
75334
75576
  }
75335
- await (runtime.runUpload ?? runUpload)({ session, miniProgramId, version, files: uploadFiles }, { console: output, fetchImpl: runtime.fetchImpl });
75336
- const publishResult = await (runtime.publishUserMiniprogram ?? publishUserMiniprogram)({ session, miniProgramId, manifest }, { fetchImpl: runtime.fetchImpl });
75577
+ let publishResult;
75578
+ try {
75579
+ await (runtime.runUpload ?? runUpload)({ session, miniProgramId, version, files: uploadFiles }, { console: output, fetchImpl: runtime.fetchImpl });
75580
+ publishResult = await (runtime.publishUserMiniprogram ?? publishUserMiniprogram)({ session, miniProgramId, manifest }, { fetchImpl: runtime.fetchImpl });
75581
+ }
75582
+ catch (error) {
75583
+ throw translateHeyboxDeployError(error, { projectRoot, version });
75584
+ }
75337
75585
  output.log(`[hb-sdk] Publish ok: ${miniProgramId} ${version}`);
75338
75586
  if (publishResult.publish_url) {
75339
75587
  output.log(`[hb-sdk] Publish URL: ${publishResult.publish_url}`);
@@ -75381,17 +75629,6 @@ async function runBuildScript(pm, cwd, spawnImpl) {
75381
75629
  });
75382
75630
  });
75383
75631
  }
75384
- async function readManifest(manifestPath) {
75385
- const raw = await fs$3.readFile(manifestPath, 'utf8');
75386
- const hadBom = raw.charCodeAt(0) === 0xfeff;
75387
- const text = hadBom ? raw.slice(1) : raw;
75388
- try {
75389
- return { manifest: JSON.parse(text), hadBom };
75390
- }
75391
- catch (error) {
75392
- throw new Error(`dist/manifest.json 不是合法 JSON:${error instanceof Error ? error.message : String(error)}`);
75393
- }
75394
- }
75395
75632
  async function walkDistFiles(distDir) {
75396
75633
  const results = [];
75397
75634
  await walk(distDir, '');
@@ -75425,6 +75662,39 @@ async function walkDistFiles(distDir) {
75425
75662
  function inferMimeType(fileName) {
75426
75663
  return MIME_BY_EXT[path.extname(fileName).toLowerCase()] || 'application/octet-stream';
75427
75664
  }
75665
+ function translateHeyboxDeployError(error, options) {
75666
+ const message = error instanceof Error && error.message ? error.message : String(error);
75667
+ if (isDuplicateUploadErrorMessage(message)) {
75668
+ const packageJsonPath = path.join(options.projectRoot, 'package.json');
75669
+ const nextVersion = suggestNextPatchVersion(options.version);
75670
+ return new Error([
75671
+ `当前小程序版本 ${options.version} 的上传文件已存在,不能覆盖同版本发布产物。`,
75672
+ `请升级 ${packageJsonPath} 中的 version${nextVersion ? `(例如 ${nextVersion})` : ''},重新 build 后再 deploy。`,
75673
+ `原始错误:${message}`,
75674
+ ].join('\n'));
75675
+ }
75676
+ if (isAuthExpiredErrorMessage(message)) {
75677
+ return new Error([
75678
+ 'Heybox 登录态已失效(服务端 session 已过期或被清除)。',
75679
+ '请运行 `hb-sdk login` 重新登录后再执行 deploy。',
75680
+ `原始错误:${message}`,
75681
+ ].join('\n'));
75682
+ }
75683
+ return error instanceof Error ? error : new Error(message);
75684
+ }
75685
+ function isDuplicateUploadErrorMessage(message) {
75686
+ return /重名|duplicate|already exists|exists/i.test(message);
75687
+ }
75688
+ function isAuthExpiredErrorMessage(message) {
75689
+ return /请重新登录|未登录|登录已失效|登录态已失效|unauthorized|未授权/i.test(message);
75690
+ }
75691
+ function suggestNextPatchVersion(version) {
75692
+ const matched = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
75693
+ if (!matched) {
75694
+ return '';
75695
+ }
75696
+ return `${matched[1]}.${matched[2]}.${Number(matched[3]) + 1}`;
75697
+ }
75428
75698
 
75429
75699
  class Locked extends Error {
75430
75700
  constructor(port) {
@@ -75442,11 +75712,6 @@ const lockedPorts = {
75442
75712
  // and a new young set for locked ports are created.
75443
75713
  const releaseOldLockedPortsIntervalMs = 1000 * 15;
75444
75714
 
75445
- // Keep `reserve` deliberately process-wide by port number.
75446
- // It is meant to avoid in-process races, not to model every possible
75447
- // IPv4/IPv6 or host-specific bind combination.
75448
- const reservedPorts = new Set();
75449
-
75450
75715
  // Lazily create timeout on first use
75451
75716
  let timeout;
75452
75717
 
@@ -75498,8 +75763,6 @@ const getAvailablePort = async (options, hosts) => {
75498
75763
  return options.port;
75499
75764
  };
75500
75765
 
75501
- const isLockedPort = port => lockedPorts.old.has(port) || lockedPorts.young.has(port) || reservedPorts.has(port);
75502
-
75503
75766
  const portCheckSequence = function * (ports) {
75504
75767
  if (ports) {
75505
75768
  yield * ports;
@@ -75538,8 +75801,6 @@ async function getPorts(options) {
75538
75801
  }
75539
75802
  }
75540
75803
 
75541
- const {reserve, ...netOptions} = options ?? {};
75542
-
75543
75804
  if (timeout === undefined) {
75544
75805
  timeout = setTimeout(() => {
75545
75806
  timeout = undefined;
@@ -75562,20 +75823,16 @@ async function getPorts(options) {
75562
75823
  continue;
75563
75824
  }
75564
75825
 
75565
- let availablePort = await getAvailablePort({...netOptions, port}, hosts); // eslint-disable-line no-await-in-loop
75566
- while (isLockedPort(availablePort)) {
75826
+ let availablePort = await getAvailablePort({...options, port}, hosts); // eslint-disable-line no-await-in-loop
75827
+ while (lockedPorts.old.has(availablePort) || lockedPorts.young.has(availablePort)) {
75567
75828
  if (port !== 0) {
75568
75829
  throw new Locked(port);
75569
75830
  }
75570
75831
 
75571
- availablePort = await getAvailablePort({...netOptions, port}, hosts); // eslint-disable-line no-await-in-loop
75832
+ availablePort = await getAvailablePort({...options, port}, hosts); // eslint-disable-line no-await-in-loop
75572
75833
  }
75573
75834
 
75574
- if (reserve) {
75575
- reservedPorts.add(availablePort);
75576
- } else {
75577
- lockedPorts.young.add(availablePort);
75578
- }
75835
+ lockedPorts.young.add(availablePort);
75579
75836
 
75580
75837
  return availablePort;
75581
75838
  } catch (error) {
@@ -75588,6 +75845,337 @@ async function getPorts(options) {
75588
75845
  throw new Error('No available ports found');
75589
75846
  }
75590
75847
 
75848
+ const MINI_PROGRAM_URL_QUERY_PARAM = 'mini_url';
75849
+ const MOCK_NETWORK_PROXY_PATH = '/__hb_sdk_mock_network__';
75850
+ const MOCK_NETWORK_PROXY_BODY_LIMIT = 1024 * 1024;
75851
+ const MOCK_HOST_ROOT_CANDIDATES = [
75852
+ path.resolve(__dirname, 'devtools/mock-host'),
75853
+ path.resolve(__dirname, '../devtools/mock-host'),
75854
+ path.resolve(__dirname, '../../devtools/mock-host'),
75855
+ path.resolve(__dirname, '../../../devtools/mock-host'),
75856
+ ];
75857
+ async function startMiniProgramMockHostServer(options) {
75858
+ const root = options.root ?? resolveMiniProgramMockHostRoot(options.rootCandidates);
75859
+ const server = createMiniProgramMockHostServer(root, {
75860
+ fetchImpl: options.fetchImpl ?? fetch,
75861
+ });
75862
+ const port = await listenHttpServer(server, {
75863
+ host: options.host,
75864
+ port: options.port,
75865
+ }, options.getPort ?? getPorts);
75866
+ return {
75867
+ close: () => new Promise((resolve) => {
75868
+ server.close(() => resolve());
75869
+ }),
75870
+ port,
75871
+ root,
75872
+ server,
75873
+ url: createMiniProgramMockHostUrl({
75874
+ appUrl: options.appUrl,
75875
+ host: options.host,
75876
+ port,
75877
+ }),
75878
+ };
75879
+ }
75880
+ function resolveMiniProgramMockHostRoot(candidates = MOCK_HOST_ROOT_CANDIDATES) {
75881
+ const found = candidates.find((candidate) => fs$4.existsSync(path.join(candidate, 'index.html')));
75882
+ if (!found) {
75883
+ throw new Error('未找到 hb-sdk mock host 静态产物。请先执行 @heybox/hb-sdk 的 build:mock-host。');
75884
+ }
75885
+ return found;
75886
+ }
75887
+ function createMiniProgramMockHostUrl(options) {
75888
+ const url = new URL(`http://${toDisplayHost$1(options.host)}:${options.port}/`);
75889
+ url.searchParams.set(MINI_PROGRAM_URL_QUERY_PARAM, options.appUrl);
75890
+ return url.toString();
75891
+ }
75892
+ function createMiniProgramMockHostServer(root, runtime) {
75893
+ return node_http.createServer(async (request, response) => {
75894
+ const requestUrl = new URL(request.url, 'http://localhost');
75895
+ const pathname = decodeURIComponent(requestUrl.pathname);
75896
+ if (pathname === MOCK_NETWORK_PROXY_PATH) {
75897
+ await handleMockNetworkProxy(request, response, runtime.fetchImpl);
75898
+ return;
75899
+ }
75900
+ const targetPath = resolveStaticPath(root, pathname);
75901
+ if (!targetPath) {
75902
+ response.writeHead(404);
75903
+ response.end('Not found');
75904
+ return;
75905
+ }
75906
+ try {
75907
+ const stat = fs$4.statSync(targetPath);
75908
+ if (stat.isDirectory()) {
75909
+ response.writeHead(404);
75910
+ response.end('Not found');
75911
+ return;
75912
+ }
75913
+ response.writeHead(200, {
75914
+ 'content-type': readContentType(targetPath),
75915
+ 'cache-control': 'no-store',
75916
+ });
75917
+ fs$4.createReadStream(targetPath).pipe(response);
75918
+ }
75919
+ catch {
75920
+ response.writeHead(404);
75921
+ response.end('Not found');
75922
+ }
75923
+ });
75924
+ }
75925
+ async function handleMockNetworkProxy(request, response, fetchImpl) {
75926
+ if (request.method === 'OPTIONS') {
75927
+ writeJsonResponse(response, 204, undefined);
75928
+ return;
75929
+ }
75930
+ if (request.method !== 'POST') {
75931
+ writeJsonResponse(response, 405, {
75932
+ code: 'METHOD_NOT_ALLOWED',
75933
+ message: 'mock network proxy only supports POST',
75934
+ });
75935
+ return;
75936
+ }
75937
+ try {
75938
+ const payload = await readJsonRequestBody(request);
75939
+ const result = await requestMockNetwork(payload, fetchImpl);
75940
+ writeJsonResponse(response, 200, result);
75941
+ }
75942
+ catch (error) {
75943
+ writeJsonResponse(response, readMockNetworkErrorStatus(error), toMockNetworkErrorPayload(error));
75944
+ }
75945
+ }
75946
+ async function requestMockNetwork(payload, fetchImpl) {
75947
+ if (!isRecord(payload) || typeof payload.url !== 'string' || !payload.url.trim()) {
75948
+ throw createMockNetworkError(400, 'INVALID_NETWORK_REQUEST', 'network.request url 必须是非空字符串');
75949
+ }
75950
+ const url = createMockNetworkUrl(payload.url, payload.params);
75951
+ const method = typeof payload.method === 'string' && payload.method.trim() ? payload.method.trim().toUpperCase() : 'GET';
75952
+ const headers = normalizeMockNetworkHeaders(payload.headers);
75953
+ const controller = new AbortController();
75954
+ const timeout = Number(payload.timeout) > 0 ? Number(payload.timeout) : 10000;
75955
+ const timer = setTimeout(() => controller.abort(), timeout);
75956
+ try {
75957
+ const response = await fetchImpl(url.toString(), {
75958
+ method,
75959
+ headers,
75960
+ body: createMockNetworkBody(method, payload.data, headers),
75961
+ redirect: 'follow',
75962
+ signal: controller.signal,
75963
+ });
75964
+ const contentType = response.headers.get('content-type') || '';
75965
+ const text = await response.text();
75966
+ clearTimeout(timer);
75967
+ return {
75968
+ data: contentType.includes('application/json') ? safeJsonParse(text) : text,
75969
+ status: response.status,
75970
+ statusText: response.statusText,
75971
+ headers: Object.fromEntries(response.headers.entries()),
75972
+ };
75973
+ }
75974
+ catch (error) {
75975
+ clearTimeout(timer);
75976
+ throw createMockNetworkError(502, 'MOCK_NETWORK_REQUEST_FAILED', error instanceof Error && error.name === 'AbortError'
75977
+ ? `network.request timeout after ${timeout}ms`
75978
+ : readErrorMessage$1(error));
75979
+ }
75980
+ }
75981
+ function createMockNetworkUrl(input, params) {
75982
+ let url;
75983
+ try {
75984
+ url = new URL(input);
75985
+ }
75986
+ catch {
75987
+ throw createMockNetworkError(400, 'INVALID_NETWORK_REQUEST', 'network.request url 必须是合法 URL');
75988
+ }
75989
+ if (!['http:', 'https:'].includes(url.protocol)) {
75990
+ throw createMockNetworkError(400, 'INVALID_NETWORK_REQUEST', 'network.request 仅支持 HTTP(S) URL');
75991
+ }
75992
+ if (isRecord(params)) {
75993
+ Object.entries(params).forEach(([key, value]) => {
75994
+ if (value !== undefined && value !== null) {
75995
+ url.searchParams.set(key, String(value));
75996
+ }
75997
+ });
75998
+ }
75999
+ return url;
76000
+ }
76001
+ function normalizeMockNetworkHeaders(headers) {
76002
+ const normalized = new Headers();
76003
+ if (!isRecord(headers)) {
76004
+ return normalized;
76005
+ }
76006
+ Object.entries(headers).forEach(([key, value]) => {
76007
+ if (typeof value === 'string' && !isBlockedMockNetworkHeader(key)) {
76008
+ normalized.set(key, value);
76009
+ }
76010
+ });
76011
+ return normalized;
76012
+ }
76013
+ function isBlockedMockNetworkHeader(key) {
76014
+ return [
76015
+ 'connection',
76016
+ 'content-length',
76017
+ 'host',
76018
+ 'keep-alive',
76019
+ 'proxy-authenticate',
76020
+ 'proxy-authorization',
76021
+ 'te',
76022
+ 'trailer',
76023
+ 'transfer-encoding',
76024
+ 'upgrade',
76025
+ ].includes(key.toLowerCase());
76026
+ }
76027
+ function createMockNetworkBody(method, data, headers) {
76028
+ if (data === undefined || method === 'GET' || method === 'HEAD') {
76029
+ return undefined;
76030
+ }
76031
+ if (typeof data === 'string') {
76032
+ return data;
76033
+ }
76034
+ if (!headers.has('content-type')) {
76035
+ headers.set('content-type', 'application/json');
76036
+ }
76037
+ return JSON.stringify(data);
76038
+ }
76039
+ function readJsonRequestBody(request) {
76040
+ return new Promise((resolve, reject) => {
76041
+ const chunks = [];
76042
+ let size = 0;
76043
+ request.on('data', (chunk) => {
76044
+ const buffer = Buffer.from(chunk);
76045
+ size += buffer.byteLength;
76046
+ if (size > MOCK_NETWORK_PROXY_BODY_LIMIT) {
76047
+ reject(createMockNetworkError(413, 'REQUEST_ENTITY_TOO_LARGE', 'mock network proxy request body too large'));
76048
+ request.destroy();
76049
+ return;
76050
+ }
76051
+ chunks.push(buffer);
76052
+ });
76053
+ request.on('error', reject);
76054
+ request.on('aborted', () => {
76055
+ reject('mock network proxy request aborted');
76056
+ });
76057
+ request.on('end', () => {
76058
+ const rawBody = Buffer.concat(chunks).toString('utf8');
76059
+ if (!rawBody.trim()) {
76060
+ resolve({});
76061
+ return;
76062
+ }
76063
+ try {
76064
+ resolve(JSON.parse(rawBody));
76065
+ }
76066
+ catch {
76067
+ reject(createMockNetworkError(400, 'INVALID_JSON', 'mock network proxy received invalid JSON'));
76068
+ }
76069
+ });
76070
+ });
76071
+ }
76072
+ function writeJsonResponse(response, status, body) {
76073
+ response.writeHead(status, {
76074
+ 'content-type': 'application/json; charset=utf-8',
76075
+ 'cache-control': 'no-store',
76076
+ });
76077
+ response.end(body === undefined ? '' : JSON.stringify(body));
76078
+ }
76079
+ function safeJsonParse(text) {
76080
+ try {
76081
+ return JSON.parse(text);
76082
+ }
76083
+ catch {
76084
+ return text;
76085
+ }
76086
+ }
76087
+ function isRecord(value) {
76088
+ return Object.prototype.toString.call(value) === '[object Object]';
76089
+ }
76090
+ function createMockNetworkError(status, code, message) {
76091
+ const error = new Error(message);
76092
+ error.code = code;
76093
+ error.status = status;
76094
+ return error;
76095
+ }
76096
+ function readMockNetworkErrorStatus(error) {
76097
+ return isObjectLike(error) && typeof error.status === 'number' ? error.status : 500;
76098
+ }
76099
+ function toMockNetworkErrorPayload(error) {
76100
+ if (isObjectLike(error) && typeof error.code === 'string' && typeof error.message === 'string') {
76101
+ return {
76102
+ code: error.code,
76103
+ message: error.message,
76104
+ };
76105
+ }
76106
+ return {
76107
+ code: 'MOCK_NETWORK_ERROR',
76108
+ message: readErrorMessage$1(error),
76109
+ };
76110
+ }
76111
+ function isObjectLike(value) {
76112
+ return (typeof value === 'object' || typeof value === 'function') && value !== null;
76113
+ }
76114
+ function resolveStaticPath(root, pathname) {
76115
+ const safePathname = pathname === '/' ? '/index.html' : pathname;
76116
+ const normalizedRoot = path.normalize(root);
76117
+ const targetPath = path.normalize(path.join(normalizedRoot, safePathname));
76118
+ if (targetPath !== normalizedRoot && !targetPath.startsWith(`${normalizedRoot}${path.sep}`)) {
76119
+ return undefined;
76120
+ }
76121
+ return targetPath;
76122
+ }
76123
+ function readContentType(filePath) {
76124
+ if (filePath.endsWith('.html')) {
76125
+ return 'text/html; charset=utf-8';
76126
+ }
76127
+ if (filePath.endsWith('.js')) {
76128
+ return 'text/javascript; charset=utf-8';
76129
+ }
76130
+ if (filePath.endsWith('.css')) {
76131
+ return 'text/css; charset=utf-8';
76132
+ }
76133
+ if (filePath.endsWith('.json')) {
76134
+ return 'application/json; charset=utf-8';
76135
+ }
76136
+ if (filePath.endsWith('.svg')) {
76137
+ return 'image/svg+xml';
76138
+ }
76139
+ return 'application/octet-stream';
76140
+ }
76141
+ async function listenHttpServer(server, options, getPortImpl) {
76142
+ const maxAttempts = 20;
76143
+ const port = await getPortImpl({
76144
+ host: options.host,
76145
+ port: createPortCandidates(options.port, maxAttempts),
76146
+ });
76147
+ if (port < options.port || port >= options.port + maxAttempts) {
76148
+ throw new Error(`无法找到可用 mock host 端口,起始端口: ${options.port}`);
76149
+ }
76150
+ await new Promise((resolve, reject) => {
76151
+ const onError = (error) => {
76152
+ server.off('listening', onListening);
76153
+ reject(error);
76154
+ };
76155
+ const onListening = () => {
76156
+ server.off('error', onError);
76157
+ resolve();
76158
+ };
76159
+ server.once('error', onError);
76160
+ server.once('listening', onListening);
76161
+ server.listen(port, options.host);
76162
+ });
76163
+ const address = server.address();
76164
+ return address.port;
76165
+ }
76166
+ function createPortCandidates(startPort, count) {
76167
+ return Array.from({ length: count }, (_, index) => startPort + index);
76168
+ }
76169
+ function toDisplayHost$1(host) {
76170
+ if (!host || host === '0.0.0.0' || host === '::') {
76171
+ return 'localhost';
76172
+ }
76173
+ return host;
76174
+ }
76175
+ function readErrorMessage$1(error) {
76176
+ return error instanceof Error ? error.message : String(error);
76177
+ }
76178
+
75591
76179
  let isDockerCached;
75592
76180
 
75593
76181
  function hasDockerEnv() {
@@ -75651,20 +76239,11 @@ const isWsl = () => {
75651
76239
  }
75652
76240
 
75653
76241
  try {
75654
- if (fs$4.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')) {
75655
- return !isInsideContainer();
75656
- }
75657
- } catch {}
75658
-
75659
- // Fallback for custom kernels: check WSL-specific paths.
75660
- if (
75661
- fs$4.existsSync('/proc/sys/fs/binfmt_misc/WSLInterop')
75662
- || fs$4.existsSync('/run/WSL')
75663
- ) {
75664
- return !isInsideContainer();
76242
+ return fs$4.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')
76243
+ ? !isInsideContainer() : false;
76244
+ } catch {
76245
+ return false;
75665
76246
  }
75666
-
75667
- return false;
75668
76247
  };
75669
76248
 
75670
76249
  var isWsl$1 = process$1.env.__IS_WSL_TEST__ ? isWsl : isWsl();
@@ -76221,9 +76800,6 @@ function readErrorMessage(error) {
76221
76800
 
76222
76801
  const DEFAULT_APP_PORT = 5173;
76223
76802
  const DEFAULT_MOCK_PORT = 5174;
76224
- const MINI_PROGRAM_URL_QUERY_PARAM = 'mini_url';
76225
- const MOCK_NETWORK_PROXY_PATH = '/__hb_sdk_mock_network__';
76226
- const MOCK_NETWORK_PROXY_BODY_LIMIT = 1024 * 1024;
76227
76803
  // Shared with Heybox H5 Vite plugins so hb-sdk can own the launch output.
76228
76804
  const MANAGED_DEV_OUTPUT_ENV = 'HEYBOX_DEV_SERVER_MANAGED_OUTPUT';
76229
76805
  const VITE_LOG_LEVEL = 'warn';
@@ -76237,26 +76813,29 @@ async function runDevCommand(options, runtime = {}) {
76237
76813
  await appServer.listen(options.port ?? DEFAULT_APP_PORT);
76238
76814
  const appUrl = await resolveViteAppUrl(appServer, options, fetchImpl);
76239
76815
  const closers = [() => appServer.close()];
76240
- const mockHostRoot = runtime.mockHostRoot ?? resolveMockHostRoot(runtime.mockHostRootCandidates);
76241
- const mockServer = createMockHostServer(mockHostRoot, { fetchImpl });
76242
- const mockPort = await listenHttpServer(mockServer, {
76243
- host: options.host,
76244
- port: options.mockPort ?? DEFAULT_MOCK_PORT,
76245
- }, runtime.getPort ?? getPorts);
76246
- const mockUrl = createMockHostUrl({
76247
- appUrl,
76248
- host: options.host,
76249
- port: mockPort,
76250
- });
76251
- closers.push(() => new Promise((resolve) => {
76252
- mockServer.close(() => resolve());
76253
- }));
76816
+ let mockHost;
76817
+ try {
76818
+ mockHost = await startMiniProgramMockHostServer({
76819
+ appUrl,
76820
+ fetchImpl,
76821
+ getPort: runtime.getPort ?? getPorts,
76822
+ host: options.host,
76823
+ port: options.mockPort ?? DEFAULT_MOCK_PORT,
76824
+ root: runtime.mockHostRoot,
76825
+ rootCandidates: runtime.mockHostRootCandidates,
76826
+ });
76827
+ }
76828
+ catch (error) {
76829
+ await closeAll(closers);
76830
+ throw error;
76831
+ }
76832
+ closers.push(() => mockHost.close());
76254
76833
  output.log(`\n[hb-sdk] Mock runtime host is ready`);
76255
- output.log(`[hb-sdk] Mock runtime host: ${mockUrl}`);
76834
+ output.log(`[hb-sdk] Mock runtime host: ${mockHost.url}`);
76256
76835
  output.log(`[hb-sdk] Mini program URL: ${appUrl}`);
76257
76836
  output.log(`[hb-sdk] Mac APP: use the button in Mock runtime host`);
76258
76837
  if (options.open !== false) {
76259
- void openUrl(mockUrl);
76838
+ void openUrl(mockHost.url);
76260
76839
  }
76261
76840
  installShutdownHandlers(closers, runtime.process ?? process);
76262
76841
  }
@@ -76276,6 +76855,10 @@ function findProjectRoot(startDir) {
76276
76855
  async function loadProjectVite(projectRoot) {
76277
76856
  const projectRequire = node_module.createRequire(path.join(projectRoot, 'package.json'));
76278
76857
  try {
76858
+ const packageJson = JSON.parse(await fs$3.readFile(path.join(projectRoot, 'package.json'), 'utf8'));
76859
+ if (!hasPackageDependency(packageJson, 'vite')) {
76860
+ throw new Error('package.json 未声明 vite');
76861
+ }
76279
76862
  const viteEntry = projectRequire.resolve('vite');
76280
76863
  return (await import(node_url.pathToFileURL(viteEntry).href));
76281
76864
  }
@@ -76283,6 +76866,12 @@ async function loadProjectVite(projectRoot) {
76283
76866
  throw new Error(`当前项目未安装 vite。请先安装项目依赖,或将 vite 放到项目 devDependencies。原始错误: ${readErrorMessage(error)}`);
76284
76867
  }
76285
76868
  }
76869
+ function hasPackageDependency(packageJson, name) {
76870
+ return Boolean(packageJson.dependencies?.[name] ||
76871
+ packageJson.devDependencies?.[name] ||
76872
+ packageJson.optionalDependencies?.[name] ||
76873
+ packageJson.peerDependencies?.[name]);
76874
+ }
76286
76875
  function createViteServerOptions(projectRoot, options) {
76287
76876
  const configFile = resolveProjectViteConfig(projectRoot);
76288
76877
  const server = {
@@ -76379,299 +76968,6 @@ async function canReachDocumentUrl(url, fetchImpl) {
76379
76968
  function ensureTrailingSlash(value) {
76380
76969
  return value.endsWith('/') ? value : `${value}/`;
76381
76970
  }
76382
- const MOCK_HOST_ROOT_CANDIDATES = [
76383
- path.resolve(__dirname, 'devtools/mock-host'),
76384
- path.resolve(__dirname, '../devtools/mock-host'),
76385
- path.resolve(__dirname, '../../devtools/mock-host'),
76386
- path.resolve(__dirname, '../../../devtools/mock-host'),
76387
- ];
76388
- function resolveMockHostRoot(candidates = MOCK_HOST_ROOT_CANDIDATES) {
76389
- const found = candidates.find((candidate) => fs$4.existsSync(path.join(candidate, 'index.html')));
76390
- if (!found) {
76391
- throw new Error(`未找到 hb-sdk mock host 静态产物。请先执行 @heybox/hb-sdk 的 build:mock-host。`);
76392
- }
76393
- return found;
76394
- }
76395
- function createMockHostServer(root, runtime) {
76396
- return node_http.createServer(async (request, response) => {
76397
- const requestUrl = new URL(request.url, 'http://localhost');
76398
- const pathname = decodeURIComponent(requestUrl.pathname);
76399
- if (pathname === MOCK_NETWORK_PROXY_PATH) {
76400
- await handleMockNetworkProxy(request, response, runtime.fetchImpl);
76401
- return;
76402
- }
76403
- const targetPath = resolveStaticPath(root, pathname);
76404
- if (!targetPath) {
76405
- response.writeHead(404);
76406
- response.end('Not found');
76407
- return;
76408
- }
76409
- try {
76410
- const stat = fs$4.statSync(targetPath);
76411
- if (stat.isDirectory()) {
76412
- response.writeHead(404);
76413
- response.end('Not found');
76414
- return;
76415
- }
76416
- response.writeHead(200, {
76417
- 'content-type': readContentType(targetPath),
76418
- 'cache-control': 'no-store',
76419
- });
76420
- fs$4.createReadStream(targetPath).pipe(response);
76421
- }
76422
- catch {
76423
- response.writeHead(404);
76424
- response.end('Not found');
76425
- }
76426
- });
76427
- }
76428
- async function handleMockNetworkProxy(request, response, fetchImpl) {
76429
- if (request.method === 'OPTIONS') {
76430
- writeJsonResponse(response, 204, undefined);
76431
- return;
76432
- }
76433
- if (request.method !== 'POST') {
76434
- writeJsonResponse(response, 405, {
76435
- code: 'METHOD_NOT_ALLOWED',
76436
- message: 'mock network proxy only supports POST',
76437
- });
76438
- return;
76439
- }
76440
- try {
76441
- const payload = await readJsonRequestBody(request);
76442
- const result = await requestMockNetwork(payload, fetchImpl);
76443
- writeJsonResponse(response, 200, result);
76444
- }
76445
- catch (error) {
76446
- writeJsonResponse(response, readMockNetworkErrorStatus(error), toMockNetworkErrorPayload(error));
76447
- }
76448
- }
76449
- async function requestMockNetwork(payload, fetchImpl) {
76450
- if (!isRecord(payload) || typeof payload.url !== 'string' || !payload.url.trim()) {
76451
- throw createMockNetworkError(400, 'INVALID_NETWORK_REQUEST', 'network.request url 必须是非空字符串');
76452
- }
76453
- const url = createMockNetworkUrl(payload.url, payload.params);
76454
- const method = typeof payload.method === 'string' && payload.method.trim() ? payload.method.trim().toUpperCase() : 'GET';
76455
- const headers = normalizeMockNetworkHeaders(payload.headers);
76456
- const controller = new AbortController();
76457
- const timeout = Number(payload.timeout) > 0 ? Number(payload.timeout) : 10000;
76458
- const timer = setTimeout(() => controller.abort(), timeout);
76459
- try {
76460
- const response = await fetchImpl(url.toString(), {
76461
- method,
76462
- headers,
76463
- body: createMockNetworkBody(method, payload.data, headers),
76464
- redirect: 'follow',
76465
- signal: controller.signal,
76466
- });
76467
- const contentType = response.headers.get('content-type') || '';
76468
- const text = await response.text();
76469
- clearTimeout(timer);
76470
- return {
76471
- data: contentType.includes('application/json') ? safeJsonParse(text) : text,
76472
- status: response.status,
76473
- statusText: response.statusText,
76474
- headers: Object.fromEntries(response.headers.entries()),
76475
- };
76476
- }
76477
- catch (error) {
76478
- clearTimeout(timer);
76479
- throw createMockNetworkError(502, 'MOCK_NETWORK_REQUEST_FAILED', error instanceof Error && error.name === 'AbortError' ? `network.request timeout after ${timeout}ms` : readErrorMessage(error));
76480
- }
76481
- }
76482
- function createMockNetworkUrl(input, params) {
76483
- let url;
76484
- try {
76485
- url = new URL(input);
76486
- }
76487
- catch {
76488
- throw createMockNetworkError(400, 'INVALID_NETWORK_REQUEST', 'network.request url 必须是合法 URL');
76489
- }
76490
- if (!['http:', 'https:'].includes(url.protocol)) {
76491
- throw createMockNetworkError(400, 'INVALID_NETWORK_REQUEST', 'network.request 仅支持 HTTP(S) URL');
76492
- }
76493
- if (isRecord(params)) {
76494
- Object.entries(params).forEach(([key, value]) => {
76495
- if (value !== undefined && value !== null) {
76496
- url.searchParams.set(key, String(value));
76497
- }
76498
- });
76499
- }
76500
- return url;
76501
- }
76502
- function normalizeMockNetworkHeaders(headers) {
76503
- const normalized = new Headers();
76504
- if (!isRecord(headers)) {
76505
- return normalized;
76506
- }
76507
- Object.entries(headers).forEach(([key, value]) => {
76508
- if (typeof value === 'string' && !isBlockedMockNetworkHeader(key)) {
76509
- normalized.set(key, value);
76510
- }
76511
- });
76512
- return normalized;
76513
- }
76514
- function isBlockedMockNetworkHeader(key) {
76515
- return [
76516
- 'connection',
76517
- 'content-length',
76518
- 'host',
76519
- 'keep-alive',
76520
- 'proxy-authenticate',
76521
- 'proxy-authorization',
76522
- 'te',
76523
- 'trailer',
76524
- 'transfer-encoding',
76525
- 'upgrade',
76526
- ].includes(key.toLowerCase());
76527
- }
76528
- function createMockNetworkBody(method, data, headers) {
76529
- if (data === undefined || method === 'GET' || method === 'HEAD') {
76530
- return undefined;
76531
- }
76532
- if (typeof data === 'string') {
76533
- return data;
76534
- }
76535
- if (!headers.has('content-type')) {
76536
- headers.set('content-type', 'application/json');
76537
- }
76538
- return JSON.stringify(data);
76539
- }
76540
- function readJsonRequestBody(request) {
76541
- return new Promise((resolve, reject) => {
76542
- const chunks = [];
76543
- let size = 0;
76544
- request.on('data', (chunk) => {
76545
- const buffer = Buffer.from(chunk);
76546
- size += buffer.byteLength;
76547
- if (size > MOCK_NETWORK_PROXY_BODY_LIMIT) {
76548
- reject(createMockNetworkError(413, 'REQUEST_ENTITY_TOO_LARGE', 'mock network proxy request body too large'));
76549
- request.destroy();
76550
- return;
76551
- }
76552
- chunks.push(buffer);
76553
- });
76554
- request.on('error', reject);
76555
- request.on('aborted', () => {
76556
- reject('mock network proxy request aborted');
76557
- });
76558
- request.on('end', () => {
76559
- const rawBody = Buffer.concat(chunks).toString('utf8');
76560
- if (!rawBody.trim()) {
76561
- resolve({});
76562
- return;
76563
- }
76564
- try {
76565
- resolve(JSON.parse(rawBody));
76566
- }
76567
- catch {
76568
- reject(createMockNetworkError(400, 'INVALID_JSON', 'mock network proxy received invalid JSON'));
76569
- }
76570
- });
76571
- });
76572
- }
76573
- function writeJsonResponse(response, status, body) {
76574
- response.writeHead(status, {
76575
- 'content-type': 'application/json; charset=utf-8',
76576
- 'cache-control': 'no-store',
76577
- });
76578
- response.end(body === undefined ? '' : JSON.stringify(body));
76579
- }
76580
- function safeJsonParse(text) {
76581
- try {
76582
- return JSON.parse(text);
76583
- }
76584
- catch {
76585
- return text;
76586
- }
76587
- }
76588
- function isRecord(value) {
76589
- return Object.prototype.toString.call(value) === '[object Object]';
76590
- }
76591
- function createMockNetworkError(status, code, message) {
76592
- const error = new Error(message);
76593
- error.code = code;
76594
- error.status = status;
76595
- return error;
76596
- }
76597
- function readMockNetworkErrorStatus(error) {
76598
- return isObjectLike(error) && typeof error.status === 'number' ? error.status : 500;
76599
- }
76600
- function toMockNetworkErrorPayload(error) {
76601
- if (isObjectLike(error) && typeof error.code === 'string' && typeof error.message === 'string') {
76602
- return {
76603
- code: error.code,
76604
- message: error.message,
76605
- };
76606
- }
76607
- return {
76608
- code: 'MOCK_NETWORK_ERROR',
76609
- message: readErrorMessage(error),
76610
- };
76611
- }
76612
- function isObjectLike(value) {
76613
- return (typeof value === 'object' || typeof value === 'function') && value !== null;
76614
- }
76615
- function resolveStaticPath(root, pathname) {
76616
- const safePathname = pathname === '/' ? '/index.html' : pathname;
76617
- const normalizedRoot = path.normalize(root);
76618
- const targetPath = path.normalize(path.join(normalizedRoot, safePathname));
76619
- if (targetPath !== normalizedRoot && !targetPath.startsWith(`${normalizedRoot}${path.sep}`)) {
76620
- return undefined;
76621
- }
76622
- return targetPath;
76623
- }
76624
- function readContentType(filePath) {
76625
- if (filePath.endsWith('.html')) {
76626
- return 'text/html; charset=utf-8';
76627
- }
76628
- if (filePath.endsWith('.js')) {
76629
- return 'text/javascript; charset=utf-8';
76630
- }
76631
- if (filePath.endsWith('.css')) {
76632
- return 'text/css; charset=utf-8';
76633
- }
76634
- if (filePath.endsWith('.json')) {
76635
- return 'application/json; charset=utf-8';
76636
- }
76637
- if (filePath.endsWith('.svg')) {
76638
- return 'image/svg+xml';
76639
- }
76640
- return 'application/octet-stream';
76641
- }
76642
- async function listenHttpServer(server, options, getPortImpl) {
76643
- const maxAttempts = 20;
76644
- const port = await getPortImpl({
76645
- host: options.host,
76646
- port: createPortCandidates(options.port, maxAttempts),
76647
- });
76648
- if (port < options.port || port >= options.port + maxAttempts) {
76649
- throw new Error(`无法找到可用 mock host 端口,起始端口: ${options.port}`);
76650
- }
76651
- await new Promise((resolve, reject) => {
76652
- const onError = (error) => {
76653
- server.off('listening', onListening);
76654
- reject(error);
76655
- };
76656
- const onListening = () => {
76657
- server.off('error', onError);
76658
- resolve();
76659
- };
76660
- server.once('error', onError);
76661
- server.once('listening', onListening);
76662
- server.listen(port, options.host);
76663
- });
76664
- const address = server.address();
76665
- return address.port;
76666
- }
76667
- function createPortCandidates(startPort, count) {
76668
- return Array.from({ length: count }, (_, index) => startPort + index);
76669
- }
76670
- function createMockHostUrl(options) {
76671
- const url = new URL(`http://${toDisplayHost(options.host)}:${options.port}/`);
76672
- url.searchParams.set(MINI_PROGRAM_URL_QUERY_PARAM, options.appUrl);
76673
- return url.toString();
76674
- }
76675
76971
  async function withManagedDevOutputEnv(action, env = process.env) {
76676
76972
  const previous = env[MANAGED_DEV_OUTPUT_ENV];
76677
76973
  env[MANAGED_DEV_OUTPUT_ENV] = '1';
@@ -76695,7 +76991,7 @@ function toDisplayHost(host) {
76695
76991
  }
76696
76992
  function installShutdownHandlers(closers, processLike) {
76697
76993
  const shutdown = async () => {
76698
- await Promise.allSettled(closers.map((close) => close()));
76994
+ await closeAll(closers);
76699
76995
  processLike.exit(0);
76700
76996
  };
76701
76997
  processLike.once('SIGINT', () => {
@@ -76705,13 +77001,18 @@ function installShutdownHandlers(closers, processLike) {
76705
77001
  void shutdown();
76706
77002
  });
76707
77003
  }
77004
+ async function closeAll(closers) {
77005
+ await Promise.allSettled(closers.map((close) => close()));
77006
+ }
77007
+
77008
+ const HB_SDK_PACKAGE_NAME = '@heybox/hb-sdk';
77009
+ const HB_SDK_SKILL_NAME = 'hb-sdk';
77010
+ const HB_SDK_AGENT_SKILLS_BASE_URL = 'https://open.xiaoheihe.cn/agent-skills';
77011
+ const HB_SDK_SKILL_SOURCE = `${HB_SDK_AGENT_SKILLS_BASE_URL}/${HB_SDK_SKILL_NAME}`;
77012
+ const HB_SDK_SKILL_INDEX_URL = `${HB_SDK_AGENT_SKILLS_BASE_URL}/.well-known/agent-skills/index.json`;
77013
+ const HB_SDK_SKILL_INSTALL_COMMAND = `npx skills add ${HB_SDK_SKILL_SOURCE}`;
76708
77014
 
76709
- const PACKAGE_NAME$1 = '@heybox/hb-sdk';
76710
- const SKILL_NAME = 'hb-sdk';
76711
- const DEFAULT_SKILL_SOURCE = 'https://open.xiaoheihe.cn/agent-skills/hb-sdk';
76712
- const DEFAULT_REMOTE_INDEX_URL = 'https://open.xiaoheihe.cn/agent-skills/.well-known/agent-skills/index.json';
76713
77015
  const DEFAULT_TIMEOUT_MS$1 = 3000;
76714
- const SKILL_INSTALL_COMMAND = `npx skills add ${DEFAULT_SKILL_SOURCE}`;
76715
77016
  const PACKAGE_JSON_CANDIDATES$2 = [
76716
77017
  path.resolve(__dirname, '..', 'package.json'),
76717
77018
  path.resolve(__dirname, '..', '..', 'package.json'),
@@ -76756,7 +77057,7 @@ async function getDoctorResult(runtime = {}) {
76756
77057
  };
76757
77058
  }
76758
77059
  if (!localSkill.manifest ||
76759
- localSkill.manifest.name !== SKILL_NAME ||
77060
+ localSkill.manifest.name !== HB_SDK_SKILL_NAME ||
76760
77061
  localSkill.manifest.skillVersion !== remoteSkill.version ||
76761
77062
  localSkill.manifest.sdk?.version !== remoteSkill.sdk?.version) {
76762
77063
  return {
@@ -76788,26 +77089,26 @@ function printDoctorNextStep(result, output) {
76788
77089
  return;
76789
77090
  }
76790
77091
  if (result.status === 'SDK_MISMATCH') {
76791
- output.log(`[hb-sdk] Current SDK does not match the latest skill metadata. Upgrade with: npm i -D ${PACKAGE_NAME$1}@latest`);
77092
+ output.log(`[hb-sdk] Current SDK does not match the latest skill metadata. Upgrade with: npm i -D ${HB_SDK_PACKAGE_NAME}@latest`);
76792
77093
  output.log(`[hb-sdk] Then install or refresh the skill manually: ${formatSkillInstallCommand(result)}`);
76793
77094
  return;
76794
77095
  }
76795
77096
  if (result.status === 'REMOTE_UNAVAILABLE') {
76796
77097
  output.log('[hb-sdk] Remote skill metadata is unavailable. Try again later.');
76797
- output.log(`[hb-sdk] Manual install command: ${SKILL_INSTALL_COMMAND}`);
77098
+ output.log(`[hb-sdk] Manual install command: ${HB_SDK_SKILL_INSTALL_COMMAND}`);
76798
77099
  return;
76799
77100
  }
76800
77101
  output.log(`[hb-sdk] Install or refresh the skill manually: ${formatSkillInstallCommand(result)}`);
76801
77102
  }
76802
77103
  function formatSkillInstallCommand(result) {
76803
- return `npx skills add ${result.remoteSkill?.source || DEFAULT_SKILL_SOURCE}`;
77104
+ return `npx skills add ${result.remoteSkill?.source || HB_SDK_SKILL_SOURCE}`;
76804
77105
  }
76805
77106
  async function fetchRemoteSkill(runtime) {
76806
77107
  const fetchImpl = runtime.fetchImpl ?? fetch;
76807
77108
  const controller = new AbortController();
76808
77109
  const timeout = setTimeout(() => controller.abort(), runtime.timeoutMs ?? DEFAULT_TIMEOUT_MS$1);
76809
77110
  try {
76810
- const response = await fetchImpl(runtime.remoteIndexUrl ?? DEFAULT_REMOTE_INDEX_URL, {
77111
+ const response = await fetchImpl(runtime.remoteIndexUrl ?? HB_SDK_SKILL_INDEX_URL, {
76811
77112
  headers: {
76812
77113
  accept: 'application/json',
76813
77114
  },
@@ -76817,8 +77118,8 @@ async function fetchRemoteSkill(runtime) {
76817
77118
  throw new Error(`remote skill index returned ${response.status}`);
76818
77119
  }
76819
77120
  const index = (await response.json());
76820
- const skill = index.skills?.find(item => item.name === SKILL_NAME);
76821
- if (!skill?.version || skill.sdk?.package !== PACKAGE_NAME$1 || !skill.sdk.version) {
77121
+ const skill = index.skills?.find(item => item.name === HB_SDK_SKILL_NAME);
77122
+ if (!skill?.version || skill.sdk?.package !== HB_SDK_PACKAGE_NAME || !skill.sdk.version) {
76822
77123
  throw new Error('remote hb-sdk skill metadata is incomplete');
76823
77124
  }
76824
77125
  return skill;
@@ -76862,7 +77163,7 @@ function resolveLocalSkillJsonPath(runtime) {
76862
77163
  return runtime.localSkillJsonPath;
76863
77164
  }
76864
77165
  const codexHome = runtime.codexHome ?? runtime.env?.CODEX_HOME ?? process.env.CODEX_HOME ?? path.join(os.homedir(), '.codex');
76865
- return path.join(codexHome, 'skills', SKILL_NAME, 'skill.json');
77166
+ return path.join(codexHome, 'skills', HB_SDK_SKILL_NAME, 'skill.json');
76866
77167
  }
76867
77168
  async function pathExists$2(filePath) {
76868
77169
  try {
@@ -77028,12 +77329,11 @@ async function clearLoginStatus(options = {}) {
77028
77329
  output.log(`cache: ${getAuthCacheFilePath(options)}`);
77029
77330
  }
77030
77331
 
77031
- const PACKAGE_NAME = '@heybox/hb-sdk';
77032
77332
  const UPDATE_CHECK_CACHE_VERSION = 1;
77033
77333
  const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
77034
77334
  const DEFAULT_TIMEOUT_MS = 1500;
77035
77335
  const UPDATE_CHECK_CACHE_FILE = 'update-check.json';
77036
- const NPM_REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}`;
77336
+ const NPM_REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(HB_SDK_PACKAGE_NAME)}`;
77037
77337
  const paths = envPaths('hb-sdk', { suffix: '' });
77038
77338
  const PACKAGE_JSON_CANDIDATES$1 = [
77039
77339
  path.resolve(__dirname, '..', 'package.json'),
@@ -77082,7 +77382,7 @@ async function getUpdateReminder(options = {}) {
77082
77382
  return createUpdateReminder(currentVersion, latestVersion);
77083
77383
  }
77084
77384
  function formatUpdateReminder(reminder) {
77085
- return `[hb-sdk] New version available: ${reminder.currentVersion} -> ${reminder.latestVersion}. Upgrade with: npm i -D ${PACKAGE_NAME}@latest`;
77385
+ return `[hb-sdk] New version available: ${reminder.currentVersion} -> ${reminder.latestVersion}. Upgrade with: npm i -D ${HB_SDK_PACKAGE_NAME}@latest`;
77086
77386
  }
77087
77387
  function isVersionGreater(version, baseline) {
77088
77388
  const compared = compareSemver(version, baseline);
@@ -77259,7 +77559,7 @@ async function pathExists$1(filePath) {
77259
77559
  }
77260
77560
 
77261
77561
  const CLI_VERSION_PLACEHOLDER = ['__HB', 'SDK', 'CLI', 'VERSION__'].join('_');
77262
- const BUILT_CLI_VERSION = '0.2.0-alpha.2';
77562
+ const BUILT_CLI_VERSION = '0.3.1';
77263
77563
  const PACKAGE_JSON_CANDIDATES = [
77264
77564
  path.resolve(__dirname, '..', '..', 'package.json'),
77265
77565
  path.resolve(__dirname, '..', 'package.json'),