@offckb/cli 0.4.8 → 0.4.9-canary-807f2ef.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +79 -16
  2. package/build/index.js +2473 -301
  3. package/package.json +2 -2
package/build/index.js CHANGED
@@ -235,16 +235,23 @@ exports.defaultSettings = {
235
235
  transactionsPath: path.resolve(exports.dataPath, 'mainnet/transactions'),
236
236
  },
237
237
  tools: {
238
+ rootFolder: path.resolve(exports.dataPath, 'tools'),
238
239
  ckbDebugger: {
239
240
  minVersion: '0.200.0',
240
241
  },
242
+ ckbTui: {
243
+ version: 'v0.1.3',
244
+ },
241
245
  },
242
246
  };
243
247
  function readSettings() {
244
248
  try {
245
249
  if (fs.existsSync(exports.configPath)) {
246
250
  const data = fs.readFileSync(exports.configPath, 'utf8');
247
- return deepMerge(exports.defaultSettings, JSON.parse(data));
251
+ const parsed = JSON.parse(data);
252
+ validateSettings(parsed);
253
+ // Deep-clone defaults before merging to prevent mutation of the shared default
254
+ return deepMerge(deepClone(exports.defaultSettings), parsed);
248
255
  }
249
256
  else {
250
257
  return exports.defaultSettings;
@@ -274,10 +281,26 @@ function getCKBBinaryPath(version) {
274
281
  const binaryName = platform === 'win32' ? 'ckb.exe' : 'ckb';
275
282
  return path.join(getCKBBinaryInstallPath(version), binaryName);
276
283
  }
284
+ function deepClone(obj) {
285
+ if (obj === null || typeof obj !== 'object') {
286
+ return obj;
287
+ }
288
+ if (Array.isArray(obj)) {
289
+ return obj.map(deepClone);
290
+ }
291
+ const clone = {};
292
+ for (const key in obj) {
293
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
294
+ clone[key] = deepClone(obj[key]);
295
+ }
296
+ }
297
+ return clone;
298
+ }
299
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
277
300
  function deepMerge(target, source) {
278
301
  for (const key in source) {
279
- if (source[key] && typeof source[key] === 'object') {
280
- if (!target[key]) {
302
+ if (source[key] !== null && typeof source[key] === 'object' && !Array.isArray(source[key])) {
303
+ if (!target[key] || typeof target[key] !== 'object') {
281
304
  target[key] = {};
282
305
  }
283
306
  deepMerge(target[key], source[key]);
@@ -288,6 +311,29 @@ function deepMerge(target, source) {
288
311
  }
289
312
  return target;
290
313
  }
314
+ function validateSettings(raw) {
315
+ if (!raw || typeof raw !== 'object') {
316
+ throw new Error('Settings must be a JSON object');
317
+ }
318
+ const obj = raw;
319
+ if (obj.tools && typeof obj.tools === 'object') {
320
+ const tools = obj.tools;
321
+ if (tools.rootFolder !== undefined && typeof tools.rootFolder !== 'string') {
322
+ throw new Error('tools.rootFolder must be a string path');
323
+ }
324
+ if (tools.ckbTui && typeof tools.ckbTui === 'object') {
325
+ const ckbTui = tools.ckbTui;
326
+ if (ckbTui.version !== undefined && typeof ckbTui.version !== 'string') {
327
+ throw new Error('tools.ckbTui.version must be a string');
328
+ }
329
+ }
330
+ }
331
+ if (obj.proxy !== undefined && obj.proxy !== null) {
332
+ if (typeof obj.proxy !== 'object') {
333
+ throw new Error('proxy must be an object');
334
+ }
335
+ }
336
+ }
291
337
 
292
338
 
293
339
  /***/ }),
@@ -316,29 +362,45 @@ const deposit_1 = __nccwpck_require__(48598);
316
362
  const deploy_1 = __nccwpck_require__(79617);
317
363
  const transfer_1 = __nccwpck_require__(40265);
318
364
  const balance_1 = __nccwpck_require__(77640);
365
+ const udt_1 = __nccwpck_require__(50703);
319
366
  const create_1 = __nccwpck_require__(51534);
320
367
  const config_1 = __nccwpck_require__(93004);
321
368
  const devnet_config_1 = __nccwpck_require__(10015);
369
+ const devnet_fork_1 = __nccwpck_require__(36709);
322
370
  const debug_1 = __nccwpck_require__(39591);
323
371
  const system_scripts_1 = __nccwpck_require__(86198);
324
372
  const transfer_all_1 = __nccwpck_require__(92039);
325
373
  const gen_1 = __nccwpck_require__(750);
326
374
  const ckb_debugger_1 = __nccwpck_require__(14815);
327
375
  const logger_1 = __nccwpck_require__(65370);
376
+ const status_1 = __nccwpck_require__(71420);
377
+ const validator_1 = __nccwpck_require__(39534);
328
378
  const version = (__nccwpck_require__(8330)/* .version */ .rE);
329
379
  const description = (__nccwpck_require__(8330)/* .description */ .h_);
330
380
  // fix windows terminal encoding of simplified chinese text
331
381
  (0, encoding_1.setUTF8EncodingForWindows)();
332
382
  const program = new commander_1.Command();
333
383
  program.name('offckb').description(description).version(version).enablePositionalOptions();
334
- program
384
+ program.option('--json', 'Output logs in JSON format for agent/programmatic consumption');
385
+ program.hook('preAction', (thisCommand) => {
386
+ const opts = thisCommand.opts();
387
+ if (opts.json) {
388
+ logger_1.logger.setJsonMode(true);
389
+ }
390
+ });
391
+ const nodeCommand = program
335
392
  .command('node [CKB-Version]')
336
393
  .description('Use the CKB to start devnet')
337
394
  .option('--network <network>', 'Specify the network to deploy to', 'devnet')
338
395
  .option('-b, --binary-path <binaryPath>', 'Specify the CKB binary path to use, only for devnet, when set, will ignore version and network')
396
+ .option('--daemon', 'Run the node in the background as a daemon (devnet only)')
339
397
  .action((version, options) => __awaiter(void 0, void 0, void 0, function* () {
340
- return (0, node_1.startNode)({ version, network: options.network, binaryPath: options.binaryPath });
398
+ return (0, node_1.startNode)({ version, network: options.network, binaryPath: options.binaryPath, daemon: options.daemon });
341
399
  }));
400
+ nodeCommand
401
+ .command('stop')
402
+ .description('Stop the running CKB devnet daemon')
403
+ .action(() => __awaiter(void 0, void 0, void 0, function* () { return (0, node_1.stopNode)(); }));
342
404
  program
343
405
  .command('create [project-name]')
344
406
  .description('Create a new CKB Smart Contract project in JavaScript.')
@@ -412,13 +474,15 @@ program
412
474
  return (0, deposit_1.deposit)(toAddress, amountInCKB, options);
413
475
  }));
414
476
  program
415
- .command('transfer [toAddress] [amountInCKB]')
416
- .description('Transfer CKB tokens to address, only devnet and testnet')
477
+ .command('transfer [toAddress] [amount]')
478
+ .description('Transfer CKB or UDT tokens to address, only devnet and testnet')
417
479
  .option('--network <network>', 'Specify the network to transfer to', 'devnet')
418
- .option('--privkey <privkey>', 'Specify the private key to transfer CKB')
480
+ .option('--privkey <privkey>', 'Specify the private key to transfer')
481
+ .addOption(new commander_1.Option('--udt-kind <kind>', 'Specify the UDT kind').choices(['sudt', 'xudt']).default('sudt'))
482
+ .option('--udt-type-args <typeArgs>', 'Specify the UDT type script args to transfer UDT')
419
483
  .option('-r, --proxy-rpc', 'Use Proxy RPC to connect to blockchain')
420
- .action((toAddress, amountInCKB, options) => __awaiter(void 0, void 0, void 0, function* () {
421
- return (0, transfer_1.transfer)(toAddress, amountInCKB, options);
484
+ .action((toAddress, amount, options) => __awaiter(void 0, void 0, void 0, function* () {
485
+ return (0, transfer_1.transfer)(toAddress, amount, options);
422
486
  }));
423
487
  program
424
488
  .command('transfer-all [toAddress]')
@@ -431,11 +495,36 @@ program
431
495
  }));
432
496
  program
433
497
  .command('balance [toAddress]')
434
- .description('Check account balance, only devnet and testnet')
498
+ .description('Check account balance (CKB + detected SUDT/xUDT), only devnet and testnet')
435
499
  .option('--network <network>', 'Specify the network to check', 'devnet')
500
+ .addOption(new commander_1.Option('--udt-kind <kind>', 'Filter by UDT kind').choices(['sudt', 'xudt']))
501
+ .option('--udt-type-args <typeArgs>', 'Filter by UDT type script args')
502
+ .option('--no-udt', 'Skip UDT balance scan')
436
503
  .action((toAddress, options) => __awaiter(void 0, void 0, void 0, function* () {
437
504
  return (0, balance_1.balanceOf)(toAddress, options);
438
505
  }));
506
+ const udtCommand = program.command('udt').description('UDT token commands');
507
+ udtCommand
508
+ .command('issue <amount>')
509
+ .description('Issue new UDT tokens, only devnet and testnet')
510
+ .option('--network <network>', 'Specify the network', 'devnet')
511
+ .addOption(new commander_1.Option('--udt-kind <kind>', 'Specify the UDT kind').choices(['sudt', 'xudt']).default('sudt'))
512
+ .option('--type-args <typeArgs>', 'Specify the UDT type script args (xudt only; defaults to signer lock hash)')
513
+ .option('--to <toAddress>', 'Specify the receiver address (defaults to signer)')
514
+ .option('--privkey <privkey>', 'Specify the private key to issue UDT')
515
+ .action((amount, options) => __awaiter(void 0, void 0, void 0, function* () {
516
+ return (0, udt_1.udtIssue)(amount, options);
517
+ }));
518
+ udtCommand
519
+ .command('destroy <amount>')
520
+ .description('Destroy UDT tokens, only devnet and testnet')
521
+ .option('--network <network>', 'Specify the network', 'devnet')
522
+ .addOption(new commander_1.Option('--udt-kind <kind>', 'Specify the UDT kind').choices(['sudt', 'xudt']).default('sudt'))
523
+ .requiredOption('--type-args <typeArgs>', 'Specify the UDT type script args')
524
+ .option('--privkey <privkey>', 'Specify the private key to destroy UDT')
525
+ .action((amount, options) => __awaiter(void 0, void 0, void 0, function* () {
526
+ return (0, udt_1.udtDestroy)(amount, options);
527
+ }));
439
528
  program
440
529
  .command('debugger')
441
530
  .description('Port of the raw CKB Standalone Debugger')
@@ -445,6 +534,14 @@ program
445
534
  .action(() => __awaiter(void 0, void 0, void 0, function* () {
446
535
  return ckb_debugger_1.CKBDebugger.runWithArgs(process.argv.slice(2));
447
536
  }));
537
+ program
538
+ .command('status')
539
+ .description('Show ckb-tui status interface')
540
+ .option('--network <network>', 'Specify the network whose node status to monitor', 'devnet')
541
+ .action((option) => __awaiter(void 0, void 0, void 0, function* () {
542
+ (0, validator_1.validateNetworkOpt)(option.network);
543
+ return yield (0, status_1.status)({ network: option.network });
544
+ }));
448
545
  program
449
546
  .command('config <action> [item] [value]')
450
547
  .description('do a configuration action')
@@ -455,6 +552,14 @@ devnetCommand
455
552
  .description('Open a full-screen editor to tweak devnet config files')
456
553
  .option('-s, --set <key=value>', 'Set a devnet config field non-interactively (repeatable), e.g. --set ckb.logger.filter=info', (value, previous = []) => [...previous, value], [])
457
554
  .action(devnet_config_1.devnetConfig);
555
+ devnetCommand
556
+ .command('fork')
557
+ .description('Fork an existing mainnet/testnet chain data directory into the local devnet')
558
+ .requiredOption('--from <dir>', 'Path to the source CKB node directory (the one passed to ckb -C)')
559
+ .option('--source <source>', 'Source chain: mainnet or testnet (auto-detected from the source ckb.toml when omitted)')
560
+ .option('--spec-file <path>', 'Use a local chain spec file instead of downloading it')
561
+ .option('--force', 'Replace the existing devnet (or a previous fork)')
562
+ .action(devnet_fork_1.devnetFork);
458
563
  program.parse(process.argv);
459
564
  // If no command is specified, display help
460
565
  if (!process.argv.slice(2).length) {
@@ -534,11 +639,31 @@ function balanceOf(address_1) {
534
639
  const network = opt.network;
535
640
  (0, validator_1.validateNetworkOpt)(network);
536
641
  const ckb = new ckb_1.CKB({ network });
537
- const balanceInCKB = yield ckb.balance(address);
538
- logger_1.logger.info(`Balance: ${balanceInCKB} CKB`);
642
+ const [balanceInCKB, udtBalances] = yield Promise.all([
643
+ ckb.balance(address),
644
+ opt.udt !== false ? ckb.detectUdtBalances(address) : Promise.resolve([]),
645
+ ]);
646
+ logger_1.logger.info(`CKB: ${balanceInCKB}`);
647
+ const filtered = filterUdtBalances(udtBalances, opt);
648
+ if (filtered.length > 0) {
649
+ logger_1.logger.info('UDT:');
650
+ for (const udt of filtered) {
651
+ logger_1.logger.info(` ${udt.kind} (args=${udt.args}): ${udt.balance}`);
652
+ }
653
+ }
539
654
  process.exit(0);
540
655
  });
541
656
  }
657
+ function filterUdtBalances(balances, opt) {
658
+ if (!opt.udtKind && !opt.udtTypeArgs) {
659
+ return balances;
660
+ }
661
+ return balances.filter((udt) => {
662
+ const kindMatch = opt.udtKind ? udt.kind === opt.udtKind : true;
663
+ const argsMatch = opt.udtTypeArgs ? udt.args === opt.udtTypeArgs : true;
664
+ return kindMatch && argsMatch;
665
+ });
666
+ }
542
667
 
543
668
 
544
669
  /***/ }),
@@ -973,6 +1098,7 @@ const path_1 = __importDefault(__nccwpck_require__(16928));
973
1098
  const advanced_1 = __nccwpck_require__(98486);
974
1099
  const base_1 = __nccwpck_require__(69951);
975
1100
  const encoding_1 = __nccwpck_require__(6851);
1101
+ const json_rpc_1 = __nccwpck_require__(51726);
976
1102
  const logger_1 = __nccwpck_require__(65370);
977
1103
  function debugTransaction(txHash, network) {
978
1104
  return __awaiter(this, void 0, void 0, function* () {
@@ -1043,8 +1169,8 @@ function buildTxFileOptionBy(txHash, network) {
1043
1169
  if (!fs_1.default.existsSync(outputFilePath)) {
1044
1170
  const rpc = settings[network].rpcUrl;
1045
1171
  const txJsonFilePath = buildTransactionJsonFilePath(network, txHash);
1046
- if (!fs_1.default.existsSync(outputFilePath)) {
1047
- fs_1.default.mkdirSync(path_1.default.dirname(outputFilePath), { recursive: true });
1172
+ if (!fs_1.default.existsSync(txJsonFilePath)) {
1173
+ yield fetchTransactionIntoCache(rpc, txHash, txJsonFilePath);
1048
1174
  }
1049
1175
  yield (0, ckb_tx_dumper_1.dumpTransaction)({ rpc, txJsonFilePath, outputFilePath });
1050
1176
  }
@@ -1052,6 +1178,23 @@ function buildTxFileOptionBy(txHash, network) {
1052
1178
  return opt;
1053
1179
  });
1054
1180
  }
1181
+ // Fallback for transactions that never went through the local RPC proxy
1182
+ // (e.g. historical transactions on a forked devnet): pull the transaction
1183
+ // from the node and cache it in the same JSON-RPC format the proxy stores.
1184
+ function fetchTransactionIntoCache(rpc, txHash, txJsonFilePath) {
1185
+ return __awaiter(this, void 0, void 0, function* () {
1186
+ logger_1.logger.info(`Transaction ${txHash} not found in local cache, fetching from ${rpc} ..`);
1187
+ const result = yield (0, json_rpc_1.callJsonRpc)(rpc, 'get_transaction', [txHash]).catch((error) => {
1188
+ throw new Error(`Failed to fetch transaction ${txHash} from ${rpc}: ${error.message}`);
1189
+ });
1190
+ if (!(result === null || result === void 0 ? void 0 : result.transaction)) {
1191
+ throw new Error(`Transaction ${txHash} not found on ${rpc}. ` +
1192
+ `Check the hash and the --network option, or send the transaction through the offckb RPC proxy first.`);
1193
+ }
1194
+ fs_1.default.mkdirSync(path_1.default.dirname(txJsonFilePath), { recursive: true });
1195
+ fs_1.default.writeFileSync(txJsonFilePath, JSON.stringify(result.transaction, null, 2));
1196
+ });
1197
+ }
1055
1198
  function buildTransactionJsonFilePath(network, txHash) {
1056
1199
  const settings = (0, setting_1.readSettings)();
1057
1200
  if (network === base_1.Network.devnet) {
@@ -1393,6 +1536,43 @@ function devnetConfig() {
1393
1536
  }
1394
1537
 
1395
1538
 
1539
+ /***/ }),
1540
+
1541
+ /***/ 36709:
1542
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
1543
+
1544
+ "use strict";
1545
+
1546
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
1547
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
1548
+ return new (P || (P = Promise))(function (resolve, reject) {
1549
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
1550
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
1551
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
1552
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
1553
+ });
1554
+ };
1555
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1556
+ exports.devnetFork = devnetFork;
1557
+ const fork_1 = __nccwpck_require__(77912);
1558
+ const logger_1 = __nccwpck_require__(65370);
1559
+ function devnetFork(options) {
1560
+ return __awaiter(this, void 0, void 0, function* () {
1561
+ if (options.source && options.source !== 'mainnet' && options.source !== 'testnet') {
1562
+ logger_1.logger.error(`Invalid --source value: ${options.source}. Expected mainnet or testnet.`);
1563
+ process.exit(1);
1564
+ }
1565
+ try {
1566
+ yield (0, fork_1.forkDevnet)(options);
1567
+ }
1568
+ catch (error) {
1569
+ logger_1.logger.error(error.message);
1570
+ process.exit(1);
1571
+ }
1572
+ });
1573
+ }
1574
+
1575
+
1396
1576
  /***/ }),
1397
1577
 
1398
1578
  /***/ 998:
@@ -1400,6 +1580,39 @@ function devnetConfig() {
1400
1580
 
1401
1581
  "use strict";
1402
1582
 
1583
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
1584
+ if (k2 === undefined) k2 = k;
1585
+ var desc = Object.getOwnPropertyDescriptor(m, k);
1586
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1587
+ desc = { enumerable: true, get: function() { return m[k]; } };
1588
+ }
1589
+ Object.defineProperty(o, k2, desc);
1590
+ }) : (function(o, m, k, k2) {
1591
+ if (k2 === undefined) k2 = k;
1592
+ o[k2] = m[k];
1593
+ }));
1594
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
1595
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
1596
+ }) : function(o, v) {
1597
+ o["default"] = v;
1598
+ });
1599
+ var __importStar = (this && this.__importStar) || (function () {
1600
+ var ownKeys = function(o) {
1601
+ ownKeys = Object.getOwnPropertyNames || function (o) {
1602
+ var ar = [];
1603
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
1604
+ return ar;
1605
+ };
1606
+ return ownKeys(o);
1607
+ };
1608
+ return function (mod) {
1609
+ if (mod && mod.__esModule) return mod;
1610
+ var result = {};
1611
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
1612
+ __setModuleDefault(result, mod);
1613
+ return result;
1614
+ };
1615
+ })();
1403
1616
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
1404
1617
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
1405
1618
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -1412,23 +1625,35 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
1412
1625
  Object.defineProperty(exports, "__esModule", ({ value: true }));
1413
1626
  exports.startNode = startNode;
1414
1627
  exports.nodeDevnet = nodeDevnet;
1628
+ exports.stopNode = stopNode;
1415
1629
  exports.nodeTestnet = nodeTestnet;
1416
1630
  exports.nodeMainnet = nodeMainnet;
1417
1631
  const child_process_1 = __nccwpck_require__(35317);
1632
+ const fs = __importStar(__nccwpck_require__(79896));
1633
+ const path = __importStar(__nccwpck_require__(16928));
1418
1634
  const init_chain_1 = __nccwpck_require__(1360);
1419
1635
  const install_1 = __nccwpck_require__(65611);
1420
1636
  const setting_1 = __nccwpck_require__(25546);
1421
1637
  const encoding_1 = __nccwpck_require__(6851);
1422
1638
  const rpc_proxy_1 = __nccwpck_require__(46589);
1639
+ const fork_1 = __nccwpck_require__(77912);
1640
+ const json_rpc_1 = __nccwpck_require__(51726);
1423
1641
  const base_1 = __nccwpck_require__(69951);
1424
1642
  const logger_1 = __nccwpck_require__(65370);
1425
- function startNode({ version, network = base_1.Network.devnet, binaryPath }) {
1643
+ const DAEMON_LOG_DIR = 'logs';
1644
+ const DAEMON_LOG_FILE = 'daemon.log';
1645
+ const DAEMON_PID_FILE = 'daemon.pid';
1646
+ const DAEMON_CHILD_ENV = 'OFFCKB_DAEMON_CHILD';
1647
+ function startNode({ version, network = base_1.Network.devnet, binaryPath, daemon }) {
1426
1648
  if (binaryPath && network !== base_1.Network.devnet) {
1427
1649
  logger_1.logger.warn('Custom binaryPath is only supported for devnet. The provided binaryPath will be ignored.');
1428
1650
  }
1651
+ if (daemon && network !== base_1.Network.devnet) {
1652
+ logger_1.logger.warn('Daemon mode is only supported for devnet. The daemon flag will be ignored.');
1653
+ }
1429
1654
  switch (network) {
1430
1655
  case base_1.Network.devnet:
1431
- return nodeDevnet({ version, binaryPath });
1656
+ return nodeDevnet({ version, binaryPath, daemon });
1432
1657
  case base_1.Network.testnet:
1433
1658
  return nodeTestnet();
1434
1659
  case base_1.Network.mainnet:
@@ -1438,8 +1663,11 @@ function startNode({ version, network = base_1.Network.devnet, binaryPath }) {
1438
1663
  }
1439
1664
  }
1440
1665
  function nodeDevnet(_a) {
1441
- return __awaiter(this, arguments, void 0, function* ({ version, binaryPath }) {
1666
+ return __awaiter(this, arguments, void 0, function* ({ version, binaryPath, daemon }) {
1442
1667
  var _b, _c;
1668
+ if (daemon) {
1669
+ return startDaemon();
1670
+ }
1443
1671
  const settings = (0, setting_1.readSettings)();
1444
1672
  const ckbVersion = version || settings.bins.defaultCKBVersion;
1445
1673
  let ckbBinPath = '';
@@ -1453,7 +1681,14 @@ function nodeDevnet(_a) {
1453
1681
  }
1454
1682
  yield (0, init_chain_1.initChainIfNeeded)();
1455
1683
  const devnetConfigPath = (0, encoding_1.encodeBinPathForTerminal)(settings.devnet.configPath);
1456
- const ckbCmd = `${ckbBinPath} run -C ${devnetConfigPath}`;
1684
+ // A forked devnet must boot once with --skip-spec-check --overwrite-spec so
1685
+ // the imported (and patched) spec replaces the source chain's stored spec.
1686
+ const forkState = (0, fork_1.readForkState)(settings.devnet.configPath);
1687
+ const firstRunFlags = (forkState === null || forkState === void 0 ? void 0 : forkState.firstRunPending) ? ' --skip-spec-check --overwrite-spec' : '';
1688
+ if (forkState === null || forkState === void 0 ? void 0 : forkState.firstRunPending) {
1689
+ logger_1.logger.info(`Forked devnet (${forkState.source}) detected, first run uses --skip-spec-check --overwrite-spec.`);
1690
+ }
1691
+ const ckbCmd = `${ckbBinPath} run -C ${devnetConfigPath}${firstRunFlags}`;
1457
1692
  const minerCmd = `${ckbBinPath} miner -C ${devnetConfigPath}`;
1458
1693
  logger_1.logger.info(`Launching CKB devnet Node...`);
1459
1694
  try {
@@ -1466,6 +1701,11 @@ function nodeDevnet(_a) {
1466
1701
  (_c = ckbProcess.stderr) === null || _c === void 0 ? void 0 : _c.on('data', (data) => {
1467
1702
  logger_1.logger.error(['CKB error:', data.toString()]);
1468
1703
  });
1704
+ if (forkState === null || forkState === void 0 ? void 0 : forkState.firstRunPending) {
1705
+ // Only clear the flag once the spawned node is actually up and reports
1706
+ // the fork's genesis; if startup fails, the next run retries the flags.
1707
+ void clearForkFirstRunWhenNodeUp(ckbProcess, settings.devnet.rpcUrl, settings.devnet.configPath, forkState.genesisHash);
1708
+ }
1469
1709
  // Start the second command after 3 seconds
1470
1710
  setTimeout(() => __awaiter(this, void 0, void 0, function* () {
1471
1711
  var _a, _b;
@@ -1494,6 +1734,354 @@ function nodeDevnet(_a) {
1494
1734
  }
1495
1735
  });
1496
1736
  }
1737
+ function resolveDaemonPaths() {
1738
+ const settings = (0, setting_1.readSettings)();
1739
+ const logDir = path.join(settings.devnet.dataPath, DAEMON_LOG_DIR);
1740
+ const logFile = path.join(logDir, DAEMON_LOG_FILE);
1741
+ const pidFile = path.join(logDir, DAEMON_PID_FILE);
1742
+ return { logDir, logFile, pidFile };
1743
+ }
1744
+ // Poll the devnet RPC until the spawned node answers with the fork's genesis
1745
+ // hash, then mark the first run as done so subsequent `offckb node` runs boot
1746
+ // normally. Two guards against clearing the flag on the wrong signal:
1747
+ // - the poll aborts when the spawned ckb process exits (e.g. failed boot),
1748
+ // - an answering node is only trusted when its genesis matches the fork
1749
+ // state — an unrelated node occupying the port must not clear the flag.
1750
+ function clearForkFirstRunWhenNodeUp(ckbProcess, rpcUrl, configPath, expectedGenesisHash) {
1751
+ return __awaiter(this, void 0, void 0, function* () {
1752
+ let processExited = false;
1753
+ const markExited = () => {
1754
+ processExited = true;
1755
+ };
1756
+ ckbProcess.once('exit', markExited);
1757
+ ckbProcess.once('error', markExited);
1758
+ const timeoutMs = 10 * 60 * 1000; // large forks take a while to boot
1759
+ const start = Date.now();
1760
+ while (!processExited && Date.now() - start < timeoutMs) {
1761
+ try {
1762
+ const genesisHash = String(yield (0, json_rpc_1.callJsonRpc)(rpcUrl, 'get_block_hash', ['0x0'], 5000)).toLowerCase();
1763
+ if (genesisHash !== expectedGenesisHash.toLowerCase()) {
1764
+ logger_1.logger.warn(`A node is answering at ${rpcUrl} but reports a different genesis (${genesisHash}); ` +
1765
+ 'leaving the first-run flags in place.');
1766
+ return;
1767
+ }
1768
+ (0, fork_1.markForkFirstRunComplete)(configPath);
1769
+ logger_1.logger.success('Forked devnet is up; first-run spec flags cleared.');
1770
+ return;
1771
+ }
1772
+ catch (_a) {
1773
+ yield new Promise((resolve) => setTimeout(resolve, 1000));
1774
+ }
1775
+ }
1776
+ if (processExited) {
1777
+ logger_1.logger.warn('The CKB process exited before the forked devnet came up; first-run flags will be retried next time.');
1778
+ }
1779
+ else {
1780
+ logger_1.logger.warn('Timed out waiting for the forked devnet to start; first-run flags will be retried next time.');
1781
+ }
1782
+ });
1783
+ }
1784
+ function readPidFile(pidFile) {
1785
+ var _a, _b;
1786
+ let raw;
1787
+ try {
1788
+ raw = fs.readFileSync(pidFile, 'utf8').trim();
1789
+ }
1790
+ catch (error) {
1791
+ // Treat a missing or unreadable PID file as "no daemon".
1792
+ return null;
1793
+ }
1794
+ if (!raw) {
1795
+ return null;
1796
+ }
1797
+ // Backward compatibility: plain integer PID written by older versions.
1798
+ const plainPid = Number(raw);
1799
+ if (Number.isInteger(plainPid) && plainPid > 0) {
1800
+ return { pid: plainPid, scriptPath: (_a = resolveCliEntry()) !== null && _a !== void 0 ? _a : '', startedAt: new Date(0).toISOString() };
1801
+ }
1802
+ try {
1803
+ const parsed = JSON.parse(raw);
1804
+ const pid = Number(parsed.pid);
1805
+ if (Number.isInteger(pid) && pid > 0 && typeof parsed.scriptPath === 'string') {
1806
+ return {
1807
+ pid,
1808
+ scriptPath: parsed.scriptPath,
1809
+ startedAt: (_b = parsed.startedAt) !== null && _b !== void 0 ? _b : new Date(0).toISOString(),
1810
+ };
1811
+ }
1812
+ }
1813
+ catch (_c) {
1814
+ // fall through to sentinel below
1815
+ }
1816
+ // Content exists but is neither a valid plain PID nor valid metadata.
1817
+ // Return a sentinel so stopNode can report an invalid PID and clean up.
1818
+ return { pid: NaN, scriptPath: '', startedAt: new Date(0).toISOString() };
1819
+ }
1820
+ function writePidFile(pidFile, metadata) {
1821
+ fs.writeFileSync(pidFile, JSON.stringify(metadata, null, 2));
1822
+ }
1823
+ function resolveCliEntry() {
1824
+ var _a;
1825
+ // In priority order. process.argv[1] is the most reliable for a Node CLI.
1826
+ // OFFCKB_CLI_PATH is an escape hatch for packaged/npx/weird environments.
1827
+ // require.main?.filename is a final fallback when argv is unavailable.
1828
+ const candidates = [process.env.OFFCKB_CLI_PATH, process.argv[1], (_a = require.main) === null || _a === void 0 ? void 0 : _a.filename].filter((c) => typeof c === 'string' && c.length > 0);
1829
+ for (const candidate of candidates) {
1830
+ try {
1831
+ const resolved = path.resolve(candidate);
1832
+ const stats = fs.statSync(resolved);
1833
+ if (stats.isFile()) {
1834
+ return resolved;
1835
+ }
1836
+ }
1837
+ catch (_b) {
1838
+ // Candidate is missing or not a file; try the next one.
1839
+ }
1840
+ }
1841
+ return null;
1842
+ }
1843
+ function isProcessAlive(pid) {
1844
+ try {
1845
+ process.kill(pid, 0);
1846
+ return true;
1847
+ }
1848
+ catch (_a) {
1849
+ return false;
1850
+ }
1851
+ }
1852
+ function cleanupPidFile(pidFile) {
1853
+ try {
1854
+ fs.unlinkSync(pidFile);
1855
+ }
1856
+ catch (error) {
1857
+ logger_1.logger.warn(`Failed to remove PID file ${pidFile}:`, error);
1858
+ }
1859
+ }
1860
+ function waitForProcessExit(pid, timeoutMs) {
1861
+ const start = Date.now();
1862
+ return new Promise((resolve) => {
1863
+ const check = () => {
1864
+ if (!isProcessAlive(pid)) {
1865
+ resolve(true);
1866
+ return;
1867
+ }
1868
+ if (Date.now() - start >= timeoutMs) {
1869
+ resolve(false);
1870
+ return;
1871
+ }
1872
+ setTimeout(check, 100);
1873
+ };
1874
+ check();
1875
+ });
1876
+ }
1877
+ function getProcessCommandLine(pid) {
1878
+ return new Promise((resolve) => {
1879
+ if (process.platform === 'win32') {
1880
+ (0, child_process_1.exec)(`wmic process where ProcessId=${pid} get CommandLine /format:list`, (error, stdout) => {
1881
+ if (error) {
1882
+ resolve(null);
1883
+ return;
1884
+ }
1885
+ const match = stdout.match(/CommandLine=(.+)/);
1886
+ resolve(match ? match[1].trim() : null);
1887
+ });
1888
+ }
1889
+ else {
1890
+ (0, child_process_1.exec)(`ps -p ${pid} -o args=`, (error, stdout) => {
1891
+ if (error) {
1892
+ resolve(null);
1893
+ return;
1894
+ }
1895
+ resolve(stdout.trim());
1896
+ });
1897
+ }
1898
+ });
1899
+ }
1900
+ function verifyDaemonIdentity(pid, metadata) {
1901
+ return __awaiter(this, void 0, void 0, function* () {
1902
+ const cmdline = yield getProcessCommandLine(pid);
1903
+ if (!cmdline) {
1904
+ return false;
1905
+ }
1906
+ // The daemon child re-runs the same CLI entry point, so its command line
1907
+ // should reference the same script and should be a Node process.
1908
+ const scriptName = path.basename(metadata.scriptPath);
1909
+ const scriptDir = path.dirname(metadata.scriptPath);
1910
+ const looksLikeNode = cmdline.includes('node') || cmdline.includes('nodejs');
1911
+ const looksLikeOurScript = cmdline.includes(metadata.scriptPath) || (scriptName !== '' && cmdline.includes(scriptName));
1912
+ const looksLikeOffckb = cmdline.includes('offckb') || scriptDir.includes('offckb');
1913
+ return looksLikeNode && (looksLikeOurScript || looksLikeOffckb);
1914
+ });
1915
+ }
1916
+ function terminateProcess(pid, signal) {
1917
+ return new Promise((resolve, reject) => {
1918
+ if (process.platform === 'win32') {
1919
+ // Windows has no POSIX signals and process.kill(pid) only terminates the
1920
+ // single process. Use taskkill to terminate the whole tree.
1921
+ // /T kills the process and all child processes.
1922
+ // /F forces termination when SIGKILL is requested.
1923
+ const args = signal === 'SIGKILL' ? ['/T', '/F', '/PID', String(pid)] : ['/T', '/PID', String(pid)];
1924
+ const taskkill = (0, child_process_1.spawn)('taskkill', args, { stdio: 'ignore' });
1925
+ taskkill.on('error', reject);
1926
+ taskkill.on('exit', () => {
1927
+ // taskkill may return non-zero if the process is already gone, which
1928
+ // is acceptable for our purposes.
1929
+ resolve();
1930
+ });
1931
+ return;
1932
+ }
1933
+ // On POSIX, detached: true makes the child a session/process group leader.
1934
+ // A negative pid sends the signal to the entire process group, ensuring
1935
+ // the CKB node, miner and RPC proxy all receive it.
1936
+ try {
1937
+ process.kill(-pid, signal);
1938
+ resolve();
1939
+ }
1940
+ catch (error) {
1941
+ reject(error);
1942
+ }
1943
+ });
1944
+ }
1945
+ function startDaemon() {
1946
+ const { logDir, logFile, pidFile } = resolveDaemonPaths();
1947
+ // Prevent duplicate daemon starts. If a daemon is already running, refuse
1948
+ // to overwrite its PID file.
1949
+ const existing = readPidFile(pidFile);
1950
+ if (existing && isProcessAlive(existing.pid)) {
1951
+ logger_1.logger.error(`A CKB devnet daemon is already running (PID ${existing.pid}). Stop it first with: offckb node stop`);
1952
+ return;
1953
+ }
1954
+ if (existing && !isProcessAlive(existing.pid)) {
1955
+ // Stale PID file from a crashed daemon; clean it up before starting anew.
1956
+ cleanupPidFile(pidFile);
1957
+ }
1958
+ let out;
1959
+ let err;
1960
+ try {
1961
+ fs.mkdirSync(logDir, { recursive: true });
1962
+ out = fs.openSync(logFile, 'a');
1963
+ err = fs.openSync(logFile, 'a');
1964
+ }
1965
+ catch (error) {
1966
+ logger_1.logger.error(`Failed to prepare daemon log directory or log file at ${logFile}:`, error);
1967
+ return;
1968
+ }
1969
+ const scriptPath = resolveCliEntry();
1970
+ if (!scriptPath) {
1971
+ logger_1.logger.error('Unable to determine the CLI entry point for daemon mode. Set OFFCKB_CLI_PATH to the offckb script.');
1972
+ closeFileDescriptors(out, err);
1973
+ return;
1974
+ }
1975
+ const childArgs = process.argv.slice(2).filter((arg) => arg !== '--daemon');
1976
+ const childEnv = Object.assign(Object.assign({}, process.env), { [DAEMON_CHILD_ENV]: '1' });
1977
+ let child;
1978
+ try {
1979
+ child = (0, child_process_1.spawn)(process.execPath, [scriptPath, ...childArgs], {
1980
+ detached: true,
1981
+ stdio: ['ignore', out, err],
1982
+ env: childEnv,
1983
+ });
1984
+ }
1985
+ catch (error) {
1986
+ logger_1.logger.error('Failed to spawn daemon process:', error);
1987
+ closeFileDescriptors(out, err);
1988
+ return;
1989
+ }
1990
+ if (!child.pid) {
1991
+ logger_1.logger.error('Failed to spawn daemon process: no PID returned.');
1992
+ closeFileDescriptors(out, err);
1993
+ return;
1994
+ }
1995
+ child.unref();
1996
+ child.on('error', (error) => {
1997
+ logger_1.logger.error('Daemon child process failed to start:', error);
1998
+ cleanupPidFile(pidFile);
1999
+ });
2000
+ const metadata = {
2001
+ pid: child.pid,
2002
+ scriptPath,
2003
+ startedAt: new Date().toISOString(),
2004
+ };
2005
+ writePidFile(pidFile, metadata);
2006
+ // File descriptors are now owned by the spawned child; close our copies.
2007
+ closeFileDescriptors(out, err);
2008
+ logger_1.logger.success(`CKB devnet daemon started with PID ${child.pid}.`);
2009
+ logger_1.logger.info(`Logs: ${logFile}`);
2010
+ logger_1.logger.info(`PID file: ${pidFile}`);
2011
+ logger_1.logger.info('Stop the daemon with: offckb node stop');
2012
+ }
2013
+ function closeFileDescriptors(...fds) {
2014
+ for (const fd of fds) {
2015
+ if (fd === undefined)
2016
+ continue;
2017
+ try {
2018
+ fs.closeSync(fd);
2019
+ }
2020
+ catch (_a) {
2021
+ // ignore
2022
+ }
2023
+ }
2024
+ }
2025
+ function stopNode() {
2026
+ return __awaiter(this, void 0, void 0, function* () {
2027
+ const { pidFile } = resolveDaemonPaths();
2028
+ const metadata = readPidFile(pidFile);
2029
+ if (!metadata) {
2030
+ logger_1.logger.warn(`No daemon PID file found at ${pidFile}. Is the devnet daemon running?`);
2031
+ return;
2032
+ }
2033
+ const pid = metadata.pid;
2034
+ if (!Number.isInteger(pid) || pid <= 0) {
2035
+ logger_1.logger.error(`Invalid PID in ${pidFile}: ${pid}`);
2036
+ cleanupPidFile(pidFile);
2037
+ return;
2038
+ }
2039
+ if (!isProcessAlive(pid)) {
2040
+ logger_1.logger.warn(`Daemon process ${pid} is not running.`);
2041
+ cleanupPidFile(pidFile);
2042
+ return;
2043
+ }
2044
+ const identityOk = yield verifyDaemonIdentity(pid, metadata);
2045
+ if (!identityOk) {
2046
+ logger_1.logger.error(`Process ${pid} does not appear to be the offckb daemon. Refusing to send signals to avoid killing an unrelated process. ` +
2047
+ `If you are sure this is the daemon, stop it manually and remove ${pidFile}.`);
2048
+ return;
2049
+ }
2050
+ logger_1.logger.info(`Stopping CKB devnet daemon (PID ${pid})...`);
2051
+ try {
2052
+ yield terminateProcess(pid, 'SIGTERM');
2053
+ }
2054
+ catch (error) {
2055
+ const err = error;
2056
+ if (err.code === 'ESRCH') {
2057
+ logger_1.logger.warn(`Daemon process ${pid} is not running.`);
2058
+ cleanupPidFile(pidFile);
2059
+ return;
2060
+ }
2061
+ if (err.code === 'EPERM') {
2062
+ logger_1.logger.error(`Permission denied when sending SIGTERM to daemon process ${pid}.`);
2063
+ }
2064
+ else {
2065
+ logger_1.logger.error(`Failed to send SIGTERM to daemon process ${pid}:`, error);
2066
+ }
2067
+ // Still try to clean up the PID file so the user can recover.
2068
+ cleanupPidFile(pidFile);
2069
+ return;
2070
+ }
2071
+ const exited = yield waitForProcessExit(pid, 5000);
2072
+ if (!exited) {
2073
+ logger_1.logger.warn(`Daemon process ${pid} did not exit gracefully, sending SIGKILL...`);
2074
+ try {
2075
+ yield terminateProcess(pid, 'SIGKILL');
2076
+ }
2077
+ catch (error) {
2078
+ logger_1.logger.error(`Failed to send SIGKILL to daemon process ${pid}:`, error);
2079
+ }
2080
+ }
2081
+ cleanupPidFile(pidFile);
2082
+ logger_1.logger.success('CKB devnet daemon stopped.');
2083
+ });
2084
+ }
1497
2085
  function nodeTestnet() {
1498
2086
  return __awaiter(this, void 0, void 0, function* () {
1499
2087
  // todo: maybe we can actually start a node for testnet later
@@ -1518,6 +2106,130 @@ function nodeMainnet() {
1518
2106
  }
1519
2107
 
1520
2108
 
2109
+ /***/ }),
2110
+
2111
+ /***/ 71420:
2112
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
2113
+
2114
+ "use strict";
2115
+
2116
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
2117
+ if (k2 === undefined) k2 = k;
2118
+ var desc = Object.getOwnPropertyDescriptor(m, k);
2119
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
2120
+ desc = { enumerable: true, get: function() { return m[k]; } };
2121
+ }
2122
+ Object.defineProperty(o, k2, desc);
2123
+ }) : (function(o, m, k, k2) {
2124
+ if (k2 === undefined) k2 = k;
2125
+ o[k2] = m[k];
2126
+ }));
2127
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
2128
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
2129
+ }) : function(o, v) {
2130
+ o["default"] = v;
2131
+ });
2132
+ var __importStar = (this && this.__importStar) || (function () {
2133
+ var ownKeys = function(o) {
2134
+ ownKeys = Object.getOwnPropertyNames || function (o) {
2135
+ var ar = [];
2136
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
2137
+ return ar;
2138
+ };
2139
+ return ownKeys(o);
2140
+ };
2141
+ return function (mod) {
2142
+ if (mod && mod.__esModule) return mod;
2143
+ var result = {};
2144
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
2145
+ __setModuleDefault(result, mod);
2146
+ return result;
2147
+ };
2148
+ })();
2149
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2150
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
2151
+ return new (P || (P = Promise))(function (resolve, reject) {
2152
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
2153
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
2154
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
2155
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
2156
+ });
2157
+ };
2158
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2159
+ exports.status = status;
2160
+ const setting_1 = __nccwpck_require__(25546);
2161
+ const ckb_tui_1 = __nccwpck_require__(7020);
2162
+ const base_1 = __nccwpck_require__(69951);
2163
+ const logger_1 = __nccwpck_require__(65370);
2164
+ const net = __importStar(__nccwpck_require__(69278));
2165
+ const NETWORK_SETTINGS_KEY = {
2166
+ [base_1.Network.devnet]: 'devnet',
2167
+ [base_1.Network.testnet]: 'testnet',
2168
+ [base_1.Network.mainnet]: 'mainnet',
2169
+ };
2170
+ function status(_a) {
2171
+ return __awaiter(this, arguments, void 0, function* ({ network }) {
2172
+ var _b;
2173
+ // ckb-tui is an interactive terminal UI. Running it without a TTY
2174
+ // (pipe, redirect, CI) would hang or produce garbage output.
2175
+ if (!process.stdout.isTTY || !process.stdin.isTTY) {
2176
+ logger_1.logger.error('The status command requires an interactive terminal (TTY). ' +
2177
+ 'It cannot be used in pipes, redirects, or non-interactive environments like CI.');
2178
+ process.exit(1);
2179
+ }
2180
+ const settings = (0, setting_1.readSettings)();
2181
+ const networkKey = NETWORK_SETTINGS_KEY[network];
2182
+ const port = settings[networkKey].rpcProxyPort;
2183
+ const url = `http://127.0.0.1:${port}`;
2184
+ const isListening = yield isRPCPortListening(port);
2185
+ if (!isListening) {
2186
+ logger_1.logger.error(`RPC port ${port} is not listening. Please make sure the ${network} node is running and Proxy RPC is enabled.`);
2187
+ return;
2188
+ }
2189
+ const result = ckb_tui_1.CKBTui.run(['-r', url]);
2190
+ // Propagate ckb-tui exit code so scripts can detect TUI failure
2191
+ if (result.status !== 0) {
2192
+ process.exitCode = (_b = result.status) !== null && _b !== void 0 ? _b : 1;
2193
+ }
2194
+ });
2195
+ }
2196
+ function isRPCPortListening(port) {
2197
+ return __awaiter(this, void 0, void 0, function* () {
2198
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
2199
+ return false;
2200
+ }
2201
+ const client = new net.Socket();
2202
+ return new Promise((resolve) => {
2203
+ let settled = false;
2204
+ const TIMEOUT_MS = 2000;
2205
+ const timeout = setTimeout(() => {
2206
+ if (!settled) {
2207
+ settled = true;
2208
+ client.destroy();
2209
+ resolve(false);
2210
+ }
2211
+ }, TIMEOUT_MS);
2212
+ client.once('error', () => {
2213
+ if (!settled) {
2214
+ settled = true;
2215
+ clearTimeout(timeout);
2216
+ resolve(false);
2217
+ }
2218
+ });
2219
+ client.once('connect', () => {
2220
+ if (!settled) {
2221
+ settled = true;
2222
+ clearTimeout(timeout);
2223
+ client.end();
2224
+ resolve(true);
2225
+ }
2226
+ });
2227
+ client.connect(port, '127.0.0.1');
2228
+ });
2229
+ });
2230
+ }
2231
+
2232
+
1521
2233
  /***/ }),
1522
2234
 
1523
2235
  /***/ 86198:
@@ -1540,7 +2252,6 @@ exports.printSystemScripts = printSystemScripts;
1540
2252
  exports.printInSystemStyle = printInSystemStyle;
1541
2253
  exports.printInLumosConfigStyle = printInLumosConfigStyle;
1542
2254
  exports.printInCCCStyle = printInCCCStyle;
1543
- exports.systemCellToScriptInfo = systemCellToScriptInfo;
1544
2255
  exports.toLumosConfig = toLumosConfig;
1545
2256
  const base_1 = __nccwpck_require__(69951);
1546
2257
  const public_1 = __nccwpck_require__(84535);
@@ -1554,26 +2265,41 @@ var PrintStyle;
1554
2265
  })(PrintStyle || (exports.PrintStyle = PrintStyle = {}));
1555
2266
  function printSystemScripts(_a) {
1556
2267
  return __awaiter(this, arguments, void 0, function* ({ style = PrintStyle.system, network = base_1.Network.devnet }) {
1557
- const systemScripts = network === base_1.Network.mainnet
1558
- ? public_1.MAINNET_SYSTEM_SCRIPTS
1559
- : network === base_1.Network.testnet
1560
- ? public_1.TESTNET_SYSTEM_SCRIPTS
1561
- : (0, private_1.getDevnetSystemScriptsFromListHashes)();
2268
+ var _b;
2269
+ let systemScripts;
2270
+ // Display label and address prefix follow the chain the devnet actually
2271
+ // runs: a fork carries the source chain's genesis, scripts and prefix.
2272
+ let label = network.toUpperCase();
2273
+ let addressPrefix = network === base_1.Network.mainnet ? 'ckb' : 'ckt';
2274
+ if (network === base_1.Network.mainnet) {
2275
+ systemScripts = public_1.MAINNET_SYSTEM_SCRIPTS;
2276
+ }
2277
+ else if (network === base_1.Network.testnet) {
2278
+ systemScripts = public_1.TESTNET_SYSTEM_SCRIPTS;
2279
+ }
2280
+ else {
2281
+ const resolved = (0, private_1.resolveDevnetSystemScripts)();
2282
+ systemScripts = (_b = resolved === null || resolved === void 0 ? void 0 : resolved.scripts) !== null && _b !== void 0 ? _b : null;
2283
+ if (resolved === null || resolved === void 0 ? void 0 : resolved.forkedFrom) {
2284
+ label = `DEVNET (fork of ${resolved.forkedFrom.toUpperCase()})`;
2285
+ addressPrefix = resolved.forkedFrom === 'mainnet' ? 'ckb' : 'ckt';
2286
+ }
2287
+ }
1562
2288
  if (!systemScripts)
1563
2289
  return logger_1.logger.info(`SystemScripts is null, ${network}`);
1564
2290
  if (style === PrintStyle.system) {
1565
- return printInSystemStyle(systemScripts, network);
2291
+ return printInSystemStyle(systemScripts, label);
1566
2292
  }
1567
2293
  if (style === PrintStyle.lumos) {
1568
- return printInLumosConfigStyle(systemScripts, network);
2294
+ return printInLumosConfigStyle(systemScripts, label, addressPrefix);
1569
2295
  }
1570
2296
  if (style === PrintStyle.ccc) {
1571
- return printInCCCStyle(systemScripts, network);
2297
+ return printInCCCStyle(systemScripts, label);
1572
2298
  }
1573
2299
  });
1574
2300
  }
1575
- function printInSystemStyle(systemScripts, network) {
1576
- logger_1.logger.info(`*** CKB ${network.toUpperCase()} System Scripts ***\n`);
2301
+ function printInSystemStyle(systemScripts, label) {
2302
+ logger_1.logger.info(`*** CKB ${label} System Scripts ***\n`);
1577
2303
  for (const [name, script] of Object.entries(systemScripts)) {
1578
2304
  logger_1.logger.info(`- name: ${name}`);
1579
2305
  if (script == null) {
@@ -1586,67 +2312,16 @@ function printInSystemStyle(systemScripts, network) {
1586
2312
  logger_1.logger.info(` cellDeps: ${JSON.stringify(script.script.cellDeps, null, 2)}\n`);
1587
2313
  }
1588
2314
  }
1589
- function printInLumosConfigStyle(scripts, network) {
1590
- const config = toLumosConfig(scripts, network === base_1.Network.mainnet ? 'ckb' : 'ckt');
1591
- logger_1.logger.info(`*** CKB ${network.toUpperCase()} System Scripts As LumosConfig ***\n`);
2315
+ function printInLumosConfigStyle(scripts, label, addressPrefix) {
2316
+ const config = toLumosConfig(scripts, addressPrefix);
2317
+ logger_1.logger.info(`*** CKB ${label} System Scripts As LumosConfig ***\n`);
1592
2318
  logger_1.logger.info(JSON.stringify(config, null, 2));
1593
2319
  }
1594
- function printInCCCStyle(scripts, network) {
2320
+ function printInCCCStyle(scripts, label) {
1595
2321
  const knownsScripts = (0, private_1.toCCCKnownScripts)(scripts);
1596
- logger_1.logger.info(`*** CKB ${network.toUpperCase()} System Scripts As CCC KnownScripts ***\n`);
2322
+ logger_1.logger.info(`*** CKB ${label} System Scripts As CCC KnownScripts ***\n`);
1597
2323
  logger_1.logger.info(JSON.stringify(knownsScripts, null, 2));
1598
2324
  }
1599
- function systemCellToScriptInfo({ cell, depType, depGroup, extraCellDeps, }) {
1600
- // todo: we left the type in cellDepsInfo since it requires async fetching and
1601
- // chain running to get the full type script of the type-id deps.
1602
- // Also, in devnet there is no real need to auto upgrade the system scripts with type-id
1603
- if (depType === 'code') {
1604
- let cellDeps = [
1605
- {
1606
- cellDep: {
1607
- outPoint: {
1608
- txHash: cell.tx_hash,
1609
- index: cell.index,
1610
- },
1611
- depType,
1612
- },
1613
- },
1614
- ];
1615
- if (extraCellDeps && extraCellDeps.length > 0) {
1616
- cellDeps = [...extraCellDeps, ...cellDeps];
1617
- }
1618
- return {
1619
- codeHash: (cell.type_hash || cell.data_hash),
1620
- hashType: cell.type_hash ? 'type' : 'data2',
1621
- cellDeps,
1622
- };
1623
- }
1624
- if (depType === 'depGroup') {
1625
- if (!depGroup) {
1626
- throw new Error('require depGroup info since the dep type is depGroup');
1627
- }
1628
- let cellDeps = [
1629
- {
1630
- cellDep: {
1631
- outPoint: {
1632
- txHash: depGroup.txHash,
1633
- index: depGroup.index,
1634
- },
1635
- depType,
1636
- },
1637
- },
1638
- ];
1639
- if (extraCellDeps && extraCellDeps.length > 0) {
1640
- cellDeps = [...extraCellDeps, ...cellDeps];
1641
- }
1642
- return {
1643
- codeHash: (cell.type_hash || cell.data_hash),
1644
- hashType: cell.type_hash ? 'type' : 'data2',
1645
- cellDeps,
1646
- };
1647
- }
1648
- throw new Error(`unknown DepType ${depType}`);
1649
- }
1650
2325
  function toLumosConfig(scripts, addressPrefix = 'ckt') {
1651
2326
  const config = {
1652
2327
  PREFIX: addressPrefix,
@@ -1829,9 +2504,9 @@ const ckb_1 = __nccwpck_require__(83748);
1829
2504
  const base_1 = __nccwpck_require__(69951);
1830
2505
  const link_1 = __nccwpck_require__(27814);
1831
2506
  const validator_1 = __nccwpck_require__(39534);
1832
- const logger_1 = __nccwpck_require__(65370);
1833
- function transfer(toAddress_1, amountInCKB_1) {
1834
- return __awaiter(this, arguments, void 0, function* (toAddress, amountInCKB, opt = { network: base_1.Network.devnet }) {
2507
+ function transfer(toAddress_1, amount_1) {
2508
+ return __awaiter(this, arguments, void 0, function* (toAddress, amount, opt = { network: base_1.Network.devnet }) {
2509
+ var _a;
1835
2510
  const network = opt.network;
1836
2511
  (0, validator_1.validateNetworkOpt)(network);
1837
2512
  if (opt.privkey == null) {
@@ -1839,16 +2514,89 @@ function transfer(toAddress_1, amountInCKB_1) {
1839
2514
  }
1840
2515
  const privateKey = opt.privkey;
1841
2516
  const ckb = new ckb_1.CKB({ network });
2517
+ if (opt.udtTypeArgs) {
2518
+ const kind = (_a = opt.udtKind) !== null && _a !== void 0 ? _a : 'sudt';
2519
+ (0, validator_1.validateUdtKind)(kind);
2520
+ const udtTypeArgs = (0, validator_1.validateUdtTypeArgs)(kind, opt.udtTypeArgs);
2521
+ const udtType = yield ckb.buildUdtTypeScript(kind, udtTypeArgs);
2522
+ const txHash = yield ckb.udtTransfer({
2523
+ toAddress,
2524
+ amount,
2525
+ privateKey,
2526
+ udtType,
2527
+ kind,
2528
+ });
2529
+ (0, link_1.logTxSuccess)(network, txHash, 'transfer UDT');
2530
+ return;
2531
+ }
1842
2532
  const txHash = yield ckb.transfer({
1843
2533
  toAddress,
1844
- amountInCKB,
2534
+ amountInCKB: amount,
1845
2535
  privateKey,
1846
2536
  });
1847
- if (network === 'testnet') {
1848
- logger_1.logger.info(`Successfully transfer, check ${(0, link_1.buildTestnetTxLink)(txHash)} for details.`);
1849
- return;
2537
+ (0, link_1.logTxSuccess)(network, txHash, 'transfer');
2538
+ });
2539
+ }
2540
+
2541
+
2542
+ /***/ }),
2543
+
2544
+ /***/ 50703:
2545
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
2546
+
2547
+ "use strict";
2548
+
2549
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2550
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
2551
+ return new (P || (P = Promise))(function (resolve, reject) {
2552
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
2553
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
2554
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
2555
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
2556
+ });
2557
+ };
2558
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2559
+ exports.udtIssue = udtIssue;
2560
+ exports.udtDestroy = udtDestroy;
2561
+ const ckb_1 = __nccwpck_require__(83748);
2562
+ const base_1 = __nccwpck_require__(69951);
2563
+ const link_1 = __nccwpck_require__(27814);
2564
+ const validator_1 = __nccwpck_require__(39534);
2565
+ function udtIssue(amount_1) {
2566
+ return __awaiter(this, arguments, void 0, function* (amount, opt = { network: base_1.Network.devnet, udtKind: 'sudt', privkey: '' }) {
2567
+ const network = opt.network;
2568
+ (0, validator_1.validateNetworkOpt)(network);
2569
+ (0, validator_1.validateUdtKind)(opt.udtKind);
2570
+ if (!opt.privkey) {
2571
+ throw new Error('--privkey is required!');
1850
2572
  }
1851
- logger_1.logger.info('Successfully transfer, txHash:', txHash);
2573
+ const ckb = new ckb_1.CKB({ network });
2574
+ const txHash = yield ckb.udtIssue({
2575
+ privateKey: opt.privkey,
2576
+ kind: opt.udtKind,
2577
+ amount,
2578
+ typeArgs: opt.typeArgs ? (0, validator_1.validateUdtTypeArgs)(opt.udtKind, opt.typeArgs) : undefined,
2579
+ toAddress: opt.to,
2580
+ });
2581
+ (0, link_1.logTxSuccess)(network, txHash, 'issued UDT');
2582
+ });
2583
+ }
2584
+ function udtDestroy(amount_1) {
2585
+ return __awaiter(this, arguments, void 0, function* (amount, opt = { network: base_1.Network.devnet, udtKind: 'sudt', typeArgs: '', privkey: '' }) {
2586
+ const network = opt.network;
2587
+ (0, validator_1.validateNetworkOpt)(network);
2588
+ (0, validator_1.validateUdtKind)(opt.udtKind);
2589
+ if (!opt.privkey) {
2590
+ throw new Error('--privkey is required!');
2591
+ }
2592
+ const ckb = new ckb_1.CKB({ network });
2593
+ const txHash = yield ckb.udtDestroy({
2594
+ privateKey: opt.privkey,
2595
+ kind: opt.udtKind,
2596
+ amount,
2597
+ typeArgs: (0, validator_1.validateUdtTypeArgs)(opt.udtKind, opt.typeArgs),
2598
+ });
2599
+ (0, link_1.logTxSuccess)(network, txHash, 'destroyed UDT');
1852
2600
  });
1853
2601
  }
1854
2602
 
@@ -2820,6 +3568,397 @@ class InitializationError extends Error {
2820
3568
  exports.InitializationError = InitializationError;
2821
3569
 
2822
3570
 
3571
+ /***/ }),
3572
+
3573
+ /***/ 77912:
3574
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
3575
+
3576
+ "use strict";
3577
+
3578
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3579
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3580
+ return new (P || (P = Promise))(function (resolve, reject) {
3581
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
3582
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
3583
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
3584
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
3585
+ });
3586
+ };
3587
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3588
+ return (mod && mod.__esModule) ? mod : { "default": mod };
3589
+ };
3590
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3591
+ exports.FORK_STATE_FILE = void 0;
3592
+ exports.getForkStatePath = getForkStatePath;
3593
+ exports.readForkState = readForkState;
3594
+ exports.writeForkState = writeForkState;
3595
+ exports.markForkFirstRunComplete = markForkFirstRunComplete;
3596
+ exports.detectSourceFromCkbToml = detectSourceFromCkbToml;
3597
+ exports.parseGenesisHashFromInitOutput = parseGenesisHashFromInitOutput;
3598
+ exports.parseGenesisHashFromListHashes = parseGenesisHashFromListHashes;
3599
+ exports.expectedGenesisHash = expectedGenesisHash;
3600
+ exports.patchDevSpecForFork = patchDevSpecForFork;
3601
+ exports.forkDevnet = forkDevnet;
3602
+ const child_process_1 = __nccwpck_require__(35317);
3603
+ const fs_1 = __importDefault(__nccwpck_require__(79896));
3604
+ const path_1 = __importDefault(__nccwpck_require__(16928));
3605
+ const toml_1 = __importDefault(__nccwpck_require__(2811));
3606
+ const setting_1 = __nccwpck_require__(25546);
3607
+ const install_1 = __nccwpck_require__(65611);
3608
+ const fs_2 = __nccwpck_require__(37293);
3609
+ const request_1 = __nccwpck_require__(65897);
3610
+ const logger_1 = __nccwpck_require__(65370);
3611
+ const const_1 = __nccwpck_require__(75587);
3612
+ exports.FORK_STATE_FILE = 'fork.json';
3613
+ // The official guide requires genesis_epoch_length to stay at 1743 for a
3614
+ // mainnet fork, and to fall back to the default 1000 for a testnet fork
3615
+ // (i.e. the key must be absent). Getting this wrong produces a different
3616
+ // genesis hash and the node refuses to start. See nervosnetwork/ckb#5205.
3617
+ const MAINNET_GENESIS_EPOCH_LENGTH = 1743;
3618
+ function getForkStatePath(configPath) {
3619
+ return path_1.default.join(configPath, exports.FORK_STATE_FILE);
3620
+ }
3621
+ function readForkState(configPath) {
3622
+ const statePath = getForkStatePath(configPath);
3623
+ let raw;
3624
+ try {
3625
+ raw = fs_1.default.readFileSync(statePath, 'utf8');
3626
+ }
3627
+ catch (_a) {
3628
+ return null;
3629
+ }
3630
+ try {
3631
+ const parsed = JSON.parse(raw);
3632
+ if (typeof parsed.source !== 'string' || typeof parsed.firstRunPending !== 'boolean') {
3633
+ return null;
3634
+ }
3635
+ return parsed;
3636
+ }
3637
+ catch (_b) {
3638
+ return null;
3639
+ }
3640
+ }
3641
+ function writeForkState(configPath, state) {
3642
+ const statePath = getForkStatePath(configPath);
3643
+ const tempPath = `${statePath}.tmp`;
3644
+ fs_1.default.writeFileSync(tempPath, JSON.stringify(state, null, 2));
3645
+ fs_1.default.renameSync(tempPath, statePath);
3646
+ }
3647
+ function markForkFirstRunComplete(configPath) {
3648
+ const state = readForkState(configPath);
3649
+ if (!state || !state.firstRunPending)
3650
+ return;
3651
+ writeForkState(configPath, Object.assign(Object.assign({}, state), { firstRunPending: false }));
3652
+ }
3653
+ function detectSourceFromCkbToml(ckbTomlContent) {
3654
+ let parsed;
3655
+ try {
3656
+ parsed = toml_1.default.parse(ckbTomlContent);
3657
+ }
3658
+ catch (_a) {
3659
+ return null;
3660
+ }
3661
+ const chain = parsed.chain;
3662
+ if (chain == null || typeof chain !== 'object')
3663
+ return null;
3664
+ const spec = chain.spec;
3665
+ if (spec == null || typeof spec !== 'object')
3666
+ return null;
3667
+ const bundled = spec.bundled;
3668
+ if (typeof bundled !== 'string')
3669
+ return null;
3670
+ if (bundled.includes('mainnet'))
3671
+ return 'mainnet';
3672
+ if (bundled.includes('testnet'))
3673
+ return 'testnet';
3674
+ return null;
3675
+ }
3676
+ function parseGenesisHashFromInitOutput(output) {
3677
+ const match = output.match(/Genesis Hash:\s*(0x[0-9a-fA-F]{64})/);
3678
+ return match ? match[1].toLowerCase() : null;
3679
+ }
3680
+ // `ckb list-hashes` prints TOML with a single table whose `genesis` field is
3681
+ // the chain's genesis hash.
3682
+ function parseGenesisHashFromListHashes(output) {
3683
+ const match = output.match(/^\s*genesis\s*=\s*"(0x[0-9a-fA-F]{64})"\s*$/m);
3684
+ return match ? match[1].toLowerCase() : null;
3685
+ }
3686
+ function expectedGenesisHash(source) {
3687
+ return source === 'mainnet' ? const_1.MAINNET_GENESIS_HASH : const_1.TESTNET_GENESIS_HASH;
3688
+ }
3689
+ // Patch the imported chain spec into a minable dev chain, following
3690
+ // https://docs.nervos.org/docs/node/devnet-from-existing-data
3691
+ function patchDevSpecForFork(spec, source) {
3692
+ var _a, _b;
3693
+ const pow = Object.assign(Object.assign({}, ((_a = spec.pow) !== null && _a !== void 0 ? _a : {})), { func: 'Dummy' });
3694
+ const params = Object.assign({}, ((_b = spec.params) !== null && _b !== void 0 ? _b : {}));
3695
+ params.cellbase_maturity = 0;
3696
+ params.permanent_difficulty_in_dummy = true;
3697
+ if (source === 'mainnet') {
3698
+ params.genesis_epoch_length = MAINNET_GENESIS_EPOCH_LENGTH;
3699
+ }
3700
+ else if (source === 'testnet') {
3701
+ // testnet derives genesis_epoch_length from the default (1000); a leftover
3702
+ // mainnet value here is exactly the nervosnetwork/ckb#5205 trap.
3703
+ delete params.genesis_epoch_length;
3704
+ }
3705
+ // custom specs keep whatever genesis_epoch_length they declared
3706
+ return Object.assign(Object.assign({}, spec), { pow, params });
3707
+ }
3708
+ function validateSourceDir(sourceDir) {
3709
+ if (!(0, fs_2.isFolderExists)(sourceDir)) {
3710
+ throw new Error(`Source directory does not exist: ${sourceDir}`);
3711
+ }
3712
+ if (!(0, fs_2.isFolderExists)(path_1.default.join(sourceDir, 'data', 'db'))) {
3713
+ throw new Error(`${sourceDir} does not look like a CKB node directory (missing data/db). ` +
3714
+ `Point --from at the directory the source node runs with (-C).`);
3715
+ }
3716
+ }
3717
+ // Best-effort detection of a running ckb process using the given directory.
3718
+ // Returns null when the check cannot be performed (Windows, no ps).
3719
+ function isCkbNodeRunningOn(dir) {
3720
+ if (process.platform === 'win32')
3721
+ return null;
3722
+ let processes = '';
3723
+ try {
3724
+ processes = (0, child_process_1.execSync)('ps -eo args', { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
3725
+ }
3726
+ catch (_a) {
3727
+ return null;
3728
+ }
3729
+ return processes.split('\n').some((line) => {
3730
+ var _a;
3731
+ const tokens = line.trim().split(/\s+/);
3732
+ // The program itself must be a ckb binary — a looser match would flag
3733
+ // unrelated processes that merely mention the directory.
3734
+ const program = path_1.default.basename((_a = tokens[0]) !== null && _a !== void 0 ? _a : '');
3735
+ if (program !== 'ckb' && program !== 'ckb.exe')
3736
+ return false;
3737
+ return tokens.includes('run') && line.includes(dir);
3738
+ });
3739
+ }
3740
+ function assertSourceNodeStopped(sourceDir) {
3741
+ const running = isCkbNodeRunningOn(sourceDir);
3742
+ if (running === null) {
3743
+ logger_1.logger.warn('Make sure the source CKB node is stopped; copying a live database produces corrupted data.');
3744
+ return;
3745
+ }
3746
+ if (running) {
3747
+ throw new Error(`A CKB node appears to be running on ${sourceDir}. Stop it first; ` +
3748
+ `copying a live database produces corrupted data.`);
3749
+ }
3750
+ }
3751
+ function assertOffckbDevnetStopped() {
3752
+ const settings = (0, setting_1.readSettings)();
3753
+ // Foreground `offckb node` (no daemon pid file): look for the ckb process.
3754
+ if (isCkbNodeRunningOn(settings.devnet.configPath) === true) {
3755
+ throw new Error('An offckb devnet node appears to be running. Stop it before forking.');
3756
+ }
3757
+ // Daemon mode: check the pid file.
3758
+ const pidFile = path_1.default.join(settings.devnet.dataPath, 'logs', 'daemon.pid');
3759
+ let raw;
3760
+ try {
3761
+ raw = fs_1.default.readFileSync(pidFile, 'utf8').trim();
3762
+ }
3763
+ catch (_a) {
3764
+ return;
3765
+ }
3766
+ let pid = Number(raw);
3767
+ if (!Number.isInteger(pid) || pid <= 0) {
3768
+ try {
3769
+ pid = Number(JSON.parse(raw).pid);
3770
+ }
3771
+ catch (_b) {
3772
+ return;
3773
+ }
3774
+ }
3775
+ if (!Number.isInteger(pid) || pid <= 0)
3776
+ return;
3777
+ let alive = true;
3778
+ try {
3779
+ process.kill(pid, 0);
3780
+ }
3781
+ catch (_c) {
3782
+ alive = false; // ESRCH: stale pid file, no daemon running
3783
+ }
3784
+ if (alive) {
3785
+ throw new Error(`The offckb devnet daemon is running (PID ${pid}). Stop it first with: offckb node stop`);
3786
+ }
3787
+ }
3788
+ function resolveSpecFile(options, source, ckbVersion) {
3789
+ return __awaiter(this, void 0, void 0, function* () {
3790
+ if (options.specFile) {
3791
+ const specFile = path_1.default.resolve(options.specFile);
3792
+ if (!fs_1.default.existsSync(specFile)) {
3793
+ throw new Error(`Spec file not found: ${specFile}`);
3794
+ }
3795
+ return specFile;
3796
+ }
3797
+ const cacheDir = path_1.default.join(setting_1.cachePath, 'specs', ckbVersion);
3798
+ const cachedSpec = path_1.default.join(cacheDir, `${source}.toml`);
3799
+ if (fs_1.default.existsSync(cachedSpec)) {
3800
+ logger_1.logger.debug(`Using cached ${source} spec: ${cachedSpec}`);
3801
+ return cachedSpec;
3802
+ }
3803
+ const url = `https://raw.githubusercontent.com/nervosnetwork/ckb/v${ckbVersion}/resource/specs/${source}.toml`;
3804
+ logger_1.logger.info(`Downloading ${source} chain spec from ${url} ..`);
3805
+ try {
3806
+ const response = yield request_1.Request.send(url);
3807
+ const content = yield response.text();
3808
+ fs_1.default.mkdirSync(cacheDir, { recursive: true });
3809
+ fs_1.default.writeFileSync(cachedSpec, content);
3810
+ return cachedSpec;
3811
+ }
3812
+ catch (error) {
3813
+ throw new Error(`Failed to download the ${source} chain spec for CKB v${ckbVersion}: ${error.message}. ` +
3814
+ `Pass a local copy with --spec-file.`);
3815
+ }
3816
+ });
3817
+ }
3818
+ function copySourceData(sourceDir, configPath) {
3819
+ const sourceData = path_1.default.join(sourceDir, 'data');
3820
+ const targetData = path_1.default.join(configPath, 'data');
3821
+ logger_1.logger.info(`Copying chain data from ${sourceData} to ${targetData} ..`);
3822
+ logger_1.logger.info('This can take a while for large chains.');
3823
+ fs_1.default.mkdirSync(configPath, { recursive: true });
3824
+ // Full copy on purpose: never hardlink — RocksDB appends to WAL/MANIFEST in
3825
+ // place, and linked files would corrupt the source chain.
3826
+ fs_1.default.cpSync(sourceData, targetData, { recursive: true });
3827
+ }
3828
+ function runCkbInit(ckbBinPath, configPath, specFile) {
3829
+ // argv form, never a shell: --spec-file is user input and quote-wrapping
3830
+ // alone does not stop shell expansion (command substitution still runs
3831
+ // inside double quotes).
3832
+ const args = ['init', '-C', configPath, '--chain', 'dev', '--import-spec', specFile, '--force'];
3833
+ logger_1.logger.debug(`Running: ${ckbBinPath} ${args.join(' ')}`);
3834
+ return (0, child_process_1.execFileSync)(ckbBinPath, args, { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
3835
+ }
3836
+ // Best-effort read of the source directory's own chain identity. `ckb
3837
+ // list-hashes` resolves the chain spec of the given config dir (no database
3838
+ // access needed), which tells us which chain the source node was configured
3839
+ // for. Returns null when the source is not a standard config dir.
3840
+ function readSourceGenesisHash(ckbBinPath, sourceDir) {
3841
+ try {
3842
+ const output = (0, child_process_1.execFileSync)(ckbBinPath, ['list-hashes', '-C', sourceDir], {
3843
+ encoding: 'utf8',
3844
+ maxBuffer: 16 * 1024 * 1024,
3845
+ stdio: ['ignore', 'pipe', 'ignore'],
3846
+ });
3847
+ return parseGenesisHashFromListHashes(output);
3848
+ }
3849
+ catch (_a) {
3850
+ return null;
3851
+ }
3852
+ }
3853
+ // Overwrite the ckb-init-generated configs with offckb's own devnet configs so
3854
+ // the fork behaves like a normal offckb devnet (miner account as block
3855
+ // assembler, RPC on 8114 with the Indexer module, proxy on 28114). The
3856
+ // imported specs/dev.toml is kept.
3857
+ function alignConfigsWithOffckb(configPath) {
3858
+ const settings = (0, setting_1.readSettings)();
3859
+ const devnetSourcePath = path_1.default.resolve(setting_1.packageRootPath, './ckb/devnet');
3860
+ fs_1.default.copyFileSync(path_1.default.join(devnetSourcePath, 'ckb.toml'), path_1.default.join(configPath, 'ckb.toml'));
3861
+ fs_1.default.copyFileSync(path_1.default.join(devnetSourcePath, 'default.db-options'), path_1.default.join(configPath, 'default.db-options'));
3862
+ const minerToml = fs_1.default.readFileSync(path_1.default.join(devnetSourcePath, 'ckb-miner.toml'), 'utf8');
3863
+ fs_1.default.writeFileSync(path_1.default.join(configPath, 'ckb-miner.toml'), minerToml.replace('http://ckb:8114/', settings.devnet.rpcUrl));
3864
+ }
3865
+ function forkDevnet(options) {
3866
+ return __awaiter(this, void 0, void 0, function* () {
3867
+ var _a, _b;
3868
+ const settings = (0, setting_1.readSettings)();
3869
+ const configPath = settings.devnet.configPath;
3870
+ const ckbVersion = settings.bins.defaultCKBVersion;
3871
+ const sourceDir = path_1.default.resolve(options.from);
3872
+ validateSourceDir(sourceDir);
3873
+ assertSourceNodeStopped(sourceDir);
3874
+ assertOffckbDevnetStopped();
3875
+ if ((0, fs_2.isFolderExists)(configPath)) {
3876
+ if (!options.force) {
3877
+ throw new Error(`A devnet already exists at ${configPath}. Re-run with --force to replace it, ` +
3878
+ `or reset it first with: offckb clean`);
3879
+ }
3880
+ logger_1.logger.info(`Removing existing devnet at ${configPath} ..`);
3881
+ fs_1.default.rmSync(configPath, { recursive: true, force: true });
3882
+ }
3883
+ // Identify the source chain: explicit flag > ckb.toml bundled spec.
3884
+ let source = (_a = options.source) !== null && _a !== void 0 ? _a : null;
3885
+ if (source == null) {
3886
+ const sourceCkbToml = path_1.default.join(sourceDir, 'ckb.toml');
3887
+ if (fs_1.default.existsSync(sourceCkbToml)) {
3888
+ source = detectSourceFromCkbToml(fs_1.default.readFileSync(sourceCkbToml, 'utf8'));
3889
+ }
3890
+ }
3891
+ if (source == null && !options.specFile) {
3892
+ throw new Error('Could not identify the source chain from the source directory. ' +
3893
+ 'Pass --source mainnet|testnet, or provide the chain spec with --spec-file.');
3894
+ }
3895
+ yield (0, install_1.installCKBBinary)(ckbVersion);
3896
+ const ckbBinPath = (0, setting_1.getCKBBinaryPath)(ckbVersion);
3897
+ try {
3898
+ // Inside the try so a failed copy (disk full, permissions, I/O) gets the
3899
+ // same rollback: a partial configPath would otherwise block the next
3900
+ // attempt as an "existing devnet".
3901
+ copySourceData(sourceDir, configPath);
3902
+ const specFile = yield resolveSpecFile(options, source !== null && source !== void 0 ? source : 'mainnet', ckbVersion);
3903
+ const initOutput = runCkbInit(ckbBinPath, configPath, specFile);
3904
+ const genesisHash = parseGenesisHashFromInitOutput(initOutput);
3905
+ if (!genesisHash) {
3906
+ throw new Error(`Could not parse the genesis hash from ckb init output:\n${initOutput}`);
3907
+ }
3908
+ // A custom spec may still be a well-known chain; let the chain data
3909
+ // self-identify via its genesis hash.
3910
+ if (source == null) {
3911
+ source = (_b = (0, const_1.identifyPublicChainByGenesisHash)(genesisHash)) !== null && _b !== void 0 ? _b : 'custom';
3912
+ }
3913
+ if (source !== 'custom') {
3914
+ const expected = expectedGenesisHash(source);
3915
+ if (genesisHash !== expected) {
3916
+ throw new Error(`Genesis hash mismatch: expected ${expected} for ${source}, got ${genesisHash}. ` +
3917
+ `This usually means the chain spec does not match the source data. ` +
3918
+ `(Importing a testnet spec with a CKB older than v0.207.0 sets a wrong genesis_epoch_length, ` +
3919
+ `see nervosnetwork/ckb#5205.)`);
3920
+ }
3921
+ }
3922
+ // The genesis above comes from the imported spec alone — it cannot see
3923
+ // that --source/--spec-file contradicts the copied data (e.g. a mainnet
3924
+ // spec over testnet data would pass and only fail when the node boots).
3925
+ // Cross-check the source directory's own configured genesis and reject
3926
+ // mismatches now. Skipped (null) when the source is not a standard
3927
+ // config dir; the node's boot-time genesis check remains the backstop.
3928
+ const sourceGenesisHash = readSourceGenesisHash(ckbBinPath, sourceDir);
3929
+ if (sourceGenesisHash && sourceGenesisHash !== genesisHash) {
3930
+ throw new Error(`The source directory is configured for a different chain (genesis ${sourceGenesisHash}) ` +
3931
+ `than the imported spec (genesis ${genesisHash}). Pass a matching --source or --spec-file.`);
3932
+ }
3933
+ const devSpecPath = path_1.default.join(configPath, 'specs', 'dev.toml');
3934
+ const spec = toml_1.default.parse(fs_1.default.readFileSync(devSpecPath, 'utf8'));
3935
+ const patched = patchDevSpecForFork(spec, source);
3936
+ const tempSpecPath = `${devSpecPath}.tmp`;
3937
+ fs_1.default.writeFileSync(tempSpecPath, toml_1.default.stringify(patched));
3938
+ fs_1.default.renameSync(tempSpecPath, devSpecPath);
3939
+ alignConfigsWithOffckb(configPath);
3940
+ const state = {
3941
+ source,
3942
+ sourceDir,
3943
+ ckbVersion,
3944
+ genesisHash,
3945
+ forkedAt: new Date().toISOString(),
3946
+ firstRunPending: true,
3947
+ };
3948
+ writeForkState(configPath, state);
3949
+ logger_1.logger.success(`Devnet forked from ${sourceDir} (${source}, genesis ${genesisHash}).`);
3950
+ logger_1.logger.info('Start it with: offckb node');
3951
+ logger_1.logger.info('The first run applies --skip-spec-check --overwrite-spec automatically.');
3952
+ }
3953
+ catch (error) {
3954
+ // Leave no half-forked devnet behind.
3955
+ fs_1.default.rmSync(configPath, { recursive: true, force: true });
3956
+ throw error;
3957
+ }
3958
+ });
3959
+ }
3960
+
3961
+
2823
3962
  /***/ }),
2824
3963
 
2825
3964
  /***/ 1360:
@@ -2934,7 +4073,7 @@ const fs = __importStar(__nccwpck_require__(79896));
2934
4073
  const path = __importStar(__nccwpck_require__(16928));
2935
4074
  const semver_1 = __importDefault(__nccwpck_require__(89939));
2936
4075
  const os_1 = __importDefault(__nccwpck_require__(70857));
2937
- const adm_zip_1 = __importDefault(__nccwpck_require__(83746));
4076
+ const adm_zip_1 = __importDefault(__nccwpck_require__(3858));
2938
4077
  const tar = __importStar(__nccwpck_require__(80228));
2939
4078
  const request_1 = __nccwpck_require__(65897);
2940
4079
  const setting_1 = __nccwpck_require__(25546);
@@ -3154,7 +4293,8 @@ function buildDownloadUrl(version, opt = {}) {
3154
4293
  "use strict";
3155
4294
 
3156
4295
  Object.defineProperty(exports, "__esModule", ({ value: true }));
3157
- exports.TYPE_ID_SCRIPT = void 0;
4296
+ exports.TESTNET_GENESIS_HASH = exports.MAINNET_GENESIS_HASH = exports.TYPE_ID_SCRIPT = void 0;
4297
+ exports.identifyPublicChainByGenesisHash = identifyPublicChainByGenesisHash;
3158
4298
  exports.TYPE_ID_SCRIPT = {
3159
4299
  name: 'type_id',
3160
4300
  script: {
@@ -3163,6 +4303,18 @@ exports.TYPE_ID_SCRIPT = {
3163
4303
  cellDeps: [],
3164
4304
  },
3165
4305
  };
4306
+ // Well-known genesis block hashes, used to identify which chain a devnet
4307
+ // actually runs (a forked devnet carries the source chain's genesis).
4308
+ // https://github.com/nervosnetwork/ckb/tree/master/resource/specs
4309
+ exports.MAINNET_GENESIS_HASH = '0x92b197aa1fba0f63633922c61c92375c9c074a93e85963554f5499fe1450d0e5';
4310
+ exports.TESTNET_GENESIS_HASH = '0x10639e0895502b5688a6be8cf69460d76541bfa4821629d86d62ba0aae3f9606';
4311
+ function identifyPublicChainByGenesisHash(genesisHash) {
4312
+ if (genesisHash === exports.MAINNET_GENESIS_HASH)
4313
+ return 'mainnet';
4314
+ if (genesisHash === exports.TESTNET_GENESIS_HASH)
4315
+ return 'testnet';
4316
+ return null;
4317
+ }
3166
4318
 
3167
4319
 
3168
4320
  /***/ }),
@@ -3250,30 +4402,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3250
4402
  return (mod && mod.__esModule) ? mod : { "default": mod };
3251
4403
  };
3252
4404
  Object.defineProperty(exports, "__esModule", ({ value: true }));
4405
+ exports.resolveDevnetSystemScripts = resolveDevnetSystemScripts;
3253
4406
  exports.getDevnetSystemScriptsFromListHashes = getDevnetSystemScriptsFromListHashes;
3254
4407
  exports.toCCCKnownScripts = toCCCKnownScripts;
3255
4408
  exports.buildCCCDevnetKnownScripts = buildCCCDevnetKnownScripts;
4409
+ exports.buildDevnetCCCClient = buildDevnetCCCClient;
3256
4410
  const core_1 = __nccwpck_require__(39842);
3257
4411
  const setting_1 = __nccwpck_require__(25546);
3258
- const system_scripts_1 = __nccwpck_require__(86198);
3259
4412
  const list_hashes_1 = __nccwpck_require__(31603);
3260
4413
  const logger_1 = __nccwpck_require__(65370);
3261
4414
  const const_1 = __nccwpck_require__(75587);
4415
+ const public_1 = __nccwpck_require__(84535);
3262
4416
  const toml_1 = __importDefault(__nccwpck_require__(2811));
3263
4417
  const util_1 = __nccwpck_require__(58642);
3264
- function getDevnetSystemScriptsFromListHashes() {
4418
+ function buildSystemScriptsFromSpecHashes(chainSpecHashes) {
3265
4419
  var _a;
3266
- const settings = (0, setting_1.readSettings)();
3267
- const listHashesString = (0, list_hashes_1.getDevnetListHashes)(settings.bins.defaultCKBVersion);
3268
- if (!listHashesString) {
3269
- logger_1.logger.info(`list-hashes not found!`);
3270
- return null;
3271
- }
3272
- const listHashes = toml_1.default.parse(listHashesString);
3273
- const chainSpecHashes = Object.values(listHashes)[0];
3274
- if (chainSpecHashes == null) {
3275
- throw new Error(`invalid chain spec hashes file ${listHashesString}`);
3276
- }
3277
4420
  const systemScriptArray = chainSpecHashes.system_cells
3278
4421
  .map((cell) => {
3279
4422
  // Extract the file name from the path using the helper function
@@ -3286,7 +4429,7 @@ function getDevnetSystemScriptsFromListHashes() {
3286
4429
  txHash: chainSpecHashes.dep_groups[depGroupIndex].tx_hash,
3287
4430
  index: chainSpecHashes.dep_groups[depGroupIndex].index,
3288
4431
  };
3289
- const scriptInfo = (0, system_scripts_1.systemCellToScriptInfo)({ cell, depType, depGroup });
4432
+ const scriptInfo = (0, util_1.systemCellToScriptInfo)({ cell, depType, depGroup });
3290
4433
  return {
3291
4434
  name,
3292
4435
  file: cell.path,
@@ -3306,6 +4449,46 @@ function getDevnetSystemScriptsFromListHashes() {
3306
4449
  systemScripts.type_id = const_1.TYPE_ID_SCRIPT;
3307
4450
  return systemScripts;
3308
4451
  }
4452
+ // `ckb list-hashes` only reports what the chain spec declares in genesis.
4453
+ // Post-genesis deployments (sudt/xudt/omnilock/spore on mainnet and testnet)
4454
+ // can never appear there, so for a forked devnet we fill the gaps from the
4455
+ // well-known static records of the chain the genesis belongs to. Genesis
4456
+ // scripts always come from list-hashes — the chain's own spec wins.
4457
+ function supplementFromStaticRecord(scripts, staticRecord) {
4458
+ for (const [name, script] of Object.entries(staticRecord)) {
4459
+ const key = name;
4460
+ if (scripts[key] == null && script != null) {
4461
+ // deep clone so callers can never mutate the shared static record
4462
+ scripts[key] = JSON.parse(JSON.stringify(script));
4463
+ }
4464
+ }
4465
+ }
4466
+ function resolveDevnetSystemScripts() {
4467
+ const settings = (0, setting_1.readSettings)();
4468
+ const listHashesString = (0, list_hashes_1.getDevnetListHashes)(settings.bins.defaultCKBVersion);
4469
+ if (!listHashesString) {
4470
+ logger_1.logger.info(`list-hashes not found!`);
4471
+ return null;
4472
+ }
4473
+ const listHashes = toml_1.default.parse(listHashesString);
4474
+ const chainSpecHashes = Object.values(listHashes)[0];
4475
+ if (chainSpecHashes == null) {
4476
+ throw new Error(`invalid chain spec hashes file ${listHashesString}`);
4477
+ }
4478
+ const scripts = buildSystemScriptsFromSpecHashes(chainSpecHashes);
4479
+ const forkedFrom = (0, const_1.identifyPublicChainByGenesisHash)(chainSpecHashes.genesis);
4480
+ if (forkedFrom === 'mainnet') {
4481
+ supplementFromStaticRecord(scripts, public_1.MAINNET_SYSTEM_SCRIPTS);
4482
+ }
4483
+ else if (forkedFrom === 'testnet') {
4484
+ supplementFromStaticRecord(scripts, public_1.TESTNET_SYSTEM_SCRIPTS);
4485
+ }
4486
+ return { scripts, forkedFrom, genesisHash: chainSpecHashes.genesis };
4487
+ }
4488
+ function getDevnetSystemScriptsFromListHashes() {
4489
+ var _a, _b;
4490
+ return (_b = (_a = resolveDevnetSystemScripts()) === null || _a === void 0 ? void 0 : _a.scripts) !== null && _b !== void 0 ? _b : null;
4491
+ }
3309
4492
  function toCCCKnownScripts(scripts) {
3310
4493
  const DEVNET_SCRIPTS = {
3311
4494
  [core_1.KnownScript.Secp256k1Blake160]: scripts.secp256k1_blake160_sighash_all.script,
@@ -3333,6 +4516,19 @@ function buildCCCDevnetKnownScripts() {
3333
4516
  const devnetKnownScripts = toCCCKnownScripts(devnetSystemScripts);
3334
4517
  return devnetKnownScripts;
3335
4518
  }
4519
+ // Build the ccc client for the devnet. A forked devnet carries the source
4520
+ // chain's genesis, so a mainnet fork must use the `ckb` address prefix
4521
+ // (ClientPublicMainnet); everything else uses the `ckt` prefix. Known scripts
4522
+ // always come from the fork-aware resolver above.
4523
+ function buildDevnetCCCClient(url, fallbacks = []) {
4524
+ const resolved = resolveDevnetSystemScripts();
4525
+ if (resolved == null) {
4526
+ throw new Error('can not getSystemScriptsFromListHashes in devnet');
4527
+ }
4528
+ const scripts = toCCCKnownScripts(resolved.scripts);
4529
+ const ClientCtor = resolved.forkedFrom === 'mainnet' ? core_1.ccc.ClientPublicMainnet : core_1.ccc.ClientPublicTestnet;
4530
+ return new ClientCtor({ url, scripts, fallbacks });
4531
+ }
3336
4532
 
3337
4533
 
3338
4534
  /***/ }),
@@ -3717,6 +4913,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
3717
4913
  exports.readDeployedScriptInfoFrom = readDeployedScriptInfoFrom;
3718
4914
  exports.getScriptInfoFrom = getScriptInfoFrom;
3719
4915
  exports.extractScriptNameFromPath = extractScriptNameFromPath;
4916
+ exports.systemCellToScriptInfo = systemCellToScriptInfo;
3720
4917
  const fs = __importStar(__nccwpck_require__(79896));
3721
4918
  const migration_1 = __nccwpck_require__(76679);
3722
4919
  const path_1 = __importDefault(__nccwpck_require__(16928));
@@ -3807,6 +5004,57 @@ function extractScriptNameFromPath(pathString) {
3807
5004
  // On Windows, it handles backslashes; on Unix, it handles forward slashes
3808
5005
  return path_1.default.basename(pathString);
3809
5006
  }
5007
+ function systemCellToScriptInfo({ cell, depType, depGroup, extraCellDeps, }) {
5008
+ // todo: we left the type in cellDepsInfo since it requires async fetching and
5009
+ // chain running to get the full type script of the type-id deps.
5010
+ // Also, in devnet there is no real need to auto upgrade the system scripts with type-id
5011
+ if (depType === 'code') {
5012
+ let cellDeps = [
5013
+ {
5014
+ cellDep: {
5015
+ outPoint: {
5016
+ txHash: cell.tx_hash,
5017
+ index: cell.index,
5018
+ },
5019
+ depType,
5020
+ },
5021
+ },
5022
+ ];
5023
+ if (extraCellDeps && extraCellDeps.length > 0) {
5024
+ cellDeps = [...extraCellDeps, ...cellDeps];
5025
+ }
5026
+ return {
5027
+ codeHash: (cell.type_hash || cell.data_hash),
5028
+ hashType: cell.type_hash ? 'type' : 'data2',
5029
+ cellDeps,
5030
+ };
5031
+ }
5032
+ if (depType === 'depGroup') {
5033
+ if (!depGroup) {
5034
+ throw new Error('require depGroup info since the dep type is depGroup');
5035
+ }
5036
+ let cellDeps = [
5037
+ {
5038
+ cellDep: {
5039
+ outPoint: {
5040
+ txHash: depGroup.txHash,
5041
+ index: depGroup.index,
5042
+ },
5043
+ depType,
5044
+ },
5045
+ },
5046
+ ];
5047
+ if (extraCellDeps && extraCellDeps.length > 0) {
5048
+ cellDeps = [...extraCellDeps, ...cellDeps];
5049
+ }
5050
+ return {
5051
+ codeHash: (cell.type_hash || cell.data_hash),
5052
+ hashType: cell.type_hash ? 'type' : 'data2',
5053
+ cellDeps,
5054
+ };
5055
+ }
5056
+ throw new Error(`unknown DepType ${depType}`);
5057
+ }
3810
5058
 
3811
5059
 
3812
5060
  /***/ }),
@@ -3827,18 +5075,36 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
3827
5075
  step((generator = generator.apply(thisArg, _arguments || [])).next());
3828
5076
  });
3829
5077
  };
5078
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
5079
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
5080
+ var m = o[Symbol.asyncIterator], i;
5081
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
5082
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
5083
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
5084
+ };
3830
5085
  Object.defineProperty(exports, "__esModule", ({ value: true }));
3831
5086
  exports.CKB = exports.CKBProps = void 0;
3832
5087
  const core_1 = __nccwpck_require__(39842);
3833
5088
  const validator_1 = __nccwpck_require__(39534);
3834
5089
  const network_1 = __nccwpck_require__(5370);
3835
5090
  const private_1 = __nccwpck_require__(5835);
5091
+ const public_1 = __nccwpck_require__(84535);
3836
5092
  const migration_1 = __nccwpck_require__(76679);
3837
5093
  const base_1 = __nccwpck_require__(69951);
3838
5094
  const logger_1 = __nccwpck_require__(65370);
5095
+ const DEFAULT_UDT_SCAN_MAX_CELLS = 1000;
5096
+ const DEFAULT_UDT_DESTROY_MAX_INPUT_CELLS = 100;
3839
5097
  class CKBProps {
3840
5098
  }
3841
5099
  exports.CKBProps = CKBProps;
5100
+ function readUdtBalance(outputData) {
5101
+ try {
5102
+ return BigInt(core_1.ccc.udtBalanceFrom(outputData));
5103
+ }
5104
+ catch (_a) {
5105
+ return null;
5106
+ }
5107
+ }
3842
5108
  class CKB {
3843
5109
  constructor({ network = base_1.Network.devnet, feeRate = 1000, isEnableProxyRpc = true }) {
3844
5110
  if (!(0, validator_1.isValidNetworkString)(network)) {
@@ -3856,11 +5122,7 @@ class CKB {
3856
5122
  url: network_1.networks.testnet.proxy_rpc_url,
3857
5123
  fallbacks: [network_1.networks.testnet.rpc_url],
3858
5124
  }) // we keep the fallbacks in case the proxy rpc is not started
3859
- : new core_1.ccc.ClientPublicTestnet({
3860
- url: network_1.networks.devnet.proxy_rpc_url,
3861
- scripts: (0, private_1.buildCCCDevnetKnownScripts)(),
3862
- fallbacks: [network_1.networks.devnet.rpc_url],
3863
- });
5125
+ : (0, private_1.buildDevnetCCCClient)(network_1.networks.devnet.proxy_rpc_url, [network_1.networks.devnet.rpc_url]);
3864
5126
  }
3865
5127
  else {
3866
5128
  this.client =
@@ -3874,11 +5136,7 @@ class CKB {
3874
5136
  url: network_1.networks.testnet.rpc_url,
3875
5137
  fallbacks: [],
3876
5138
  }) // pass it to avoid using websocket and fallback RPCs
3877
- : new core_1.ccc.ClientPublicTestnet({
3878
- url: network_1.networks.devnet.rpc_url,
3879
- scripts: (0, private_1.buildCCCDevnetKnownScripts)(),
3880
- fallbacks: [], // pass it to avoid using websocket and fallback RPCs
3881
- });
5139
+ : (0, private_1.buildDevnetCCCClient)(network_1.networks.devnet.rpc_url);
3882
5140
  }
3883
5141
  }
3884
5142
  buildSigner(privateKey) {
@@ -3972,6 +5230,286 @@ class CKB {
3972
5230
  return txHash;
3973
5231
  });
3974
5232
  }
5233
+ getSystemScripts() {
5234
+ if (this.network === base_1.Network.mainnet) {
5235
+ return public_1.MAINNET_SYSTEM_SCRIPTS;
5236
+ }
5237
+ if (this.network === base_1.Network.testnet) {
5238
+ return public_1.TESTNET_SYSTEM_SCRIPTS;
5239
+ }
5240
+ const scripts = (0, private_1.getDevnetSystemScriptsFromListHashes)();
5241
+ if (!scripts) {
5242
+ throw new Error(`Failed to load devnet system scripts`);
5243
+ }
5244
+ return scripts;
5245
+ }
5246
+ buildUdtTypeScript(kind, args) {
5247
+ return __awaiter(this, void 0, void 0, function* () {
5248
+ if (kind === 'xudt') {
5249
+ return core_1.ccc.Script.fromKnownScript(this.client, core_1.ccc.KnownScript.XUdt, args);
5250
+ }
5251
+ const scriptInfo = yield this.getUdtScriptInfo(kind);
5252
+ return core_1.ccc.Script.from({
5253
+ codeHash: scriptInfo.codeHash,
5254
+ hashType: scriptInfo.hashType,
5255
+ args,
5256
+ });
5257
+ });
5258
+ }
5259
+ getUdtScriptInfo(kind) {
5260
+ return __awaiter(this, void 0, void 0, function* () {
5261
+ var _a;
5262
+ if (kind === 'xudt') {
5263
+ return this.client.getKnownScript(core_1.ccc.KnownScript.XUdt);
5264
+ }
5265
+ const systemScripts = this.getSystemScripts();
5266
+ const sudtScript = (_a = systemScripts.sudt) === null || _a === void 0 ? void 0 : _a.script;
5267
+ if (!sudtScript) {
5268
+ throw new Error(`SUDT script not found on ${this.network}`);
5269
+ }
5270
+ return sudtScript;
5271
+ });
5272
+ }
5273
+ detectUdtBalances(address_1) {
5274
+ return __awaiter(this, arguments, void 0, function* (address, { maxCells = DEFAULT_UDT_SCAN_MAX_CELLS } = {}) {
5275
+ const lock = (yield core_1.ccc.Address.fromString(address, this.client)).script;
5276
+ const sudtScriptInfo = yield this.getUdtScriptInfo('sudt').catch(() => null);
5277
+ const xudtScriptInfo = yield this.getUdtScriptInfo('xudt').catch(() => null);
5278
+ const balances = new Map();
5279
+ let scanned = 0;
5280
+ const scan = (scriptInfo, kind) => __awaiter(this, void 0, void 0, function* () {
5281
+ var _a, e_1, _b, _c;
5282
+ var _d, _e;
5283
+ try {
5284
+ for (var _f = true, _g = __asyncValues(this.client.findCells({
5285
+ script: {
5286
+ codeHash: scriptInfo.codeHash,
5287
+ hashType: scriptInfo.hashType,
5288
+ args: '0x',
5289
+ },
5290
+ scriptType: 'type',
5291
+ scriptSearchMode: 'prefix',
5292
+ filter: { script: lock },
5293
+ withData: true,
5294
+ }, 'asc')), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
5295
+ _c = _h.value;
5296
+ _f = false;
5297
+ const cell = _c;
5298
+ if (scanned >= maxCells) {
5299
+ logger_1.logger.warn(`UDT balance scan stopped after ${maxCells} cells; balances may be incomplete`);
5300
+ break;
5301
+ }
5302
+ scanned++;
5303
+ const type = cell.cellOutput.type;
5304
+ if (!type) {
5305
+ continue;
5306
+ }
5307
+ const cellBalance = readUdtBalance(cell.outputData);
5308
+ if (cellBalance == null) {
5309
+ logger_1.logger.debug(`Skipping corrupted UDT cell ${(_d = cell.outPoint) === null || _d === void 0 ? void 0 : _d.txHash}:${(_e = cell.outPoint) === null || _e === void 0 ? void 0 : _e.index}`);
5310
+ continue;
5311
+ }
5312
+ const key = `${kind}:${type.codeHash}:${type.hashType}:${type.args}`;
5313
+ const entry = balances.get(key);
5314
+ if (entry) {
5315
+ entry.balance += cellBalance;
5316
+ }
5317
+ else {
5318
+ balances.set(key, {
5319
+ kind,
5320
+ codeHash: type.codeHash,
5321
+ hashType: String(type.hashType),
5322
+ args: type.args,
5323
+ balance: cellBalance,
5324
+ });
5325
+ }
5326
+ }
5327
+ }
5328
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
5329
+ finally {
5330
+ try {
5331
+ if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
5332
+ }
5333
+ finally { if (e_1) throw e_1.error; }
5334
+ }
5335
+ });
5336
+ if (sudtScriptInfo) {
5337
+ yield scan(sudtScriptInfo, 'sudt');
5338
+ }
5339
+ if (xudtScriptInfo) {
5340
+ yield scan(xudtScriptInfo, 'xudt');
5341
+ }
5342
+ return Array.from(balances.values()).map((item) => (Object.assign(Object.assign({}, item), { balance: item.balance.toString() })));
5343
+ });
5344
+ }
5345
+ udtBalance(address_1, udtType_1) {
5346
+ return __awaiter(this, arguments, void 0, function* (address, udtType, { maxCells = DEFAULT_UDT_SCAN_MAX_CELLS } = {}) {
5347
+ var _a, e_2, _b, _c;
5348
+ const lock = (yield core_1.ccc.Address.fromString(address, this.client)).script;
5349
+ let balance = BigInt(0);
5350
+ let scanned = 0;
5351
+ try {
5352
+ for (var _d = true, _e = __asyncValues(this.client.findCellsByLock(lock, udtType, true)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
5353
+ _c = _f.value;
5354
+ _d = false;
5355
+ const cell = _c;
5356
+ scanned++;
5357
+ if (scanned > maxCells) {
5358
+ logger_1.logger.warn(`UDT balance scan stopped after ${maxCells} cells; balances may be incomplete`);
5359
+ break;
5360
+ }
5361
+ const cellBalance = readUdtBalance(cell.outputData);
5362
+ if (cellBalance != null) {
5363
+ balance += cellBalance;
5364
+ }
5365
+ }
5366
+ }
5367
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
5368
+ finally {
5369
+ try {
5370
+ if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
5371
+ }
5372
+ finally { if (e_2) throw e_2.error; }
5373
+ }
5374
+ return balance.toString();
5375
+ });
5376
+ }
5377
+ udtTransfer(_a) {
5378
+ return __awaiter(this, arguments, void 0, function* ({ privateKey, toAddress, amount, udtType, kind }) {
5379
+ const signer = this.buildSigner(privateKey);
5380
+ const to = yield core_1.ccc.Address.fromString(toAddress, this.client);
5381
+ const amountBigInt = (0, validator_1.validateUdtAmount)(amount);
5382
+ const outputsData = [core_1.ccc.hexFrom(core_1.ccc.numToBytes(amountBigInt, 16))];
5383
+ const tx = core_1.ccc.Transaction.from({
5384
+ outputs: [
5385
+ {
5386
+ lock: to.script,
5387
+ type: udtType,
5388
+ capacity: core_1.ccc.fixedPointFrom(0),
5389
+ },
5390
+ ],
5391
+ outputsData,
5392
+ });
5393
+ const scriptInfo = yield this.getUdtScriptInfo(kind);
5394
+ tx.addCellDeps(scriptInfo.cellDeps.map((dep) => dep.cellDep));
5395
+ yield tx.completeInputsByUdt(signer, udtType);
5396
+ const inputsUdtBalance = yield tx.getInputsUdtBalance(this.client, udtType);
5397
+ const outputsUdtBalance = tx.getOutputsUdtBalance(udtType);
5398
+ const changeAmount = inputsUdtBalance - outputsUdtBalance;
5399
+ if (changeAmount > BigInt(0)) {
5400
+ const from = yield signer.getAddressObjSecp256k1();
5401
+ tx.outputs.push(core_1.ccc.CellOutput.from({
5402
+ lock: from.script,
5403
+ type: udtType,
5404
+ capacity: core_1.ccc.fixedPointFrom(0),
5405
+ }, core_1.ccc.hexFrom(core_1.ccc.numToBytes(changeAmount, 16))));
5406
+ tx.outputsData.push(core_1.ccc.hexFrom(core_1.ccc.numToBytes(changeAmount, 16)));
5407
+ }
5408
+ yield tx.completeFeeBy(signer, this.feeRate);
5409
+ const txHash = yield signer.sendTransaction(tx);
5410
+ return txHash;
5411
+ });
5412
+ }
5413
+ udtIssue(_a) {
5414
+ return __awaiter(this, arguments, void 0, function* ({ privateKey, kind, amount, typeArgs, toAddress }) {
5415
+ const signer = this.buildSigner(privateKey);
5416
+ const signerAddress = yield signer.getAddressObjSecp256k1();
5417
+ const to = toAddress ? yield core_1.ccc.Address.fromString(toAddress, this.client) : signerAddress;
5418
+ const amountBigInt = (0, validator_1.validateUdtAmount)(amount);
5419
+ let resolvedTypeArgs;
5420
+ if (kind === 'sudt') {
5421
+ if (typeArgs) {
5422
+ logger_1.logger.warn('SUDT type args are derived from the issuer lock hash; --type-args is ignored');
5423
+ }
5424
+ const issuerLockHash = signerAddress.script.hash();
5425
+ resolvedTypeArgs = ('0x' + issuerLockHash.slice(2, 42));
5426
+ }
5427
+ else {
5428
+ if (typeArgs) {
5429
+ resolvedTypeArgs = (0, validator_1.validateUdtTypeArgs)(kind, typeArgs);
5430
+ }
5431
+ else {
5432
+ const issuerLockHash = signerAddress.script.hash();
5433
+ resolvedTypeArgs = issuerLockHash;
5434
+ }
5435
+ }
5436
+ const udtType = yield this.buildUdtTypeScript(kind, resolvedTypeArgs);
5437
+ const outputsData = [core_1.ccc.hexFrom(core_1.ccc.numToBytes(amountBigInt, 16))];
5438
+ const tx = core_1.ccc.Transaction.from({
5439
+ outputs: [
5440
+ {
5441
+ lock: to.script,
5442
+ type: udtType,
5443
+ capacity: core_1.ccc.fixedPointFrom(0),
5444
+ },
5445
+ ],
5446
+ outputsData,
5447
+ });
5448
+ const scriptInfo = yield this.getUdtScriptInfo(kind);
5449
+ tx.addCellDeps(scriptInfo.cellDeps.map((dep) => dep.cellDep));
5450
+ yield tx.completeInputsByCapacity(signer);
5451
+ yield tx.completeFeeBy(signer, this.feeRate);
5452
+ const txHash = yield signer.sendTransaction(tx);
5453
+ return txHash;
5454
+ });
5455
+ }
5456
+ udtDestroy(_a) {
5457
+ return __awaiter(this, arguments, void 0, function* ({ privateKey, kind, typeArgs, amount }, { maxInputCells = DEFAULT_UDT_DESTROY_MAX_INPUT_CELLS } = {}) {
5458
+ var _b, e_3, _c, _d;
5459
+ const signer = this.buildSigner(privateKey);
5460
+ const from = yield signer.getAddressObjSecp256k1();
5461
+ const validatedTypeArgs = (0, validator_1.validateUdtTypeArgs)(kind, typeArgs);
5462
+ const udtType = yield this.buildUdtTypeScript(kind, validatedTypeArgs);
5463
+ const destroyAmount = (0, validator_1.validateUdtAmount)(amount);
5464
+ const cells = [];
5465
+ let totalBalance = BigInt(0);
5466
+ try {
5467
+ for (var _e = true, _f = __asyncValues(this.client.findCellsByLock(from.script, udtType, true)), _g; _g = yield _f.next(), _b = _g.done, !_b; _e = true) {
5468
+ _d = _g.value;
5469
+ _e = false;
5470
+ const cell = _d;
5471
+ if (cells.length >= maxInputCells) {
5472
+ throw new Error(`Too many UDT cells to destroy (limit: ${maxInputCells}); split into smaller operations`);
5473
+ }
5474
+ const cellBalance = readUdtBalance(cell.outputData);
5475
+ if (cellBalance == null) {
5476
+ continue;
5477
+ }
5478
+ cells.push(cell);
5479
+ totalBalance += cellBalance;
5480
+ }
5481
+ }
5482
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
5483
+ finally {
5484
+ try {
5485
+ if (!_e && !_b && (_c = _f.return)) yield _c.call(_f);
5486
+ }
5487
+ finally { if (e_3) throw e_3.error; }
5488
+ }
5489
+ if (totalBalance < destroyAmount) {
5490
+ throw new Error(`Insufficient UDT balance: ${totalBalance} < ${destroyAmount}`);
5491
+ }
5492
+ if (destroyAmount === totalBalance) {
5493
+ throw new Error('Destroying the entire UDT balance may be rejected by the UDT script. Leave at least 1 token or use a smaller amount.');
5494
+ }
5495
+ const tx = core_1.ccc.Transaction.from({});
5496
+ for (const cell of cells) {
5497
+ tx.addInput({ previousOutput: cell.outPoint });
5498
+ }
5499
+ const remaining = totalBalance - destroyAmount;
5500
+ tx.addOutput(core_1.ccc.CellOutput.from({
5501
+ lock: from.script,
5502
+ type: udtType,
5503
+ capacity: core_1.ccc.fixedPointFrom(0),
5504
+ }, core_1.ccc.hexFrom(core_1.ccc.numToBytes(remaining, 16))), core_1.ccc.hexFrom(core_1.ccc.numToBytes(remaining, 16)));
5505
+ const scriptInfo = yield this.getUdtScriptInfo(kind);
5506
+ tx.addCellDeps(scriptInfo.cellDeps.map((dep) => dep.cellDep));
5507
+ yield tx.completeInputsByCapacity(signer);
5508
+ yield tx.completeFeeBy(signer, this.feeRate);
5509
+ const txHash = yield signer.sendTransaction(tx);
5510
+ return txHash;
5511
+ });
5512
+ }
3975
5513
  deployScript(scriptBinBytes, privateKey) {
3976
5514
  return __awaiter(this, void 0, void 0, function* () {
3977
5515
  const signer = this.buildSigner(privateKey);
@@ -5321,6 +6859,314 @@ exports.CKBDebugger = CKBDebugger;
5321
6859
  CKBDebugger.wasmDebugger = null;
5322
6860
 
5323
6861
 
6862
+ /***/ }),
6863
+
6864
+ /***/ 7020:
6865
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
6866
+
6867
+ "use strict";
6868
+
6869
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6870
+ if (k2 === undefined) k2 = k;
6871
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6872
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6873
+ desc = { enumerable: true, get: function() { return m[k]; } };
6874
+ }
6875
+ Object.defineProperty(o, k2, desc);
6876
+ }) : (function(o, m, k, k2) {
6877
+ if (k2 === undefined) k2 = k;
6878
+ o[k2] = m[k];
6879
+ }));
6880
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
6881
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
6882
+ }) : function(o, v) {
6883
+ o["default"] = v;
6884
+ });
6885
+ var __importStar = (this && this.__importStar) || (function () {
6886
+ var ownKeys = function(o) {
6887
+ ownKeys = Object.getOwnPropertyNames || function (o) {
6888
+ var ar = [];
6889
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
6890
+ return ar;
6891
+ };
6892
+ return ownKeys(o);
6893
+ };
6894
+ return function (mod) {
6895
+ if (mod && mod.__esModule) return mod;
6896
+ var result = {};
6897
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
6898
+ __setModuleDefault(result, mod);
6899
+ return result;
6900
+ };
6901
+ })();
6902
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6903
+ return (mod && mod.__esModule) ? mod : { "default": mod };
6904
+ };
6905
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6906
+ exports.CKBTui = void 0;
6907
+ const child_process_1 = __nccwpck_require__(35317);
6908
+ const path = __importStar(__nccwpck_require__(16928));
6909
+ const fs = __importStar(__nccwpck_require__(79896));
6910
+ const os = __importStar(__nccwpck_require__(70857));
6911
+ const crypto = __importStar(__nccwpck_require__(76982));
6912
+ const adm_zip_1 = __importDefault(__nccwpck_require__(3858));
6913
+ const setting_1 = __nccwpck_require__(25546);
6914
+ const logger_1 = __nccwpck_require__(65370);
6915
+ const fs_1 = __nccwpck_require__(37293);
6916
+ const DOWNLOAD_TIMEOUT_MS = 120000;
6917
+ const EXTRACT_TIMEOUT_MS = 60000;
6918
+ // Strict semver regex: v<major>.<minor>.<patch> (no leading zeros on digits)
6919
+ const STRICT_VERSION_REGEX = /^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
6920
+ class CKBTui {
6921
+ /**
6922
+ * Pure lookup — returns the expected binary path without triggering any
6923
+ * download or installation side effects. May return null if not yet computed.
6924
+ */
6925
+ static getBinaryPath() {
6926
+ if (!this.binaryPath) {
6927
+ const settings = (0, setting_1.readSettings)();
6928
+ const binDir = this.resolveAndValidateBinDir(settings.tools.rootFolder);
6929
+ const binaryName = process.platform === 'win32' ? 'ckb-tui.exe' : 'ckb-tui';
6930
+ this.binaryPath = path.join(binDir, binaryName);
6931
+ }
6932
+ return this.binaryPath;
6933
+ }
6934
+ /**
6935
+ * Returns the binary path, downloading and installing if the binary
6936
+ * does not already exist.
6937
+ */
6938
+ static ensureInstalled() {
6939
+ const binaryPath = this.getBinaryPath();
6940
+ if (binaryPath && fs.existsSync(binaryPath)) {
6941
+ return binaryPath;
6942
+ }
6943
+ // Reset and re-install
6944
+ this.binaryPath = null;
6945
+ this.installSync();
6946
+ return this.binaryPath;
6947
+ }
6948
+ static isInstalled() {
6949
+ try {
6950
+ const binPath = this.getBinaryPath();
6951
+ return binPath !== null && fs.existsSync(binPath);
6952
+ }
6953
+ catch (_a) {
6954
+ return false;
6955
+ }
6956
+ }
6957
+ static run(args = []) {
6958
+ const binaryPath = this.ensureInstalled();
6959
+ return (0, child_process_1.spawnSync)(binaryPath, args, { stdio: 'inherit' });
6960
+ }
6961
+ // --- private helpers ---
6962
+ /**
6963
+ * Resolve and validate that the configured rootFolder is under the
6964
+ * OffCKB data directory. Rejects paths that resolve outside.
6965
+ */
6966
+ static resolveAndValidateBinDir(configuredRoot) {
6967
+ const resolved = path.resolve(configuredRoot);
6968
+ const resolvedData = path.resolve(setting_1.dataPath);
6969
+ // Require the resolved path to be within the resolved data directory
6970
+ const relative = path.relative(resolvedData, resolved);
6971
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
6972
+ throw new Error(`tools.rootFolder ("${configuredRoot}") resolves outside the OffCKB data directory ` +
6973
+ `("${resolvedData}"). For security, tool binaries must be stored under the data path.`);
6974
+ }
6975
+ return resolved;
6976
+ }
6977
+ static validateVersion(version) {
6978
+ if (!STRICT_VERSION_REGEX.test(version)) {
6979
+ throw new Error(`Invalid version format: "${version}". Expected format: vX.Y.Z (e.g., v0.1.3)`);
6980
+ }
6981
+ }
6982
+ static getAssetName() {
6983
+ const platform = process.platform;
6984
+ const arch = process.arch;
6985
+ if (platform === 'darwin') {
6986
+ if (arch !== 'arm64') {
6987
+ throw new Error(`Unsupported architecture for macOS: ${arch}. Only Apple Silicon (arm64) is supported.`);
6988
+ }
6989
+ return 'ckb-tui-with-node-macos-aarch64.tar.gz';
6990
+ }
6991
+ else if (platform === 'linux') {
6992
+ if (arch !== 'x64') {
6993
+ throw new Error(`Unsupported architecture for Linux: ${arch}. Only x86_64 is supported.`);
6994
+ }
6995
+ return 'ckb-tui-with-node-linux-amd64.tar.gz';
6996
+ }
6997
+ else if (platform === 'win32') {
6998
+ if (arch !== 'x64') {
6999
+ throw new Error(`Unsupported architecture for Windows: ${arch}. Only x86_64 is supported.`);
7000
+ }
7001
+ return 'ckb-tui-with-node-windows-amd64.zip';
7002
+ }
7003
+ throw new Error(`Unsupported platform: ${platform}`);
7004
+ }
7005
+ /**
7006
+ * Synchronously download and install the ckb-tui binary.
7007
+ * Uses spawnSync with array arguments (no shell interpolation) for security.
7008
+ */
7009
+ static installSync() {
7010
+ const settings = (0, setting_1.readSettings)();
7011
+ const version = settings.tools.ckbTui.version;
7012
+ this.validateVersion(version);
7013
+ const assetName = this.getAssetName();
7014
+ const binDir = this.resolveAndValidateBinDir(settings.tools.rootFolder);
7015
+ const binaryName = process.platform === 'win32' ? 'ckb-tui.exe' : 'ckb-tui';
7016
+ this.binaryPath = path.join(binDir, binaryName);
7017
+ const downloadUrl = `https://github.com/Officeyutong/ckb-tui/releases/download/${version}/${assetName}`;
7018
+ // Ensure the target directory exists
7019
+ fs.mkdirSync(binDir, { recursive: true });
7020
+ // Use a temp directory for atomic download & extraction
7021
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'offckb-ckb-tui-'));
7022
+ const archivePath = path.join(tempDir, assetName);
7023
+ try {
7024
+ // 1. Download
7025
+ logger_1.logger.info(`Downloading ckb-tui from ${downloadUrl}...`);
7026
+ const curlResult = (0, child_process_1.spawnSync)('curl', ['-fsSL', '--max-time', '300', '-o', archivePath, downloadUrl], {
7027
+ stdio: 'inherit',
7028
+ timeout: DOWNLOAD_TIMEOUT_MS,
7029
+ });
7030
+ if (curlResult.error) {
7031
+ throw new Error(`Failed to download ckb-tui: ${curlResult.error.message}`);
7032
+ }
7033
+ if (curlResult.status !== 0) {
7034
+ throw new Error(`curl exited with code ${curlResult.status}`);
7035
+ }
7036
+ // 2. Verify checksum (best-effort: warns if checksum file is unavailable)
7037
+ this.verifyChecksum(version, assetName, archivePath);
7038
+ // 3. Extract to temp directory
7039
+ logger_1.logger.info('Extracting...');
7040
+ const extractDir = path.join(tempDir, 'extracted');
7041
+ fs.mkdirSync(extractDir, { recursive: true });
7042
+ this.extractArchive(archivePath, extractDir);
7043
+ // 4. Locate the extracted binary
7044
+ const extractedBinary = (0, fs_1.findFileInFolder)(extractDir, binaryName);
7045
+ if (!extractedBinary) {
7046
+ throw new Error(`ckb-tui binary ("${binaryName}") was not found after extraction.`);
7047
+ }
7048
+ // 5. Atomically move to the final location
7049
+ fs.renameSync(extractedBinary, this.binaryPath);
7050
+ // 6. Make executable on Unix
7051
+ if (process.platform !== 'win32') {
7052
+ fs.chmodSync(this.binaryPath, 0o755);
7053
+ }
7054
+ logger_1.logger.info('ckb-tui installed successfully.');
7055
+ }
7056
+ catch (error) {
7057
+ // Reset cached path on failure so a subsequent call retries
7058
+ this.binaryPath = null;
7059
+ const message = error instanceof Error ? error.message : String(error);
7060
+ logger_1.logger.error('Failed to download/install ckb-tui:', message, '\nPlease check your network connectivity, verify that the specified version exists in the releases, ' +
7061
+ 'and ensure you have sufficient file system permissions.');
7062
+ throw error;
7063
+ }
7064
+ finally {
7065
+ // Clean up the temp directory
7066
+ try {
7067
+ fs.rmSync(tempDir, { recursive: true, force: true });
7068
+ }
7069
+ catch (_a) {
7070
+ // Best-effort cleanup — temp dir will be cleaned by the OS eventually
7071
+ }
7072
+ }
7073
+ }
7074
+ /**
7075
+ * Best-effort SHA-256 checksum verification.
7076
+ * Downloads the checksum file published alongside the release asset and
7077
+ * verifies the downloaded archive. Logs a warning (but does not fail) if
7078
+ * the checksum file is unavailable — this maintains compatibility while
7079
+ * the upstream project adopts checksum publishing.
7080
+ */
7081
+ static verifyChecksum(version, assetName, archivePath) {
7082
+ const checksumUrl = `https://github.com/Officeyutong/ckb-tui/releases/download/${version}/checksums-sha256.txt`;
7083
+ const checksumPath = path.join(path.dirname(archivePath), 'checksums-sha256.txt');
7084
+ const fetchResult = (0, child_process_1.spawnSync)('curl', ['-fsSL', '--max-time', '30', '-o', checksumPath, checksumUrl], {
7085
+ stdio: 'pipe',
7086
+ timeout: 30000,
7087
+ });
7088
+ if (fetchResult.status !== 0) {
7089
+ logger_1.logger.warn(`SHA-256 checksum file not available for version ${version}. ` +
7090
+ 'Skipping integrity verification. Consider asking the upstream maintainer to publish checksum files.');
7091
+ return;
7092
+ }
7093
+ try {
7094
+ const checksumContent = fs.readFileSync(checksumPath, 'utf8');
7095
+ const expectedHash = this.parseChecksumFile(checksumContent, assetName);
7096
+ if (!expectedHash) {
7097
+ logger_1.logger.warn(`Checksum entry for "${assetName}" not found in checksums file. Skipping verification.`);
7098
+ return;
7099
+ }
7100
+ const actualHash = crypto.createHash('sha256').update(fs.readFileSync(archivePath)).digest('hex');
7101
+ if (actualHash !== expectedHash) {
7102
+ throw new Error(`SHA-256 checksum mismatch for ${assetName}.\n` +
7103
+ `Expected: ${expectedHash}\nActual: ${actualHash}\n` +
7104
+ 'The downloaded file may be corrupted or tampered with.');
7105
+ }
7106
+ logger_1.logger.info('SHA-256 checksum verified successfully.');
7107
+ }
7108
+ finally {
7109
+ try {
7110
+ fs.unlinkSync(checksumPath);
7111
+ }
7112
+ catch (_a) {
7113
+ // Best effort
7114
+ }
7115
+ }
7116
+ }
7117
+ /**
7118
+ * Parse a standard SHA-256 checksum file (format: "<hash> <filename>" per line)
7119
+ * and return the hex hash for the given asset name, or null if not found.
7120
+ */
7121
+ static parseChecksumFile(content, assetName) {
7122
+ for (const line of content.split('\n')) {
7123
+ const trimmed = line.trim();
7124
+ if (!trimmed || trimmed.startsWith('#'))
7125
+ continue;
7126
+ const match = trimmed.match(/^([0-9a-fA-F]{64})\s+[*]?(.+)$/);
7127
+ if (match && match[2] === assetName) {
7128
+ return match[1].toLowerCase();
7129
+ }
7130
+ }
7131
+ return null;
7132
+ }
7133
+ /**
7134
+ * Extract a downloaded archive to the given directory.
7135
+ * Uses AdmZip for .zip files (Node-native, no shell) and spawnSync with array
7136
+ * arguments for .tar.gz (no shell interpolation).
7137
+ */
7138
+ static extractArchive(archivePath, extractDir) {
7139
+ if (archivePath.endsWith('.tar.gz')) {
7140
+ const result = (0, child_process_1.spawnSync)('tar', ['-xzf', archivePath, '-C', extractDir], {
7141
+ stdio: 'inherit',
7142
+ timeout: EXTRACT_TIMEOUT_MS,
7143
+ });
7144
+ if (result.error) {
7145
+ throw new Error(`tar extraction failed: ${result.error.message}`);
7146
+ }
7147
+ if (result.status !== 0) {
7148
+ throw new Error(`tar exited with code ${result.status}`);
7149
+ }
7150
+ }
7151
+ else if (archivePath.endsWith('.zip')) {
7152
+ try {
7153
+ const zip = new adm_zip_1.default(archivePath);
7154
+ zip.extractAllTo(extractDir, true);
7155
+ }
7156
+ catch (error) {
7157
+ const message = error instanceof Error ? error.message : String(error);
7158
+ throw new Error(`ZIP extraction failed: ${message}`);
7159
+ }
7160
+ }
7161
+ else {
7162
+ throw new Error(`Unsupported archive format: ${path.extname(archivePath)}`);
7163
+ }
7164
+ }
7165
+ }
7166
+ exports.CKBTui = CKBTui;
7167
+ CKBTui.binaryPath = null;
7168
+
7169
+
5324
7170
  /***/ }),
5325
7171
 
5326
7172
  /***/ 20924:
@@ -5346,6 +7192,7 @@ const fs_1 = __importDefault(__nccwpck_require__(79896));
5346
7192
  const path_1 = __importDefault(__nccwpck_require__(16928));
5347
7193
  const core_1 = __nccwpck_require__(39842);
5348
7194
  const advanced_1 = __nccwpck_require__(98486);
7195
+ const json_rpc_1 = __nccwpck_require__(51726);
5349
7196
  const logger_1 = __nccwpck_require__(65370);
5350
7197
  const OutPointCodec = core_1.ccc.mol.struct({
5351
7198
  txHash: core_1.ccc.mol.Byte32,
@@ -5470,6 +7317,19 @@ function resolveInputs(client, inputs) {
5470
7317
  return resolved;
5471
7318
  });
5472
7319
  }
7320
+ function resolveHeaderDeps(rpc, headerDeps) {
7321
+ return __awaiter(this, void 0, void 0, function* () {
7322
+ const headers = [];
7323
+ for (const hash of headerDeps) {
7324
+ const header = yield (0, json_rpc_1.callJsonRpc)(rpc, 'get_header', [hash]);
7325
+ if (!header) {
7326
+ throw new Error(`Header not found: ${hash}`);
7327
+ }
7328
+ headers.push(header);
7329
+ }
7330
+ return headers;
7331
+ });
7332
+ }
5473
7333
  function dumpTransaction(_a) {
5474
7334
  return __awaiter(this, arguments, void 0, function* ({ rpc, txJsonFilePath, outputFilePath }) {
5475
7335
  try {
@@ -5485,15 +7345,16 @@ function dumpTransaction(_a) {
5485
7345
  });
5486
7346
  const txJson = JSON.parse(fs_1.default.readFileSync(txJsonFilePath, 'utf-8'));
5487
7347
  const tx = advanced_1.cccA.JsonRpcTransformers.transactionTo(txJson);
5488
- const [cell_deps, inputs] = yield Promise.all([
7348
+ const [cell_deps, inputs, header_deps] = yield Promise.all([
5489
7349
  resolveCellDeps(client, tx.cellDeps),
5490
7350
  resolveInputs(client, tx.inputs),
7351
+ resolveHeaderDeps(rpc, tx.headerDeps),
5491
7352
  ]);
5492
7353
  const mockTx = {
5493
7354
  mock_info: {
5494
7355
  inputs,
5495
7356
  cell_deps,
5496
- header_deps: tx.headerDeps.map((h) => h.toString()),
7357
+ header_deps,
5497
7358
  },
5498
7359
  tx: {
5499
7360
  version: '0x' + tx.version.toString(16),
@@ -7746,18 +9607,107 @@ function getSubfolders(folderPath) {
7746
9607
  }
7747
9608
 
7748
9609
 
9610
+ /***/ }),
9611
+
9612
+ /***/ 51726:
9613
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
9614
+
9615
+ "use strict";
9616
+
9617
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
9618
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9619
+ return new (P || (P = Promise))(function (resolve, reject) {
9620
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9621
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
9622
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9623
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9624
+ });
9625
+ };
9626
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9627
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9628
+ };
9629
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
9630
+ exports.callJsonRpc = callJsonRpc;
9631
+ const http_1 = __importDefault(__nccwpck_require__(58611));
9632
+ const https_1 = __importDefault(__nccwpck_require__(65692));
9633
+ // Minimal raw JSON-RPC caller. Unlike Request.send it never goes through the
9634
+ // configured proxy, which makes it safe for talking to the local devnet node.
9635
+ function callJsonRpc(rpcUrl_1, method_1, params_1) {
9636
+ return __awaiter(this, arguments, void 0, function* (rpcUrl, method, params, timeoutMs = 30000) {
9637
+ const body = JSON.stringify({ id: 1, jsonrpc: '2.0', method, params });
9638
+ const url = new URL(rpcUrl);
9639
+ const transport = url.protocol === 'https:' ? https_1.default : http_1.default;
9640
+ return new Promise((resolve, reject) => {
9641
+ const req = transport.request({
9642
+ hostname: url.hostname,
9643
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
9644
+ path: url.pathname + url.search,
9645
+ method: 'POST',
9646
+ headers: {
9647
+ 'content-type': 'application/json',
9648
+ 'content-length': Buffer.byteLength(body),
9649
+ },
9650
+ timeout: timeoutMs,
9651
+ }, (res) => {
9652
+ const chunks = [];
9653
+ res.on('data', (chunk) => chunks.push(chunk));
9654
+ // The response stream can error or close before `end` (connection
9655
+ // reset, truncated body). Without these listeners the promise would
9656
+ // hang until the request timeout — or the process would crash on an
9657
+ // unhandled stream error.
9658
+ res.once('error', reject);
9659
+ res.on('close', () => {
9660
+ if (!res.complete) {
9661
+ reject(new Error(`JSON-RPC ${method} to ${rpcUrl} returned a truncated response`));
9662
+ }
9663
+ });
9664
+ res.on('end', () => {
9665
+ try {
9666
+ const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8'));
9667
+ if (parsed.error) {
9668
+ reject(new Error(`JSON-RPC ${method} failed: ${JSON.stringify(parsed.error)}`));
9669
+ return;
9670
+ }
9671
+ resolve(parsed.result);
9672
+ }
9673
+ catch (error) {
9674
+ reject(new Error(`Invalid JSON-RPC response from ${rpcUrl}: ${error.message}`));
9675
+ }
9676
+ });
9677
+ });
9678
+ req.on('timeout', () => {
9679
+ req.destroy(new Error(`JSON-RPC ${method} to ${rpcUrl} timed out`));
9680
+ });
9681
+ req.on('error', reject);
9682
+ req.write(body);
9683
+ req.end();
9684
+ });
9685
+ });
9686
+ }
9687
+
9688
+
7749
9689
  /***/ }),
7750
9690
 
7751
9691
  /***/ 27814:
7752
- /***/ ((__unused_webpack_module, exports) => {
9692
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
7753
9693
 
7754
9694
  "use strict";
7755
9695
 
7756
9696
  Object.defineProperty(exports, "__esModule", ({ value: true }));
7757
9697
  exports.buildTestnetTxLink = buildTestnetTxLink;
9698
+ exports.logTxSuccess = logTxSuccess;
9699
+ const logger_1 = __nccwpck_require__(65370);
7758
9700
  function buildTestnetTxLink(txHash) {
7759
9701
  return `https://pudge.explorer.nervos.org/transaction/${txHash}`;
7760
9702
  }
9703
+ function logTxSuccess(network, txHash, action) {
9704
+ if (network === 'testnet') {
9705
+ logger_1.logger.info(`Successfully ${action}, check ${buildTestnetTxLink(txHash)} for details.`);
9706
+ }
9707
+ else {
9708
+ logger_1.logger.info(`Successfully ${action}, txHash:`, txHash);
9709
+ }
9710
+ }
7761
9711
 
7762
9712
 
7763
9713
  /***/ }),
@@ -7819,6 +9769,7 @@ class UnifiedLogger {
7819
9769
  constructor(options = {}) {
7820
9770
  this.enableColors = options.enableColors !== false;
7821
9771
  this.showLevel = options.showLevel !== false;
9772
+ this.jsonMode = options.jsonMode === true;
7822
9773
  // Create Winston logger with custom format and levels
7823
9774
  this.logger = winston_1.default.createLogger({
7824
9775
  level: options.level || node_process_1.default.env.LOG_LEVEL || 'info',
@@ -7832,17 +9783,33 @@ class UnifiedLogger {
7832
9783
  format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.errors({ stack: true }), winston_1.default.format.printf(({ level, message, timestamp }) => {
7833
9784
  return this.formatMessage(level, message, timestamp);
7834
9785
  })),
7835
- transports: [
9786
+ transports: options.transports || [
7836
9787
  new winston_1.default.transports.Console({
7837
9788
  stderrLevels: ['error', 'warn'],
7838
9789
  }),
7839
9790
  ],
7840
9791
  });
7841
9792
  }
9793
+ /**
9794
+ * Toggle JSON output mode. When enabled, every log line is emitted as a
9795
+ * structured JSON object, which is easier for agents and scripts to parse.
9796
+ */
9797
+ setJsonMode(enabled) {
9798
+ this.jsonMode = enabled;
9799
+ }
7842
9800
  /**
7843
9801
  * Format the message with appropriate colors and structure
7844
9802
  */
7845
- formatMessage(level, message, _timestamp) {
9803
+ formatMessage(level, message, timestamp) {
9804
+ // Agent-friendly JSON output: one JSON object per log line
9805
+ if (this.jsonMode) {
9806
+ const normalizedMessage = Array.isArray(message) ? message.join('\n') : String(message);
9807
+ return JSON.stringify({
9808
+ level,
9809
+ message: normalizedMessage,
9810
+ timestamp,
9811
+ });
9812
+ }
7846
9813
  // If showLevel is false, return just the message
7847
9814
  if (!this.showLevel) {
7848
9815
  if (Array.isArray(message)) {
@@ -7925,6 +9892,12 @@ class UnifiedLogger {
7925
9892
  * Log a message with the specified level
7926
9893
  */
7927
9894
  log(level, message) {
9895
+ // In JSON mode, emit multi-line messages as a single structured log entry
9896
+ // so agents can parse one complete JSON object per line.
9897
+ if (this.jsonMode && Array.isArray(message)) {
9898
+ this.logger.log(level, message.join('\n'));
9899
+ return;
9900
+ }
7928
9901
  if (Array.isArray(message)) {
7929
9902
  message.forEach((line) => this.logger.log(level, line));
7930
9903
  }
@@ -8091,6 +10064,11 @@ exports.isValidNetworkString = isValidNetworkString;
8091
10064
  exports.validateNetworkOpt = validateNetworkOpt;
8092
10065
  exports.isValidVersion = isValidVersion;
8093
10066
  exports.normalizePrivKey = normalizePrivKey;
10067
+ exports.isValidUdtKind = isValidUdtKind;
10068
+ exports.validateUdtKind = validateUdtKind;
10069
+ exports.validateUdtAmount = validateUdtAmount;
10070
+ exports.validateHexString = validateHexString;
10071
+ exports.validateUdtTypeArgs = validateUdtTypeArgs;
8094
10072
  const path_1 = __importDefault(__nccwpck_require__(16928));
8095
10073
  const fs_1 = __importDefault(__nccwpck_require__(79896));
8096
10074
  const base_1 = __nccwpck_require__(69951);
@@ -8179,6 +10157,46 @@ function normalizePrivKey(privKey) {
8179
10157
  // Return the formally strictly padded ckb format `0x` string
8180
10158
  return '0x' + key;
8181
10159
  }
10160
+ function isValidUdtKind(kind) {
10161
+ return kind === 'sudt' || kind === 'xudt';
10162
+ }
10163
+ function validateUdtKind(kind) {
10164
+ if (!kind) {
10165
+ throw new Error('UDT kind is required');
10166
+ }
10167
+ if (!isValidUdtKind(kind)) {
10168
+ throw new Error(`invalid UDT kind "${kind}", must be "sudt" or "xudt"`);
10169
+ }
10170
+ }
10171
+ const U128_MAX = (BigInt(1) << BigInt(128)) - BigInt(1);
10172
+ function validateUdtAmount(amount) {
10173
+ if (!/^\d+$/.test(amount)) {
10174
+ throw new Error(`invalid UDT amount "${amount}", must be a non-negative decimal integer`);
10175
+ }
10176
+ const value = BigInt(amount);
10177
+ if (value > U128_MAX) {
10178
+ throw new Error(`UDT amount exceeds 128-bit max: ${amount}`);
10179
+ }
10180
+ return value;
10181
+ }
10182
+ const HEX_REGEX = /^0x[0-9a-fA-F]*$/;
10183
+ function validateHexString(value, name) {
10184
+ if (!value || !HEX_REGEX.test(value)) {
10185
+ throw new Error(`invalid ${name} "${value}", must be a hex string starting with 0x`);
10186
+ }
10187
+ return value;
10188
+ }
10189
+ function validateUdtTypeArgs(kind, typeArgs) {
10190
+ const hex = validateHexString(typeArgs, 'type args');
10191
+ const byteLength = (hex.length - 2) / 2;
10192
+ if (kind === 'sudt' && byteLength !== 20) {
10193
+ throw new Error(`invalid SUDT type args length: expected 20 bytes, got ${byteLength}`);
10194
+ }
10195
+ if (kind === 'xudt' && byteLength !== 32) {
10196
+ throw new Error(`invalid xUDT type args length: expected 32 bytes, got ${byteLength}`);
10197
+ }
10198
+ return hex;
10199
+ }
8182
10200
 
8183
10201
 
8184
10202
  /***/ }),
@@ -15922,13 +17940,13 @@ exports.randomBytes = randomBytes;
15922
17940
 
15923
17941
  /***/ }),
15924
17942
 
15925
- /***/ 83746:
17943
+ /***/ 3858:
15926
17944
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
15927
17945
 
15928
- const Utils = __nccwpck_require__(28605);
17946
+ const Utils = __nccwpck_require__(91373);
15929
17947
  const pth = __nccwpck_require__(16928);
15930
- const ZipEntry = __nccwpck_require__(59155);
15931
- const ZipFile = __nccwpck_require__(43985);
17948
+ const ZipEntry = __nccwpck_require__(58851);
17949
+ const ZipFile = __nccwpck_require__(46593);
15932
17950
 
15933
17951
  const get_Bool = (...val) => Utils.findLast(val, (c) => typeof c === "boolean");
15934
17952
  const get_Str = (...val) => Utils.findLast(val, (c) => typeof c === "string");
@@ -15974,6 +17992,18 @@ module.exports = function (/**String*/ input, /** object */ options) {
15974
17992
  // instanciate utils filesystem
15975
17993
  const filetools = new Utils(opts);
15976
17994
 
17995
+ // Restore the archived permissions on extracted directories. This has to run
17996
+ // after a directory's contents are written: applying a restrictive mode
17997
+ // (e.g. 0o500) up front would stop us writing the files it contains. Applying
17998
+ // the deepest paths first keeps parent directories traversable while their
17999
+ // children are updated (issue #530).
18000
+ const applyDirAttributes = (dirEntries) => {
18001
+ dirEntries
18002
+ .filter((d) => d.attr)
18003
+ .sort((a, b) => b.path.length - a.path.length)
18004
+ .forEach((d) => filetools.fs.chmodSync(d.path, d.attr));
18005
+ };
18006
+
15977
18007
  if (typeof opts.decoder !== "object" || typeof opts.decoder.encode !== "function" || typeof opts.decoder.decode !== "function") {
15978
18008
  opts.decoder = Utils.decoder;
15979
18009
  }
@@ -16013,7 +18043,7 @@ module.exports = function (/**String*/ input, /** object */ options) {
16013
18043
  function fixPath(zipPath) {
16014
18044
  const { join, normalize, sep } = pth.posix;
16015
18045
  // convert windows file separators and normalize
16016
- return join(".", normalize(sep + zipPath.split("\\").join(sep) + sep));
18046
+ return join(pth.isAbsolute(zipPath) ? "/": '.', normalize(sep + zipPath.split("\\").join(sep) + sep));
16017
18047
  }
16018
18048
 
16019
18049
  function filenameFilter(filterfn) {
@@ -16128,6 +18158,7 @@ module.exports = function (/**String*/ input, /** object */ options) {
16128
18158
  * Remove the entry from the file or the entry and all it's nested directories and files if the given entry is a directory
16129
18159
  *
16130
18160
  * @param {ZipEntry|string} entry
18161
+ * @param {boolean} withsubfolders
16131
18162
  * @returns {void}
16132
18163
  */
16133
18164
  deleteFile: function (entry, withsubfolders = true) {
@@ -16412,7 +18443,7 @@ module.exports = function (/**String*/ input, /** object */ options) {
16412
18443
  addLocalFolderAsync2: function (options, callback) {
16413
18444
  const self = this;
16414
18445
  options = typeof options === "object" ? options : { localPath: options };
16415
- localPath = pth.resolve(fixPath(options.localPath));
18446
+ const localPath = pth.resolve(fixPath(options.localPath));
16416
18447
  let { zipPath, filter, namefix } = options;
16417
18448
 
16418
18449
  if (filter instanceof RegExp) {
@@ -16431,7 +18462,7 @@ module.exports = function (/**String*/ input, /** object */ options) {
16431
18462
  zipPath = zipPath ? fixPath(zipPath) : "";
16432
18463
 
16433
18464
  // Check Namefix function
16434
- if (namefix == "latin1") {
18465
+ if (namefix === "latin1") {
16435
18466
  namefix = (str) =>
16436
18467
  str
16437
18468
  .normalize("NFD")
@@ -16607,7 +18638,7 @@ module.exports = function (/**String*/ input, /** object */ options) {
16607
18638
 
16608
18639
  var entryName = canonical(item.entryName);
16609
18640
 
16610
- var target = sanitize(targetPath, outFileName && !item.isDirectory ? outFileName : maintainEntryPath ? entryName : pth.basename(entryName));
18641
+ var target = sanitize(targetPath, outFileName && !item.isDirectory ? canonical(outFileName) : maintainEntryPath ? entryName : pth.basename(entryName));
16611
18642
 
16612
18643
  if (item.isDirectory) {
16613
18644
  var children = _zip.getEntryChildren(item);
@@ -16617,8 +18648,12 @@ module.exports = function (/**String*/ input, /** object */ options) {
16617
18648
  if (!content) {
16618
18649
  throw Utils.Errors.CANT_EXTRACT_FILE();
16619
18650
  }
16620
- var name = canonical(child.entryName);
16621
- var childName = sanitize(targetPath, maintainEntryPath ? name : pth.basename(name));
18651
+ // When not maintaining the full entry path, keep each child's path
18652
+ // relative to the extracted directory (drop the directory's own
18653
+ // prefix) instead of flattening every file to its basename, which
18654
+ // collapsed subdirectories together (issue #306).
18655
+ var name = canonical(maintainEntryPath ? child.entryName : child.entryName.substring(item.entryName.length));
18656
+ var childName = sanitize(targetPath, name);
16622
18657
  // The reverse operation for attr depend on method addFile()
16623
18658
  const fileAttr = keepOriginalPermission ? child.header.fileAttr : undefined;
16624
18659
  filetools.writeFileTo(childName, content, overwrite, fileAttr);
@@ -16648,12 +18683,14 @@ module.exports = function (/**String*/ input, /** object */ options) {
16648
18683
  return false;
16649
18684
  }
16650
18685
 
16651
- for (var entry in _zip.entries) {
18686
+ for (var entry of _zip.entries) {
16652
18687
  try {
16653
18688
  if (entry.isDirectory) {
16654
18689
  continue;
16655
18690
  }
16656
- var content = _zip.entries[entry].getData(pass);
18691
+ // was `_zip.entries[entry]` (indexing the array with an entry
18692
+ // object -> undefined -> threw -> test() always returned false)
18693
+ var content = entry.getData(pass);
16657
18694
  if (!content) {
16658
18695
  return false;
16659
18696
  }
@@ -16680,10 +18717,13 @@ module.exports = function (/**String*/ input, /** object */ options) {
16680
18717
  overwrite = get_Bool(false, overwrite);
16681
18718
  if (!_zip) throw Utils.Errors.NO_ZIP();
16682
18719
 
18720
+ const dirEntries = [];
16683
18721
  _zip.entries.forEach(function (entry) {
16684
18722
  var entryName = sanitize(targetPath, canonical(entry.entryName));
16685
18723
  if (entry.isDirectory) {
16686
18724
  filetools.makeDir(entryName);
18725
+ // defer restoring the directory permission until its files are written
18726
+ if (keepOriginalPermission) dirEntries.push({ path: entryName, attr: entry.header.fileAttr });
16687
18727
  return;
16688
18728
  }
16689
18729
  var content = entry.getData(pass);
@@ -16694,11 +18734,15 @@ module.exports = function (/**String*/ input, /** object */ options) {
16694
18734
  const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
16695
18735
  filetools.writeFileTo(entryName, content, overwrite, fileAttr);
16696
18736
  try {
18737
+ // best-effort: an invalid date in the archive or a filesystem that
18738
+ // rejects utimes must not fail extraction of already-written content (issue #379)
16697
18739
  filetools.fs.utimesSync(entryName, entry.header.time, entry.header.time);
16698
18740
  } catch (err) {
16699
- throw Utils.Errors.CANT_EXTRACT_FILE();
18741
+ /* ignore timestamp failures */
16700
18742
  }
16701
18743
  });
18744
+
18745
+ applyDirAttributes(dirEntries);
16702
18746
  },
16703
18747
 
16704
18748
  /**
@@ -16749,20 +18793,41 @@ module.exports = function (/**String*/ input, /** object */ options) {
16749
18793
 
16750
18794
  // Create directory entries first synchronously
16751
18795
  // this prevents race condition and assures folders are there before writing files
18796
+ const deferredDirAttr = [];
16752
18797
  for (const entry of dirEntries) {
16753
18798
  const dirPath = getPath(entry);
16754
18799
  // The reverse operation for attr depend on method addFile()
16755
18800
  const dirAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
16756
18801
  try {
16757
18802
  filetools.makeDir(dirPath);
16758
- if (dirAttr) filetools.fs.chmodSync(dirPath, dirAttr);
16759
- // in unix timestamp will change if files are later added to folder, but still
16760
- filetools.fs.utimesSync(dirPath, entry.header.time, entry.header.time);
16761
18803
  } catch (er) {
16762
18804
  callback(getError("Unable to create folder", dirPath));
18805
+ continue;
18806
+ }
18807
+ // defer restoring the directory permission until its files are written:
18808
+ // a restrictive mode applied now would block writing them
18809
+ if (dirAttr) deferredDirAttr.push({ path: dirPath, attr: dirAttr });
18810
+ try {
18811
+ // in unix timestamp will change if files are later added to folder, but still.
18812
+ // best-effort: a utimes failure must not abort extraction (issue #379)
18813
+ filetools.fs.utimesSync(dirPath, entry.header.time, entry.header.time);
18814
+ } catch (er) {
18815
+ /* ignore timestamp failures */
16763
18816
  }
16764
18817
  }
16765
18818
 
18819
+ // restore directory permissions once every file has been extracted
18820
+ const done = (err) => {
18821
+ if (!err) {
18822
+ try {
18823
+ applyDirAttributes(deferredDirAttr);
18824
+ } catch (er) {
18825
+ return callback(getError("Unable to set folder permissions", er.path || ""));
18826
+ }
18827
+ }
18828
+ callback(err);
18829
+ };
18830
+
16766
18831
  fileEntries.reverse().reduce(function (next, entry) {
16767
18832
  return function (err) {
16768
18833
  if (err) {
@@ -16780,21 +18845,19 @@ module.exports = function (/**String*/ input, /** object */ options) {
16780
18845
  const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
16781
18846
  filetools.writeFileToAsync(filePath, content, overwrite, fileAttr, function (succ) {
16782
18847
  if (!succ) {
16783
- next(getError("Unable to write file", filePath));
18848
+ return next(getError("Unable to write file", filePath));
16784
18849
  }
16785
- filetools.fs.utimes(filePath, entry.header.time, entry.header.time, function (err_2) {
16786
- if (err_2) {
16787
- next(getError("Unable to set times", filePath));
16788
- } else {
16789
- next();
16790
- }
18850
+ filetools.fs.utimes(filePath, entry.header.time, entry.header.time, function () {
18851
+ // best-effort: a utimes failure must not abort extraction
18852
+ // of already-written content (issue #379)
18853
+ next();
16791
18854
  });
16792
18855
  });
16793
18856
  }
16794
18857
  });
16795
18858
  }
16796
18859
  };
16797
- }, callback)();
18860
+ }, done)();
16798
18861
  },
16799
18862
 
16800
18863
  /**
@@ -16878,10 +18941,10 @@ module.exports = function (/**String*/ input, /** object */ options) {
16878
18941
 
16879
18942
  /***/ }),
16880
18943
 
16881
- /***/ 2916:
18944
+ /***/ 88436:
16882
18945
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
16883
18946
 
16884
- var Utils = __nccwpck_require__(28605),
18947
+ var Utils = __nccwpck_require__(91373),
16885
18948
  Constants = Utils.Constants;
16886
18949
 
16887
18950
  /* The central directory file header */
@@ -16970,6 +19033,7 @@ module.exports = function () {
16970
19033
  switch (val) {
16971
19034
  case Constants.STORED:
16972
19035
  this.version = 10;
19036
+ break;
16973
19037
  case Constants.DEFLATED:
16974
19038
  default:
16975
19039
  this.version = 20;
@@ -16981,6 +19045,7 @@ module.exports = function () {
16981
19045
  return Utils.fromDOS2Date(this.timeval);
16982
19046
  },
16983
19047
  set time(val) {
19048
+ val = new Date(val);
16984
19049
  this.timeval = Utils.fromDate2DOS(val);
16985
19050
  },
16986
19051
 
@@ -17103,6 +19168,8 @@ module.exports = function () {
17103
19168
  _localHeader.version = data.readUInt16LE(Constants.LOCVER);
17104
19169
  // general purpose bit flag
17105
19170
  _localHeader.flags = data.readUInt16LE(Constants.LOCFLG);
19171
+ // desc flag
19172
+ _localHeader.flags_desc = (_localHeader.flags & Constants.FLG_DESC) > 0;
17106
19173
  // compression method
17107
19174
  _localHeader.method = data.readUInt16LE(Constants.LOCHOW);
17108
19175
  // modification time (2 bytes time, 2 bytes date)
@@ -17169,7 +19236,11 @@ module.exports = function () {
17169
19236
  // version needed to extract
17170
19237
  data.writeUInt16LE(_version, Constants.LOCVER);
17171
19238
  // general purpose bit flag
17172
- data.writeUInt16LE(_flags, Constants.LOCFLG);
19239
+ // clear bit 3 (data descriptor): we always write the real crc-32
19240
+ // and sizes into this local header, so no trailing descriptor is
19241
+ // emitted. Leaving the flag set would make the output unreadable
19242
+ // (see issue #555).
19243
+ data.writeUInt16LE(_flags & ~Constants.FLG_DESC, Constants.LOCFLG);
17173
19244
  // compression method
17174
19245
  data.writeUInt16LE(_method, Constants.LOCHOW);
17175
19246
  // modification time (2 bytes time, 2 bytes date)
@@ -17197,7 +19268,9 @@ module.exports = function () {
17197
19268
  // version needed to extract
17198
19269
  data.writeUInt16LE(_version, Constants.CENVER);
17199
19270
  // encrypt, decrypt flags
17200
- data.writeUInt16LE(_flags, Constants.CENFLG);
19271
+ // clear bit 3 (data descriptor) to match the local header we emit
19272
+ // (real crc/sizes are written, no descriptor follows the data) — issue #555
19273
+ data.writeUInt16LE(_flags & ~Constants.FLG_DESC, Constants.CENFLG);
17201
19274
  // compression method
17202
19275
  data.writeUInt16LE(_method, Constants.CENHOW);
17203
19276
  // modification time (2 bytes time, 2 bytes date)
@@ -17259,19 +19332,19 @@ module.exports = function () {
17259
19332
 
17260
19333
  /***/ }),
17261
19334
 
17262
- /***/ 57405:
19335
+ /***/ 77837:
17263
19336
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
17264
19337
 
17265
- exports.EntryHeader = __nccwpck_require__(2916);
17266
- exports.MainHeader = __nccwpck_require__(81289);
19338
+ exports.EntryHeader = __nccwpck_require__(88436);
19339
+ exports.MainHeader = __nccwpck_require__(88889);
17267
19340
 
17268
19341
 
17269
19342
  /***/ }),
17270
19343
 
17271
- /***/ 81289:
19344
+ /***/ 88889:
17272
19345
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
17273
19346
 
17274
- var Utils = __nccwpck_require__(28605),
19347
+ var Utils = __nccwpck_require__(91373),
17275
19348
  Constants = Utils.Constants;
17276
19349
 
17277
19350
  /* The entries in the end of central directory */
@@ -17282,6 +19355,8 @@ module.exports = function () {
17282
19355
  _offset = 0,
17283
19356
  _commentLength = 0;
17284
19357
 
19358
+ const needsZip64 = () => _volumeEntries > Constants.EF_ZIP64_OR_16 || _totalEntries > Constants.EF_ZIP64_OR_16 || _size > Constants.EF_ZIP64_OR_32 || _offset > Constants.EF_ZIP64_OR_32;
19359
+
17285
19360
  return {
17286
19361
  get diskEntries() {
17287
19362
  return _volumeEntries;
@@ -17319,7 +19394,7 @@ module.exports = function () {
17319
19394
  },
17320
19395
 
17321
19396
  get mainHeaderSize() {
17322
- return Constants.ENDHDR + _commentLength;
19397
+ return (needsZip64() ? Constants.ZIP64HDR + Constants.END64HDR : 0) + Constants.ENDHDR + _commentLength;
17323
19398
  },
17324
19399
 
17325
19400
  loadFromBinary: function (/*Buffer*/ data) {
@@ -17349,7 +19424,7 @@ module.exports = function () {
17349
19424
  // total number of entries
17350
19425
  _totalEntries = Utils.readBigUInt64LE(data, Constants.ZIP64TOT);
17351
19426
  // central directory size in bytes
17352
- _size = Utils.readBigUInt64LE(data, Constants.ZIP64SIZE);
19427
+ _size = Utils.readBigUInt64LE(data, Constants.ZIP64SIZB);
17353
19428
  // offset of first CEN header
17354
19429
  _offset = Utils.readBigUInt64LE(data, Constants.ZIP64OFF);
17355
19430
 
@@ -17358,22 +19433,67 @@ module.exports = function () {
17358
19433
  },
17359
19434
 
17360
19435
  toBinary: function () {
17361
- var b = Buffer.alloc(Constants.ENDHDR + _commentLength);
19436
+ if (!needsZip64()) {
19437
+ var b = Buffer.alloc(Constants.ENDHDR + _commentLength);
19438
+ // "PK 05 06" signature
19439
+ b.writeUInt32LE(Constants.ENDSIG, 0);
19440
+ b.writeUInt32LE(0, 4);
19441
+ // number of entries on this volume
19442
+ b.writeUInt16LE(_volumeEntries, Constants.ENDSUB);
19443
+ // total number of entries
19444
+ b.writeUInt16LE(_totalEntries, Constants.ENDTOT);
19445
+ // central directory size in bytes
19446
+ b.writeUInt32LE(_size, Constants.ENDSIZ);
19447
+ // offset of first CEN header
19448
+ b.writeUInt32LE(_offset, Constants.ENDOFF);
19449
+ // zip file comment length
19450
+ b.writeUInt16LE(_commentLength, Constants.ENDCOM);
19451
+ // fill comment memory with spaces so no garbage is left there
19452
+ b.fill(" ", Constants.ENDHDR);
19453
+
19454
+ return b;
19455
+ }
19456
+
19457
+ var b = Buffer.alloc(this.mainHeaderSize);
19458
+ let offset = 0;
19459
+
19460
+ // Zip64 end of central directory record.
19461
+ b.writeUInt32LE(Constants.ZIP64SIG, offset);
19462
+ Utils.writeBigUInt64LE(b, Constants.ZIP64HDR - Constants.ZIP64LEAD, offset + Constants.ZIP64SIZE);
19463
+ b.writeUInt16LE(45, offset + Constants.ZIP64VEM);
19464
+ b.writeUInt16LE(45, offset + Constants.ZIP64VER);
19465
+ b.writeUInt32LE(0, offset + Constants.ZIP64DSK);
19466
+ b.writeUInt32LE(0, offset + Constants.ZIP64DSKDIR);
19467
+ Utils.writeBigUInt64LE(b, _volumeEntries, offset + Constants.ZIP64SUB);
19468
+ Utils.writeBigUInt64LE(b, _totalEntries, offset + Constants.ZIP64TOT);
19469
+ Utils.writeBigUInt64LE(b, _size, offset + Constants.ZIP64SIZB);
19470
+ Utils.writeBigUInt64LE(b, _offset, offset + Constants.ZIP64OFF);
19471
+
19472
+ const zip64EndOffset = _offset + _size;
19473
+ offset += Constants.ZIP64HDR;
19474
+
19475
+ // Zip64 end of central directory locator.
19476
+ b.writeUInt32LE(Constants.END64SIG, offset);
19477
+ b.writeUInt32LE(0, offset + Constants.END64START);
19478
+ Utils.writeBigUInt64LE(b, zip64EndOffset, offset + Constants.END64OFF);
19479
+ b.writeUInt32LE(1, offset + Constants.END64NUMDISKS);
19480
+ offset += Constants.END64HDR;
19481
+
17362
19482
  // "PK 05 06" signature
17363
- b.writeUInt32LE(Constants.ENDSIG, 0);
17364
- b.writeUInt32LE(0, 4);
19483
+ b.writeUInt32LE(Constants.ENDSIG, offset);
19484
+ b.writeUInt32LE(0, offset + 4);
17365
19485
  // number of entries on this volume
17366
- b.writeUInt16LE(_volumeEntries, Constants.ENDSUB);
19486
+ b.writeUInt16LE(Math.min(_volumeEntries, Constants.EF_ZIP64_OR_16), offset + Constants.ENDSUB);
17367
19487
  // total number of entries
17368
- b.writeUInt16LE(_totalEntries, Constants.ENDTOT);
19488
+ b.writeUInt16LE(Math.min(_totalEntries, Constants.EF_ZIP64_OR_16), offset + Constants.ENDTOT);
17369
19489
  // central directory size in bytes
17370
- b.writeUInt32LE(_size, Constants.ENDSIZ);
19490
+ b.writeUInt32LE(Math.min(_size, Constants.EF_ZIP64_OR_32), offset + Constants.ENDSIZ);
17371
19491
  // offset of first CEN header
17372
- b.writeUInt32LE(_offset, Constants.ENDOFF);
19492
+ b.writeUInt32LE(Math.min(_offset, Constants.EF_ZIP64_OR_32), offset + Constants.ENDOFF);
17373
19493
  // zip file comment length
17374
- b.writeUInt16LE(_commentLength, Constants.ENDCOM);
19494
+ b.writeUInt16LE(_commentLength, offset + Constants.ENDCOM);
17375
19495
  // fill comment memory with spaces so no garbage is left there
17376
- b.fill(" ", Constants.ENDHDR);
19496
+ b.fill(" ", offset + Constants.ENDHDR);
17377
19497
 
17378
19498
  return b;
17379
19499
  },
@@ -17405,7 +19525,7 @@ module.exports = function () {
17405
19525
 
17406
19526
  /***/ }),
17407
19527
 
17408
- /***/ 90858:
19528
+ /***/ 49466:
17409
19529
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
17410
19530
 
17411
19531
  module.exports = function (/*Buffer*/ inbuf) {
@@ -17445,20 +19565,20 @@ module.exports = function (/*Buffer*/ inbuf) {
17445
19565
 
17446
19566
  /***/ }),
17447
19567
 
17448
- /***/ 93133:
19568
+ /***/ 70493:
17449
19569
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
17450
19570
 
17451
- exports.Deflater = __nccwpck_require__(90858);
17452
- exports.Inflater = __nccwpck_require__(33938);
17453
- exports.ZipCrypto = __nccwpck_require__(12759);
19571
+ exports.Deflater = __nccwpck_require__(49466);
19572
+ exports.Inflater = __nccwpck_require__(27298);
19573
+ exports.ZipCrypto = __nccwpck_require__(19687);
17454
19574
 
17455
19575
 
17456
19576
  /***/ }),
17457
19577
 
17458
- /***/ 33938:
19578
+ /***/ 27298:
17459
19579
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
17460
19580
 
17461
- const version = +(process.versions ? process.versions.node : "").split(".")[0] || 0;
19581
+ const version = +(process?.versions?.node ?? "").split(".")[0] || 0;
17462
19582
 
17463
19583
  module.exports = function (/*Buffer*/ inbuf, /*number*/ expectedLength) {
17464
19584
  var zlib = __nccwpck_require__(43106);
@@ -17496,7 +19616,7 @@ module.exports = function (/*Buffer*/ inbuf, /*number*/ expectedLength) {
17496
19616
 
17497
19617
  /***/ }),
17498
19618
 
17499
- /***/ 12759:
19619
+ /***/ 19687:
17500
19620
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
17501
19621
 
17502
19622
  "use strict";
@@ -17505,7 +19625,7 @@ module.exports = function (/*Buffer*/ inbuf, /*number*/ expectedLength) {
17505
19625
  // node crypt, we use it for generate salt
17506
19626
  // eslint-disable-next-line node/no-unsupported-features/node-builtins
17507
19627
  const { randomFillSync } = __nccwpck_require__(76982);
17508
- const Errors = __nccwpck_require__(68978);
19628
+ const Errors = __nccwpck_require__(9314);
17509
19629
 
17510
19630
  // generate CRC32 lookup table
17511
19631
  const crctable = new Uint32Array(256).map((t, crc) => {
@@ -17679,7 +19799,7 @@ module.exports = { decrypt, encrypt, _salter };
17679
19799
 
17680
19800
  /***/ }),
17681
19801
 
17682
- /***/ 44436:
19802
+ /***/ 85604:
17683
19803
  /***/ ((module) => {
17684
19804
 
17685
19805
  module.exports = {
@@ -17828,7 +19948,7 @@ module.exports = {
17828
19948
 
17829
19949
  /***/ }),
17830
19950
 
17831
- /***/ 40801:
19951
+ /***/ 95825:
17832
19952
  /***/ ((module) => {
17833
19953
 
17834
19954
  module.exports = {
@@ -17840,7 +19960,7 @@ module.exports = {
17840
19960
 
17841
19961
  /***/ }),
17842
19962
 
17843
- /***/ 68978:
19963
+ /***/ 9314:
17844
19964
  /***/ ((__unused_webpack_module, exports) => {
17845
19965
 
17846
19966
  const errors = {
@@ -17910,7 +20030,7 @@ for (const msg of Object.keys(errors)) {
17910
20030
 
17911
20031
  /***/ }),
17912
20032
 
17913
- /***/ 62328:
20033
+ /***/ 71560:
17914
20034
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
17915
20035
 
17916
20036
  const pth = __nccwpck_require__(16928);
@@ -17993,25 +20113,25 @@ module.exports = function (/*String*/ path, /*Utils object*/ { fs }) {
17993
20113
 
17994
20114
  /***/ }),
17995
20115
 
17996
- /***/ 28605:
20116
+ /***/ 91373:
17997
20117
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
17998
20118
 
17999
- module.exports = __nccwpck_require__(32904);
18000
- module.exports.Constants = __nccwpck_require__(44436);
18001
- module.exports.Errors = __nccwpck_require__(68978);
18002
- module.exports.FileAttr = __nccwpck_require__(62328);
18003
- module.exports.decoder = __nccwpck_require__(40801);
20119
+ module.exports = __nccwpck_require__(12056);
20120
+ module.exports.Constants = __nccwpck_require__(85604);
20121
+ module.exports.Errors = __nccwpck_require__(9314);
20122
+ module.exports.FileAttr = __nccwpck_require__(71560);
20123
+ module.exports.decoder = __nccwpck_require__(95825);
18004
20124
 
18005
20125
 
18006
20126
  /***/ }),
18007
20127
 
18008
- /***/ 32904:
20128
+ /***/ 12056:
18009
20129
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
18010
20130
 
18011
20131
  const fsystem = __nccwpck_require__(79896);
18012
20132
  const pth = __nccwpck_require__(16928);
18013
- const Constants = __nccwpck_require__(44436);
18014
- const Errors = __nccwpck_require__(68978);
20133
+ const Constants = __nccwpck_require__(85604);
20134
+ const Errors = __nccwpck_require__(9314);
18015
20135
  const isWin = typeof process === "object" && "win32" === process.platform;
18016
20136
 
18017
20137
  const is_Obj = (obj) => typeof obj === "object" && obj !== null;
@@ -18059,7 +20179,11 @@ Utils.prototype.makeDir = function (/*String*/ folder) {
18059
20179
  try {
18060
20180
  stat = self.fs.statSync(resolvedPath);
18061
20181
  } catch (e) {
18062
- self.fs.mkdirSync(resolvedPath);
20182
+ if (e.message && e.message.startsWith('ENOENT')) {
20183
+ self.fs.mkdirSync(resolvedPath);
20184
+ } else {
20185
+ throw e;
20186
+ }
18063
20187
  }
18064
20188
  if (stat && stat.isFile()) throw Errors.FILE_IN_THE_WAY(`"${resolvedPath}"`);
18065
20189
  });
@@ -18113,39 +20237,51 @@ Utils.prototype.writeFileToAsync = function (/*String*/ path, /*Buffer*/ content
18113
20237
  if (exist && !overwrite) return callback(false);
18114
20238
 
18115
20239
  self.fs.stat(path, function (err, stat) {
18116
- if (exist && stat.isDirectory()) {
20240
+ if (exist && stat && stat.isDirectory()) {
18117
20241
  return callback(false);
18118
20242
  }
18119
20243
 
18120
20244
  var folder = pth.dirname(path);
18121
20245
  self.fs.exists(folder, function (exists) {
18122
- if (!exists) self.makeDir(folder);
20246
+ if (!exists) {
20247
+ // makeDir is synchronous and can throw (e.g. EACCES); report failure
20248
+ // rather than letting it escape this callback as an uncaught exception
20249
+ try {
20250
+ self.makeDir(folder);
20251
+ } catch (e) {
20252
+ return callback(false);
20253
+ }
20254
+ }
20255
+
20256
+ // write the content to an open descriptor, then apply the attributes
20257
+ const writeToFd = function (fd) {
20258
+ self.fs.write(fd, content, 0, content.length, 0, function (writeErr) {
20259
+ self.fs.close(fd, function () {
20260
+ // surface write failures instead of silently reporting success (issue #402)
20261
+ if (writeErr) return callback(false);
20262
+ self.fs.chmod(path, attr || 0o666, function () {
20263
+ callback(true);
20264
+ });
20265
+ });
20266
+ });
20267
+ };
18123
20268
 
18124
20269
  self.fs.open(path, "w", 0o666, function (err, fd) {
18125
20270
  if (err) {
20271
+ // the target may exist but be read-only: make it writable and retry once
18126
20272
  self.fs.chmod(path, 0o666, function () {
18127
- self.fs.open(path, "w", 0o666, function (err, fd) {
18128
- self.fs.write(fd, content, 0, content.length, 0, function () {
18129
- self.fs.close(fd, function () {
18130
- self.fs.chmod(path, attr || 0o666, function () {
18131
- callback(true);
18132
- });
18133
- });
18134
- });
20273
+ self.fs.open(path, "w", 0o666, function (retryErr, fd) {
20274
+ // Previously the retry error was ignored and an undefined fd was
20275
+ // passed to fs.write, throwing an uncaught ERR_INVALID_ARG_TYPE that
20276
+ // crashed the process (issues #470, #459, #402). Report failure instead.
20277
+ if (retryErr || !fd) return callback(false);
20278
+ writeToFd(fd);
18135
20279
  });
18136
20280
  });
18137
20281
  } else if (fd) {
18138
- self.fs.write(fd, content, 0, content.length, 0, function () {
18139
- self.fs.close(fd, function () {
18140
- self.fs.chmod(path, attr || 0o666, function () {
18141
- callback(true);
18142
- });
18143
- });
18144
- });
20282
+ writeToFd(fd);
18145
20283
  } else {
18146
- self.fs.chmod(path, attr || 0o666, function () {
18147
- callback(true);
18148
- });
20284
+ callback(false);
18149
20285
  }
18150
20286
  });
18151
20287
  });
@@ -18156,7 +20292,7 @@ Utils.prototype.writeFileToAsync = function (/*String*/ path, /*Buffer*/ content
18156
20292
  Utils.prototype.findFiles = function (/*String*/ path) {
18157
20293
  const self = this;
18158
20294
 
18159
- function findSync(/*String*/ dir, /*RegExp*/ pattern, /*Boolean*/ recursive) {
20295
+ function findSync(/*String*/ dir, /*RegExp*/ pattern, /*Boolean*/ recursive, /*Set*/ visited) {
18160
20296
  if (typeof pattern === "boolean") {
18161
20297
  recursive = pattern;
18162
20298
  pattern = undefined;
@@ -18170,12 +20306,22 @@ Utils.prototype.findFiles = function (/*String*/ path) {
18170
20306
  files.push(pth.normalize(path) + (stat.isDirectory() ? self.sep : ""));
18171
20307
  }
18172
20308
 
18173
- if (stat.isDirectory() && recursive) files = files.concat(findSync(path, pattern, recursive));
20309
+ if (stat.isDirectory() && recursive) {
20310
+ // Descend by resolved real path and skip directories we have already
20311
+ // visited. This stops a symlink that points back to an ancestor from
20312
+ // recursing forever until the path fails with ELOOP / ENAMETOOLONG
20313
+ // (issue #541).
20314
+ const realDir = self.fs.realpathSync(path);
20315
+ if (!visited.has(realDir)) {
20316
+ visited.add(realDir);
20317
+ files = files.concat(findSync(path, pattern, recursive, visited));
20318
+ }
20319
+ }
18174
20320
  });
18175
20321
  return files;
18176
20322
  }
18177
20323
 
18178
- return findSync(path, undefined, true);
20324
+ return findSync(path, undefined, true, new Set([self.fs.realpathSync(path)]));
18179
20325
  };
18180
20326
 
18181
20327
  /**
@@ -18193,29 +20339,54 @@ Utils.prototype.findFiles = function (/*String*/ path) {
18193
20339
  */
18194
20340
  Utils.prototype.findFilesAsync = function (dir, cb) {
18195
20341
  const self = this;
18196
- let results = [];
18197
- self.fs.readdir(dir, function (err, list) {
18198
- if (err) return cb(err);
18199
- let list_length = list.length;
18200
- if (!list_length) return cb(null, results);
18201
- list.forEach(function (file) {
18202
- file = pth.join(dir, file);
18203
- self.fs.stat(file, function (err, stat) {
18204
- if (err) return cb(err);
18205
- if (stat) {
20342
+ const results = [];
20343
+ let finished = false;
20344
+ const finish = function (err) {
20345
+ if (finished) return;
20346
+ finished = true;
20347
+ cb(err, err ? undefined : results);
20348
+ };
20349
+
20350
+ // Descend by resolved real path and skip directories already visited, so a
20351
+ // symlink pointing back to an ancestor cannot recurse forever (issue #541).
20352
+ const walk = function (dir, visited, done) {
20353
+ self.fs.readdir(dir, function (err, list) {
20354
+ if (err) return done(err);
20355
+ let pending = list.length;
20356
+ if (!pending) return done();
20357
+ list.forEach(function (name) {
20358
+ const file = pth.join(dir, name);
20359
+ self.fs.stat(file, function (err, stat) {
20360
+ if (err) return done(err);
20361
+ if (!stat) {
20362
+ if (!--pending) done();
20363
+ return;
20364
+ }
18206
20365
  results.push(pth.normalize(file) + (stat.isDirectory() ? self.sep : ""));
18207
- if (stat.isDirectory()) {
18208
- self.findFilesAsync(file, function (err, res) {
18209
- if (err) return cb(err);
18210
- results = results.concat(res);
18211
- if (!--list_length) cb(null, results);
18212
- });
18213
- } else {
18214
- if (!--list_length) cb(null, results);
20366
+ if (!stat.isDirectory()) {
20367
+ if (!--pending) done();
20368
+ return;
18215
20369
  }
18216
- }
20370
+ self.fs.realpath(file, function (err, realDir) {
20371
+ if (err) return done(err);
20372
+ if (visited.has(realDir)) {
20373
+ if (!--pending) done();
20374
+ return;
20375
+ }
20376
+ visited.add(realDir);
20377
+ walk(file, visited, function (err) {
20378
+ if (err) return done(err);
20379
+ if (!--pending) done();
20380
+ });
20381
+ });
20382
+ });
18217
20383
  });
18218
20384
  });
20385
+ };
20386
+
20387
+ self.fs.realpath(dir, function (err, realDir) {
20388
+ if (err) return finish(err);
20389
+ walk(dir, new Set([realDir]), finish);
18219
20390
  });
18220
20391
  };
18221
20392
 
@@ -18296,13 +20467,13 @@ Utils.findLast = function (arr, callback) {
18296
20467
  return void 0;
18297
20468
  };
18298
20469
 
18299
- // make abolute paths taking prefix as root folder
20470
+ // make absolute paths taking prefix as root folder
18300
20471
  Utils.sanitize = function (/*string*/ prefix, /*string*/ name) {
18301
20472
  prefix = pth.resolve(pth.normalize(prefix));
18302
20473
  var parts = name.split("/");
18303
20474
  for (var i = 0, l = parts.length; i < l; i++) {
18304
20475
  var path = pth.normalize(pth.join(prefix, parts.slice(i, l).join(pth.sep)));
18305
- if (path.indexOf(prefix) === 0) {
20476
+ if (path === prefix || path.startsWith(prefix + pth.sep)) {
18306
20477
  return path;
18307
20478
  }
18308
20479
  }
@@ -18322,10 +20493,16 @@ Utils.toBuffer = function toBuffer(/*buffer, Uint8Array, string*/ input, /* func
18322
20493
  };
18323
20494
 
18324
20495
  Utils.readBigUInt64LE = function (/*Buffer*/ buffer, /*int*/ index) {
18325
- var slice = Buffer.from(buffer.slice(index, index + 8));
18326
- slice.swap64();
20496
+ const lo = buffer.readUInt32LE(index);
20497
+ const hi = buffer.readUInt32LE(index + 4);
20498
+ return hi * 0x100000000 + lo;
20499
+ };
18327
20500
 
18328
- return parseInt(`0x${slice.toString("hex")}`);
20501
+ Utils.writeBigUInt64LE = function (/*Buffer*/ buffer, /*Number*/ value, /*int*/ index) {
20502
+ const lo = value >>> 0;
20503
+ const hi = Math.floor(value / 0x100000000) >>> 0;
20504
+ buffer.writeUInt32LE(lo, index);
20505
+ buffer.writeUInt32LE(hi, index + 4);
18329
20506
  };
18330
20507
 
18331
20508
  Utils.fromDOS2Date = function (val) {
@@ -18348,13 +20525,13 @@ Utils.crcTable = crcTable;
18348
20525
 
18349
20526
  /***/ }),
18350
20527
 
18351
- /***/ 59155:
20528
+ /***/ 58851:
18352
20529
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
18353
20530
 
18354
- var Utils = __nccwpck_require__(28605),
18355
- Headers = __nccwpck_require__(57405),
20531
+ var Utils = __nccwpck_require__(91373),
20532
+ Headers = __nccwpck_require__(77837),
18356
20533
  Constants = Utils.Constants,
18357
- Methods = __nccwpck_require__(93133);
20534
+ Methods = __nccwpck_require__(70493);
18358
20535
 
18359
20536
  module.exports = function (/** object */ options, /*Buffer*/ input) {
18360
20537
  var _centralHeader = new Headers.EntryHeader(),
@@ -18382,48 +20559,23 @@ module.exports = function (/** object */ options, /*Buffer*/ input) {
18382
20559
  }
18383
20560
 
18384
20561
  function crc32OK(data) {
18385
- // if bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the local header is written
18386
- if (!_centralHeader.flags_desc) {
18387
- if (Utils.crc32(data) !== _centralHeader.localHeader.crc) {
18388
- return false;
18389
- }
18390
- } else {
18391
- const descriptor = {};
18392
- const dataEndOffset = _centralHeader.realDataOffset + _centralHeader.compressedSize;
18393
- // no descriptor after compressed data, instead new local header
18394
- if (input.readUInt32LE(dataEndOffset) == Constants.LOCSIG || input.readUInt32LE(dataEndOffset) == Constants.CENSIG) {
18395
- throw Utils.Errors.DESCRIPTOR_NOT_EXIST();
18396
- }
18397
-
18398
- // get decriptor data
18399
- if (input.readUInt32LE(dataEndOffset) == Constants.EXTSIG) {
18400
- // descriptor with signature
18401
- descriptor.crc = input.readUInt32LE(dataEndOffset + Constants.EXTCRC);
18402
- descriptor.compressedSize = input.readUInt32LE(dataEndOffset + Constants.EXTSIZ);
18403
- descriptor.size = input.readUInt32LE(dataEndOffset + Constants.EXTLEN);
18404
- } else if (input.readUInt16LE(dataEndOffset + 12) === 0x4b50) {
18405
- // descriptor without signature (we check is new header starting where we expect)
18406
- descriptor.crc = input.readUInt32LE(dataEndOffset + Constants.EXTCRC - 4);
18407
- descriptor.compressedSize = input.readUInt32LE(dataEndOffset + Constants.EXTSIZ - 4);
18408
- descriptor.size = input.readUInt32LE(dataEndOffset + Constants.EXTLEN - 4);
18409
- } else {
18410
- throw Utils.Errors.DESCRIPTOR_UNKNOWN();
18411
- }
18412
-
18413
- // check data integrity
18414
- if (descriptor.compressedSize !== _centralHeader.compressedSize || descriptor.size !== _centralHeader.size || descriptor.crc !== _centralHeader.crc) {
18415
- throw Utils.Errors.DESCRIPTOR_FAULTY();
18416
- }
18417
- if (Utils.crc32(data) !== descriptor.crc) {
18418
- return false;
18419
- }
20562
+ // When bit 3 (0x08) of the general-purpose flags is set, the crc-32 and
20563
+ // sizes were unknown when the local file header was written, so that
20564
+ // header carries placeholder zeros and the real values are repeated in a
20565
+ // data descriptor after the compressed data. adm-zip always parses the
20566
+ // central directory, whose header holds the authoritative crc-32 and
20567
+ // sizes, so we validate the payload against that value.
20568
+ //
20569
+ // Earlier versions instead located and parsed the trailing descriptor and
20570
+ // threw when it was absent or in an unexpected shape. Many valid archives
20571
+ // set the descriptor flag but write the real crc/sizes into the local and
20572
+ // central headers without emitting a descriptor (or write one we did not
20573
+ // recognise), so that strict handling rejected readable zips
20574
+ // (issues #533, #548, #554). Trusting the central-directory crc keeps the
20575
+ // integrity check while accepting those archives.
20576
+ const expectedCrc = _centralHeader.flags_desc || _centralHeader.localHeader.flags_desc ? _centralHeader.crc : _centralHeader.localHeader.crc;
18420
20577
 
18421
- // @TODO: zip64 bit descriptor fields
18422
- // if bit 3 is set and any value in local header "zip64 Extended information" extra field are set 0 (place holder)
18423
- // then 64-bit descriptor format is used instead of 32-bit
18424
- // central header - "zip64 Extended information" extra field should store real values and not place holders
18425
- }
18426
- return true;
20578
+ return Utils.crc32(data) === expectedCrc;
18427
20579
  }
18428
20580
 
18429
20581
  function decompress(/*Boolean*/ async, /*Function*/ callback, /*String, Buffer*/ pass) {
@@ -18453,10 +20605,16 @@ module.exports = function (/** object */ options, /*Buffer*/ input) {
18453
20605
  compressedData = Methods.ZipCrypto.decrypt(compressedData, _centralHeader, pass);
18454
20606
  }
18455
20607
 
18456
- var data = Buffer.alloc(_centralHeader.size);
20608
+ var data;
18457
20609
 
18458
20610
  switch (_centralHeader.method) {
18459
20611
  case Utils.Constants.STORED:
20612
+ // STORED entries are not compressed, so the uncompressed output is
20613
+ // exactly the bytes present in the archive. Allocate from the real
20614
+ // data length rather than the attacker-declared central-directory
20615
+ // size, otherwise a tiny archive can declare a huge size and force a
20616
+ // multi-gigabyte allocation before any validation (CVE-2026-39244).
20617
+ data = Buffer.alloc(compressedData.length);
18460
20618
  compressedData.copy(data);
18461
20619
  if (!crc32OK(data)) {
18462
20620
  if (async && callback) callback(data, Utils.Errors.BAD_CRC()); //si added error
@@ -18467,17 +20625,19 @@ module.exports = function (/** object */ options, /*Buffer*/ input) {
18467
20625
  return data;
18468
20626
  }
18469
20627
  case Utils.Constants.DEFLATED:
20628
+ // Do not pre-allocate the declared uncompressed size. The inflater
20629
+ // grows its output buffer as zlib emits data and caps the total at
20630
+ // the declared size (maxOutputLength), so a bogus size can no longer
20631
+ // trigger an eager allocation before the data is read (CVE-2026-39244).
18470
20632
  var inflater = new Methods.Inflater(compressedData, _centralHeader.size);
18471
20633
  if (!async) {
18472
- const result = inflater.inflate(data);
18473
- result.copy(data, 0);
20634
+ data = inflater.inflate();
18474
20635
  if (!crc32OK(data)) {
18475
20636
  throw Utils.Errors.BAD_CRC(`"${decoder.decode(_entryName)}"`);
18476
20637
  }
18477
20638
  return data;
18478
20639
  } else {
18479
20640
  inflater.inflateAsync(function (result) {
18480
- result.copy(result, 0);
18481
20641
  if (callback) {
18482
20642
  if (!crc32OK(result)) {
18483
20643
  callback(result, Utils.Errors.BAD_CRC()); //si added error
@@ -18539,7 +20699,7 @@ module.exports = function (/** object */ options, /*Buffer*/ input) {
18539
20699
  }
18540
20700
 
18541
20701
  function readUInt64LE(buffer, offset) {
18542
- return (buffer.readUInt32LE(offset + 4) << 4) + buffer.readUInt32LE(offset);
20702
+ return Utils.readBigUInt64LE(buffer, offset);
18543
20703
  }
18544
20704
 
18545
20705
  function parseExtra(data) {
@@ -18633,10 +20793,12 @@ module.exports = function (/** object */ options, /*Buffer*/ input) {
18633
20793
  },
18634
20794
 
18635
20795
  get name() {
18636
- var n = decoder.decode(_entryName);
20796
+ const n = decoder.decode(_entryName);
20797
+ // For directories the name is the last path segment; drop the trailing
20798
+ // separator first so "a/b/c/" yields "c" and not "" (issue #466).
18637
20799
  return _isDirectory
18638
20800
  ? n
18639
- .substr(n.length - 1)
20801
+ .replace(/[/\\]$/, "")
18640
20802
  .split("/")
18641
20803
  .pop()
18642
20804
  : n.split("/").pop();
@@ -18760,16 +20922,19 @@ module.exports = function (/** object */ options, /*Buffer*/ input) {
18760
20922
 
18761
20923
  /***/ }),
18762
20924
 
18763
- /***/ 43985:
20925
+ /***/ 46593:
18764
20926
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
18765
20927
 
18766
- const ZipEntry = __nccwpck_require__(59155);
18767
- const Headers = __nccwpck_require__(57405);
18768
- const Utils = __nccwpck_require__(28605);
20928
+ const ZipEntry = __nccwpck_require__(58851);
20929
+ const Headers = __nccwpck_require__(77837);
20930
+ const Utils = __nccwpck_require__(91373);
18769
20931
 
18770
20932
  module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) {
18771
20933
  var entryList = [],
18772
- entryTable = {},
20934
+ // prototype-less: entry names come from untrusted input, so keys like
20935
+ // "__proto__" or "constructor" must be plain data, not touch the prototype
20936
+ // chain (which otherwise crashes addFile and hides such entries)
20937
+ entryTable = Object.create(null),
18773
20938
  _comment = Buffer.alloc(0),
18774
20939
  mainHeader = new Headers.MainHeader(),
18775
20940
  loadedEntries = false;
@@ -18819,7 +20984,7 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) {
18819
20984
 
18820
20985
  function readEntries() {
18821
20986
  loadedEntries = true;
18822
- entryTable = {};
20987
+ entryTable = Object.create(null);
18823
20988
  if (mainHeader.diskEntries > (inBuffer.length - mainHeader.offset) / Utils.Constants.CENHDR) {
18824
20989
  throw Utils.Errors.DISK_ENTRY_TOO_LARGE();
18825
20990
  }
@@ -18896,7 +21061,14 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) {
18896
21061
 
18897
21062
  function sortEntries() {
18898
21063
  if (entryList.length > 1 && !noSort) {
18899
- entryList.sort((a, b) => a.entryName.toLowerCase().localeCompare(b.entryName.toLowerCase()));
21064
+ // Decode + lowercase each name once rather than on every comparison:
21065
+ // the entryName getter re-decodes the underlying buffer on each access,
21066
+ // so the previous inline comparator did O(n log n) redundant decoding.
21067
+ // Ordering is unchanged (same localeCompare on the same keys).
21068
+ entryList = entryList
21069
+ .map((entry) => ({ entry, key: entry.entryName.toLowerCase() }))
21070
+ .sort((a, b) => a.key.localeCompare(b.key))
21071
+ .map((pair) => pair.entry);
18900
21072
  }
18901
21073
  }
18902
21074
 
@@ -19108,7 +21280,7 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) {
19108
21280
  // write main header
19109
21281
  const mh = mainHeader.toBinary();
19110
21282
  if (_comment) {
19111
- _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment
21283
+ _comment.copy(mh, mh.length - _comment.length); // add zip file comment
19112
21284
  }
19113
21285
  mh.copy(outBuffer, dindex);
19114
21286
 
@@ -19186,7 +21358,7 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) {
19186
21358
 
19187
21359
  const mh = mainHeader.toBinary();
19188
21360
  if (_comment) {
19189
- _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment
21361
+ _comment.copy(mh, mh.length - _comment.length); // add zip file comment
19190
21362
  }
19191
21363
 
19192
21364
  mh.copy(outBuffer, dindex); // write main header
@@ -128150,7 +130322,7 @@ module.exports = {"version":"3.17.0"};
128150
130322
  /***/ ((module) => {
128151
130323
 
128152
130324
  "use strict";
128153
- module.exports = /*#__PURE__*/JSON.parse('{"rE":"0.4.8","h_":"ckb development network for your first try"}');
130325
+ module.exports = /*#__PURE__*/JSON.parse('{"rE":"0.4.9-canary-807f2ef.0","h_":"ckb development network for your first try"}');
128154
130326
 
128155
130327
  /***/ })
128156
130328