neozip-cli 0.75.2-beta → 0.90.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 (31) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/DOCUMENTATION.md +20 -9
  3. package/README.md +55 -31
  4. package/dist/src/commands/mintTimestampProof.js +335 -0
  5. package/dist/src/commands/verifyEmail.js +146 -0
  6. package/dist/src/config/ConfigSetup.js +50 -20
  7. package/dist/src/config/ConfigStore.js +36 -3
  8. package/dist/src/index.js +1 -1
  9. package/dist/src/neolist.js +25 -11
  10. package/dist/src/neounzip.js +324 -66
  11. package/dist/src/neozip/blockchain.js +5 -5
  12. package/dist/src/neozip/createZip.js +211 -44
  13. package/dist/src/neozip/upgradeZip.js +182 -0
  14. package/dist/src/neozip.js +160 -24
  15. package/env.example +10 -0
  16. package/package.json +97 -82
  17. package/dist/neozipkit-bundles/blockchain.js +0 -13725
  18. package/dist/neozipkit-bundles/browser.js +0 -6186
  19. package/dist/neozipkit-bundles/core.js +0 -3839
  20. package/dist/neozipkit-bundles/node.js +0 -17730
  21. package/dist/neozipkit-wrappers/blockchain/core/contracts.js +0 -16
  22. package/dist/neozipkit-wrappers/blockchain/index.js +0 -2
  23. package/dist/neozipkit-wrappers/core/ZipDecompress.js +0 -2
  24. package/dist/neozipkit-wrappers/core/components/HashCalculator.js +0 -2
  25. package/dist/neozipkit-wrappers/core/components/Logger.js +0 -2
  26. package/dist/neozipkit-wrappers/core/constants/Errors.js +0 -2
  27. package/dist/neozipkit-wrappers/core/constants/Headers.js +0 -2
  28. package/dist/neozipkit-wrappers/core/encryption/ZipCrypto.js +0 -7
  29. package/dist/neozipkit-wrappers/core/index.js +0 -3
  30. package/dist/neozipkit-wrappers/index.js +0 -13
  31. package/dist/neozipkit-wrappers/node/index.js +0 -2
@@ -48,8 +48,10 @@ exports.showExtendedHelp = showExtendedHelp;
48
48
  const fs = __importStar(require("fs"));
49
49
  const path = __importStar(require("path"));
50
50
  const neozipkit_1 = require("neozipkit");
51
- const blockchain_1 = require("neozipkit/blockchain");
52
51
  const node_1 = __importDefault(require("neozipkit/node"));
52
+ const neozip_blockchain_1 = require("neozip-blockchain");
53
+ const ots_1 = require("neozip-blockchain/ots");
54
+ const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
53
55
  const readline = __importStar(require("readline"));
54
56
  const version_1 = require("./version");
55
57
  const exit_codes_1 = require("./exit-codes");
@@ -283,6 +285,34 @@ async function promptForTimeoutAction(networkConfig, currentRpcIndex, nonInterac
283
285
  });
284
286
  });
285
287
  }
288
+ /**
289
+ * Perform Zipstamp upgrade on the archive (TS-SUBMIT.NZIP -> TIMESTAMP.NZIP)
290
+ */
291
+ async function performZipstampUpgrade(archivePath) {
292
+ try {
293
+ // Dynamic import: use path without .js for ts-node; tsc emits .js for dist
294
+ // @ts-expect-error TS2835 - node16 requires .js but ts-node resolves .ts from source
295
+ const { upgradeZipForTimestamp } = await import('./neozip/upgradeZip');
296
+ await upgradeZipForTimestamp(archivePath, undefined, { verbose: false, debug: false });
297
+ console.log(' - Upgraded archive saved. Re-run neounzip to extract.');
298
+ }
299
+ catch (error) {
300
+ const msg = error instanceof Error ? error.message : String(error);
301
+ console.log(` - Upgrade failed: ${msg}`);
302
+ }
303
+ }
304
+ /**
305
+ * Prompt user for Zipstamp upgrade confirmation
306
+ */
307
+ function promptForZipstampUpgrade(archiveName) {
308
+ return new Promise((resolve) => {
309
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
310
+ rl.question(` - Batch confirmed. Upgrade archive to TIMESTAMP.NZIP? (y/N): `, (answer) => {
311
+ rl.close();
312
+ resolve(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes');
313
+ });
314
+ });
315
+ }
286
316
  /**
287
317
  * Perform OTS upgrade on the archive
288
318
  */
@@ -303,7 +333,7 @@ async function performOtsUpgrade(archivePath, upgradedOts) {
303
333
  fs.copyFileSync(archivePath, upgradedPath);
304
334
  console.log(` - Copied original file to upgrade location`);
305
335
  // Use the upgradeOTS function
306
- await (0, blockchain_1.upgradeOTS)(upgradedPath, upgradedOts);
336
+ await (0, ots_1.upgradeOTS)(upgradedPath, upgradedOts);
307
337
  console.log(' - Upgrade: Applied successfully');
308
338
  console.log(` - Note: Upgraded timestamp saved to ${upgradedPath}`);
309
339
  console.log(` - Original file ${archivePath} remains unchanged`);
@@ -549,9 +579,11 @@ async function testArchive(zip, options) {
549
579
  // Filter out metadata files for merkle root calculation
550
580
  const contentEntriesForMerkle = entries.filter(entry => {
551
581
  const filename = entry.filename || '';
552
- return filename !== neozipkit_1.TOKENIZED_METADATA &&
553
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
554
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED &&
582
+ return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
583
+ filename !== ots_1.TIMESTAMP_METADATA &&
584
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
585
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
586
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
555
587
  !entry.isDirectory;
556
588
  });
557
589
  for (const entry of entries) {
@@ -576,9 +608,11 @@ async function testArchive(zip, options) {
576
608
  });
577
609
  // Collect verified SHA-256 hash for merkle root calculation if available
578
610
  if (testResult.verifiedHash && !options.skipBlockchain) {
579
- const isContentFile = name !== neozipkit_1.TOKENIZED_METADATA &&
580
- name !== neozipkit_1.TIMESTAMP_METADATA &&
581
- name !== neozipkit_1.TIMESTAMP_SUBMITTED &&
611
+ const isContentFile = name !== neozip_blockchain_1.TOKENIZED_METADATA &&
612
+ name !== ots_1.TIMESTAMP_METADATA &&
613
+ name !== ots_1.TIMESTAMP_SUBMITTED &&
614
+ name !== zipstamp_server_1.SUBMIT_METADATA &&
615
+ name !== zipstamp_server_1.TIMESTAMP_METADATA &&
582
616
  !entry.isDirectory;
583
617
  if (isContentFile) {
584
618
  // Use the verified hash returned from testEntry()
@@ -591,14 +625,17 @@ async function testArchive(zip, options) {
591
625
  throw new Error('ZIP file not loaded. Cannot determine backend type.');
592
626
  }
593
627
  // Report success
628
+ const encTag = entry.isEncrypted
629
+ ? (entry.aesVersion > 0 ? ' [AES-256]' : ' [PKZIP]')
630
+ : '';
594
631
  if (entry.sha256 && !options.skipBlockchain) {
595
- console.log(`testing: ${name} ...OK SHA-256`);
632
+ console.log(`testing: ${name} ...OK SHA-256${encTag}`);
596
633
  }
597
634
  else if (entry.sha256 && options.skipBlockchain) {
598
- console.log(`testing: ${name} ...OK (SHA-256 skipped)`);
635
+ console.log(`testing: ${name} ...OK (SHA-256 skipped)${encTag}`);
599
636
  }
600
637
  else {
601
- console.log(`testing: ${name} ...OK`);
638
+ console.log(`testing: ${name} ...OK${encTag}`);
602
639
  }
603
640
  }
604
641
  catch (err) {
@@ -608,9 +645,11 @@ async function testArchive(zip, options) {
608
645
  // Check bitFlags for encryption flag (bit 0)
609
646
  const isEncrypted = entry.isEncrypted || (entry.bitFlags & 0x01) !== 0;
610
647
  // Track failed files for merkle root calculation
611
- const isContentFile = name !== neozipkit_1.TOKENIZED_METADATA &&
612
- name !== neozipkit_1.TIMESTAMP_METADATA &&
613
- name !== neozipkit_1.TIMESTAMP_SUBMITTED &&
648
+ const isContentFile = name !== neozip_blockchain_1.TOKENIZED_METADATA &&
649
+ name !== ots_1.TIMESTAMP_METADATA &&
650
+ name !== ots_1.TIMESTAMP_SUBMITTED &&
651
+ name !== zipstamp_server_1.SUBMIT_METADATA &&
652
+ name !== zipstamp_server_1.TIMESTAMP_METADATA &&
614
653
  !entry.isDirectory;
615
654
  if (isEncrypted) {
616
655
  // For encrypted files, decompression errors after decryption usually mean wrong password
@@ -730,7 +769,7 @@ async function preVerifyArchive(zip, archiveName, options) {
730
769
  let verificationSuccess = false;
731
770
  let verificationError;
732
771
  try {
733
- const verifier = new neozipkit_1.ZipkitVerifier({
772
+ const verifier = new neozip_blockchain_1.ZipkitVerifier({
734
773
  debug: options.verbose,
735
774
  skipHash: options.skipBlockchain
736
775
  });
@@ -791,21 +830,21 @@ async function preVerifyArchive(zip, archiveName, options) {
791
830
  const chainId = tokenMetadata.networkChainId;
792
831
  let networkConfig = null;
793
832
  if (chainId) {
794
- networkConfig = (0, blockchain_1.getContractConfig)(chainId);
833
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(chainId);
795
834
  }
796
835
  else {
797
836
  const networkName = tokenMetadata.network.toLowerCase();
798
837
  if (networkName.includes('base sepolia') || networkName === 'base-sepolia') {
799
- networkConfig = (0, blockchain_1.getContractConfig)(84532);
838
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(84532);
800
839
  }
801
840
  else if (networkName.includes('base mainnet') || networkName === 'base-mainnet' || networkName === 'base') {
802
- networkConfig = (0, blockchain_1.getContractConfig)(8453);
841
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(8453);
803
842
  }
804
843
  else if (networkName.includes('arbitrum sepolia') || networkName === 'arbitrum-sepolia' || (networkName.includes('arbitrum') && networkName.includes('sepolia'))) {
805
- networkConfig = (0, blockchain_1.getContractConfig)(421614);
844
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(421614);
806
845
  }
807
846
  else if (networkName.includes('sepolia testnet') || networkName === 'sepolia-testnet' || (networkName.includes('sepolia') && !networkName.includes('base') && !networkName.includes('arbitrum'))) {
808
- networkConfig = (0, blockchain_1.getContractConfig)(11155111);
847
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(11155111);
809
848
  }
810
849
  }
811
850
  // Perform verification (non-interactive mode for pre-verify)
@@ -926,9 +965,11 @@ async function extractArchive(zip, destination, options) {
926
965
  // Filter out metadata files for merkle root calculation
927
966
  const contentEntriesForMerkle = filteredEntries.filter(entry => {
928
967
  const filename = entry.filename || '';
929
- return filename !== neozipkit_1.TOKENIZED_METADATA &&
930
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
931
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED &&
968
+ return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
969
+ filename !== ots_1.TIMESTAMP_METADATA &&
970
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
971
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
972
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
932
973
  !entry.isDirectory;
933
974
  });
934
975
  for (const entry of filteredEntries) {
@@ -998,16 +1039,21 @@ async function extractArchive(zip, destination, options) {
998
1039
  totalBytes += result.bytesExtracted;
999
1040
  const hasSha = entry.sha256 ? true : false;
1000
1041
  const suffix = hasSha ? (options.skipBlockchain ? ' (SHA-256 skipped)' : ' (SHA-256 OK)') : '';
1042
+ const encTag = entry.isEncrypted
1043
+ ? (entry.aesVersion > 0 ? ' [AES-256]' : ' [PKZIP]')
1044
+ : '';
1001
1045
  if (options.verbose)
1002
- log(`extracting: ${filename} (${result.bytesExtracted} bytes)${suffix}`, options);
1046
+ log(`extracting: ${filename} (${result.bytesExtracted} bytes)${suffix}${encTag}`, options);
1003
1047
  else
1004
- log(`extracting: ${filename}${suffix}`, options);
1048
+ log(`extracting: ${filename}${suffix}${encTag}`, options);
1005
1049
  // Collect verified SHA-256 hash for merkle root calculation (single pass - no double check)
1006
1050
  // Only if this is a content file (not metadata) and SHA-256 verification was performed
1007
1051
  if (!options.skipBlockchain && hasSha) {
1008
- const isContentFile = filename !== neozipkit_1.TOKENIZED_METADATA &&
1009
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1010
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED &&
1052
+ const isContentFile = filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1053
+ filename !== ots_1.TIMESTAMP_METADATA &&
1054
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1055
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1056
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1011
1057
  !entry.isDirectory;
1012
1058
  if (isContentFile && entry.sha256) {
1013
1059
  // Use the verified hash from the entry (already verified during extraction)
@@ -1019,9 +1065,11 @@ async function extractArchive(zip, destination, options) {
1019
1065
  else {
1020
1066
  log(`error: ${filename} (${result.error || 'unknown error'})`, options);
1021
1067
  // Track failed files for merkle root calculation
1022
- const isContentFile = filename !== neozipkit_1.TOKENIZED_METADATA &&
1023
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1024
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED &&
1068
+ const isContentFile = filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1069
+ filename !== ots_1.TIMESTAMP_METADATA &&
1070
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1071
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1072
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1025
1073
  !entry.isDirectory;
1026
1074
  if (isContentFile && !options.skipBlockchain) {
1027
1075
  // Check if failure was due to SHA-256 mismatch
@@ -1051,9 +1099,12 @@ async function extractArchive(zip, destination, options) {
1051
1099
  async function verifyTokenization(zip, archiveName, options, extractionResult) {
1052
1100
  try {
1053
1101
  const entries = await getDirectory(zip, false);
1054
- const tokenEntry = entries.find((e) => (e.filename || '') === 'META-INF/NZIP.TOKEN');
1102
+ const tokenEntryLegacy = entries.find((e) => (e.filename || '') === 'META-INF/NZIP.TOKEN');
1103
+ const tokenEntryNzip = entries.find((e) => (e.filename || '') === 'META-INF/TOKEN.NZIP');
1104
+ const tokenEntry = tokenEntryNzip || tokenEntryLegacy;
1105
+ const tokenMetaFile = tokenEntry ? (tokenEntry.filename || 'META-INF/TOKEN.NZIP') : '';
1055
1106
  if (tokenEntry) {
1056
- console.log('\nTokenization detected (META-INF/NZIP.TOKEN)');
1107
+ console.log(`\nTokenization detected (${tokenMetaFile})`);
1057
1108
  }
1058
1109
  // Only do token verification if there's a token entry
1059
1110
  if (tokenEntry) {
@@ -1063,7 +1114,7 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1063
1114
  return;
1064
1115
  }
1065
1116
  // Initialize blockchain verifier
1066
- const verifier = new neozipkit_1.ZipkitVerifier({
1117
+ const verifier = new neozip_blockchain_1.ZipkitVerifier({
1067
1118
  debug: options.verbose, // Enable debug output in verbose mode
1068
1119
  skipHash: options.skipBlockchain
1069
1120
  });
@@ -1155,9 +1206,11 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1155
1206
  // Filter out blockchain metadata files to ensure consistent Merkle Root calculation
1156
1207
  const contentEntries = entries.filter(entry => {
1157
1208
  const filename = entry.filename || '';
1158
- return filename !== neozipkit_1.TOKENIZED_METADATA &&
1159
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1160
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED &&
1209
+ return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1210
+ filename !== ots_1.TIMESTAMP_METADATA &&
1211
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1212
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1213
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1161
1214
  !entry.isDirectory; // Skip directories
1162
1215
  });
1163
1216
  // Extract each file once and verify SHA-256 during extraction
@@ -1270,22 +1323,22 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1270
1323
  const chainId = tokenMetadata.networkChainId;
1271
1324
  let networkConfig = null;
1272
1325
  if (chainId) {
1273
- networkConfig = (0, blockchain_1.getContractConfig)(chainId);
1326
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(chainId);
1274
1327
  }
1275
1328
  else {
1276
1329
  // Fallback: try to determine chain ID from network name
1277
1330
  const networkName = tokenMetadata.network.toLowerCase();
1278
1331
  if (networkName.includes('base sepolia') || networkName === 'base-sepolia') {
1279
- networkConfig = (0, blockchain_1.getContractConfig)(84532);
1332
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(84532);
1280
1333
  }
1281
1334
  else if (networkName.includes('base mainnet') || networkName === 'base-mainnet' || networkName === 'base') {
1282
- networkConfig = (0, blockchain_1.getContractConfig)(8453);
1335
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(8453);
1283
1336
  }
1284
1337
  else if (networkName.includes('arbitrum sepolia') || networkName === 'arbitrum-sepolia' || (networkName.includes('arbitrum') && networkName.includes('sepolia'))) {
1285
- networkConfig = (0, blockchain_1.getContractConfig)(421614);
1338
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(421614);
1286
1339
  }
1287
1340
  else if (networkName.includes('sepolia testnet') || networkName === 'sepolia-testnet' || (networkName.includes('sepolia') && !networkName.includes('base') && !networkName.includes('arbitrum'))) {
1288
- networkConfig = (0, blockchain_1.getContractConfig)(11155111);
1341
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(11155111);
1289
1342
  }
1290
1343
  }
1291
1344
  let verificationResult;
@@ -1437,26 +1490,36 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1437
1490
  }
1438
1491
  // Display results based on verification outcome
1439
1492
  if (verificationResult.success && verificationResult.verificationDetails) {
1440
- // Show Merkle Root verification first
1441
- if (verificationResult.verificationDetails.merkleRoot && verificationResult.verificationDetails.calculatedMerkleRoot) {
1442
- const contractRoot = verificationResult.verificationDetails.merkleRoot;
1443
- const calculatedRoot = verificationResult.verificationDetails.calculatedMerkleRoot;
1444
- const rootsMatch = contractRoot === calculatedRoot;
1445
- console.log(`🔐 Merkle Root: ${contractRoot}`);
1446
- if (rootsMatch) {
1447
- console.log(`✓ Contract Merkle Root verified against calculated Merkle Root`);
1448
- }
1493
+ const d = verificationResult.verificationDetails;
1494
+ const meta = verificationResult.tokenMetadata;
1495
+ // Stamp type and summary
1496
+ console.log('\n🔐 Tokenization (NFT) Verification:');
1497
+ console.log(' - Status: VERIFIED');
1498
+ console.log(' - Stamp type: NZIP-NFT (tokenized archive)');
1499
+ if (d.contractAddress) {
1500
+ console.log(` - Contract: ${d.contractAddress}`);
1449
1501
  }
1450
- // Green checkmark for successful verification
1451
- // NOTE: All verification data (merkle root, mint date) comes from blockchain, not metadata
1452
- console.log(`✅ ${verificationResult.message}.`);
1453
- console.log(`The archive, tokenized as NFT #${verificationResult.verificationDetails.tokenId} on the ${verificationResult.verificationDetails.network}, was minted on ${verificationResult.verificationDetails.mintDate}.`);
1454
- if (options.verbose && verificationResult.verificationDetails) {
1455
- console.log(` Contract: ${verificationResult.verificationDetails.contractAddress}`);
1456
- console.log(` Network: ${verificationResult.verificationDetails.network}`);
1457
- if (verificationResult.verificationDetails.declaredMerkleRoot) {
1458
- console.log(` Declared: ${verificationResult.verificationDetails.declaredMerkleRoot}`);
1459
- }
1502
+ if (meta?.contractVersion) {
1503
+ console.log(` - Contract version: ${meta.contractVersion}`);
1504
+ }
1505
+ if (d.network) {
1506
+ console.log(` - Network: ${d.network}`);
1507
+ }
1508
+ if (d.tokenId) {
1509
+ console.log(` - Token ID: ${d.tokenId}`);
1510
+ }
1511
+ if (d.merkleRoot && d.calculatedMerkleRoot) {
1512
+ const rootsMatch = d.merkleRoot === d.calculatedMerkleRoot;
1513
+ console.log(` - Merkle root: ${d.merkleRoot}`);
1514
+ console.log(` ${rootsMatch ? '✓' : '✗'} Contract Merkle Root ${rootsMatch ? 'matches' : 'does not match'} calculated Merkle Root`);
1515
+ }
1516
+ if (d.mintDate) {
1517
+ console.log(` - Minted: ${d.mintDate}`);
1518
+ }
1519
+ console.log(`✅ ${verificationResult.message}`);
1520
+ console.log(`The archive, tokenized as NFT #${d.tokenId} on the ${d.network}, was minted on ${d.mintDate}.`);
1521
+ if (options.verbose && d.declaredMerkleRoot) {
1522
+ console.log(` Declared Merkle Root: ${d.declaredMerkleRoot}`);
1460
1523
  }
1461
1524
  }
1462
1525
  else {
@@ -1497,6 +1560,8 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1497
1560
  } // End of token verification if statement
1498
1561
  // Check for OpenTimestamp verification and upgrade
1499
1562
  await verifyAndUpgradeOts(zip, archiveName, options);
1563
+ // Check for Zipstamp verification
1564
+ await verifyZipstampTimestamp(zip, archiveName, options, extractionResult);
1500
1565
  }
1501
1566
  catch (e) {
1502
1567
  const msg = e instanceof Error ? e.message : String(e);
@@ -1520,7 +1585,7 @@ async function verifyAndUpgradeOts(zip, archiveName, options) {
1520
1585
  if (!otsEntry) {
1521
1586
  return;
1522
1587
  }
1523
- const res = await (0, neozipkit_1.verifyOtsZip)(zip);
1588
+ const res = await (0, ots_1.verifyOtsZip)(zip);
1524
1589
  console.log('\n🕐 OpenTimestamps Verification:');
1525
1590
  if (res.status === 'none') {
1526
1591
  console.log(' - Status: No OpenTimestamps found');
@@ -1539,10 +1604,12 @@ async function verifyAndUpgradeOts(zip, archiveName, options) {
1539
1604
  hour12: false
1540
1605
  });
1541
1606
  console.log(` - Status: ✅ VERIFIED`);
1607
+ console.log(` - Stamp type: OpenTimestamps (Bitcoin)`);
1542
1608
  console.log(` - Attested: Bitcoin block ${res.blockHeight} on ${date} ${time}`);
1543
1609
  }
1544
1610
  else {
1545
1611
  console.log(' - Status: ✅ VERIFIED');
1612
+ console.log(' - Stamp type: OpenTimestamps (Bitcoin)');
1546
1613
  }
1547
1614
  if (res.upgraded && res.upgradedOts) {
1548
1615
  let doUpgrade = false;
@@ -1605,6 +1672,182 @@ async function verifyAndUpgradeOts(zip, archiveName, options) {
1605
1672
  }
1606
1673
  }
1607
1674
  }
1675
+ /**
1676
+ * Verify Zipstamp timestamp if present
1677
+ * Uses our extraction logic (extractEntry/extractToFile) for compatibility with file-based ZIPs
1678
+ * When batch is confirmed but archive has TS-SUBMIT.NZIP, offers upgrade (prompt/auto/skip)
1679
+ */
1680
+ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResult) {
1681
+ try {
1682
+ if (options.skipBlockchain) {
1683
+ return;
1684
+ }
1685
+ const entries = await getDirectory(zip, false);
1686
+ const zipstampEntry = entries.find((e) => (e.filename || '') === 'META-INF/TS-SUBMIT.NZIP' ||
1687
+ (e.filename || '') === 'META-INF/TIMESTAMP.NZIP');
1688
+ if (!zipstampEntry) {
1689
+ return;
1690
+ }
1691
+ // Extract timestamp metadata using our extraction (works for both buffer and file-based)
1692
+ let timestampData = null;
1693
+ const bufferBased = isBufferBased(zip);
1694
+ const fileBased = isFileBased(zip);
1695
+ if (bufferBased) {
1696
+ const buf = await extractEntry(zip, zipstampEntry, true);
1697
+ if (buf) {
1698
+ try {
1699
+ timestampData = JSON.parse(buf.toString('utf-8'));
1700
+ }
1701
+ catch {
1702
+ timestampData = null;
1703
+ }
1704
+ }
1705
+ }
1706
+ else if (fileBased) {
1707
+ const tmpPath = path.join(require('os').tmpdir(), `neounzip-zipstamp-${Date.now()}-${(zipstampEntry.filename || 'meta').replace(/[^a-zA-Z0-9]/g, '_')}`);
1708
+ try {
1709
+ await zip.extractToFile(zipstampEntry, tmpPath, { skipHashCheck: true });
1710
+ const buf = fs.readFileSync(tmpPath);
1711
+ timestampData = JSON.parse(buf.toString('utf-8'));
1712
+ }
1713
+ catch {
1714
+ timestampData = null;
1715
+ }
1716
+ finally {
1717
+ if (fs.existsSync(tmpPath)) {
1718
+ try {
1719
+ fs.unlinkSync(tmpPath);
1720
+ }
1721
+ catch {
1722
+ /* ignore */
1723
+ }
1724
+ }
1725
+ }
1726
+ }
1727
+ if (!timestampData || !timestampData.digest) {
1728
+ console.log('\n🔗 Zipstamp Verification:');
1729
+ console.log(' - Status: ❌ ERROR');
1730
+ console.log(' - Error: Failed to extract timestamp metadata');
1731
+ return;
1732
+ }
1733
+ const merkleRoot = extractionResult?.merkleRoot ??
1734
+ zip.getMerkleRoot?.() ??
1735
+ null;
1736
+ if (!merkleRoot) {
1737
+ console.log('\n🔗 Zipstamp Verification:');
1738
+ console.log(' - Status: ❌ ERROR');
1739
+ console.log(' - Error: Merkle root not found in ZIP file');
1740
+ return;
1741
+ }
1742
+ if (timestampData.digest.toLowerCase() !== merkleRoot.toLowerCase()) {
1743
+ console.log('\n🔗 Zipstamp Verification:');
1744
+ console.log(' - Status: ❌ ERROR');
1745
+ console.log(' - Error: Timestamp digest does not match ZIP merkle root');
1746
+ return;
1747
+ }
1748
+ // Use verifyDigest directly - pass undefined for chainId when 0/invalid (server may reject chainId 0)
1749
+ const chainId = timestampData.chainId && timestampData.chainId > 0 ? timestampData.chainId : undefined;
1750
+ const batchId = timestampData.batchId || undefined;
1751
+ const response = await (0, zipstamp_server_1.verifyDigest)(merkleRoot, chainId, batchId, undefined, {
1752
+ debug: options.debug ?? false,
1753
+ });
1754
+ const res = response.success
1755
+ ? response.verified
1756
+ ? {
1757
+ status: 'valid',
1758
+ transactionHash: response.transactionHash,
1759
+ blockNumber: response.blockNumber,
1760
+ timestamp: response.timestamp,
1761
+ network: response.network,
1762
+ contractAddress: response.contractAddress,
1763
+ chainId: response.chainId,
1764
+ }
1765
+ : { status: 'pending', message: 'Timestamp is pending confirmation' }
1766
+ : { status: 'error', message: response.error || 'Verification failed' };
1767
+ console.log('\n🔗 Zipstamp Verification:');
1768
+ if (res.status === 'valid') {
1769
+ console.log(' - Status: ✅ VERIFIED');
1770
+ console.log(' - Stamp type: Zipstamp (blockchain timestamp)');
1771
+ if (res.network) {
1772
+ console.log(` - Network: ${res.network}`);
1773
+ }
1774
+ if (res.contractAddress) {
1775
+ console.log(` - Contract: ${res.contractAddress}`);
1776
+ }
1777
+ if (res.transactionHash) {
1778
+ console.log(` - Transaction: ${res.transactionHash}`);
1779
+ }
1780
+ if (res.blockNumber !== undefined) {
1781
+ console.log(` - Block: ${res.blockNumber}`);
1782
+ }
1783
+ if (res.timestamp !== undefined && res.timestamp != null) {
1784
+ const blockDate = new Date(res.timestamp * 1000);
1785
+ const mintedStr = blockDate.toLocaleString(undefined, {
1786
+ month: '2-digit',
1787
+ day: '2-digit',
1788
+ year: 'numeric',
1789
+ hour: '2-digit',
1790
+ minute: '2-digit',
1791
+ second: '2-digit',
1792
+ hour12: true,
1793
+ });
1794
+ console.log(` - Block timestamp (minted): ${mintedStr}`);
1795
+ }
1796
+ // Offer upgrade when archive has TS-SUBMIT.NZIP but batch is confirmed
1797
+ const hasPendingMetadata = (zipstampEntry.filename || '') === 'META-INF/TS-SUBMIT.NZIP';
1798
+ if (hasPendingMetadata) {
1799
+ let doUpgrade = false;
1800
+ if (options.zipstampSkipUpgrade) {
1801
+ doUpgrade = false;
1802
+ console.log(' - Upgrade: Skipped (--zipstamp-skip-upgrade)');
1803
+ }
1804
+ else if (options.zipstampAutoUpgrade) {
1805
+ doUpgrade = true;
1806
+ console.log(' - Upgrade: Automatically applying (--zipstamp-auto-upgrade)');
1807
+ }
1808
+ else {
1809
+ doUpgrade = await promptForZipstampUpgrade(archiveName);
1810
+ }
1811
+ if (doUpgrade) {
1812
+ await performZipstampUpgrade(archiveName);
1813
+ }
1814
+ else if (!options.zipstampSkipUpgrade && !options.zipstampAutoUpgrade) {
1815
+ console.log(' - Note: Run "neozip upgrade <archive>" to upgrade when ready');
1816
+ }
1817
+ }
1818
+ return;
1819
+ }
1820
+ if (res.status === 'pending') {
1821
+ console.log(' - Status: ⏳ PENDING (batch not yet confirmed on blockchain)');
1822
+ if (res.message) {
1823
+ console.log(` - Info: ${res.message}`);
1824
+ }
1825
+ console.log(' - Note: Run "neozip upgrade <archive>" to upgrade when confirmed');
1826
+ return;
1827
+ }
1828
+ if (res.status === 'error') {
1829
+ console.log(' - Status: ❌ ERROR');
1830
+ if (res.message) {
1831
+ console.log(` - Error: ${res.message}`);
1832
+ }
1833
+ return;
1834
+ }
1835
+ }
1836
+ catch (error) {
1837
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1838
+ console.log('\n🔗 Zipstamp Verification:');
1839
+ if (errorMessage.includes('ESOCKETTIMEDOUT') || errorMessage.includes('ECONNRESET') ||
1840
+ errorMessage.includes('ENOTFOUND') || errorMessage.includes('ECONNREFUSED') ||
1841
+ errorMessage.includes('timeout') || errorMessage.includes('network')) {
1842
+ console.log(' - Status: 📡 COMMUNICATION ERROR (unable to contact Zipstamp server)');
1843
+ console.log(` - Error: ${errorMessage}`);
1844
+ }
1845
+ else {
1846
+ console.log(' - Status: ❌ ERROR during verification');
1847
+ console.log(` - Error: ${errorMessage}`);
1848
+ }
1849
+ }
1850
+ }
1608
1851
  /**
1609
1852
  * Parse command line arguments
1610
1853
  */
@@ -1772,6 +2015,12 @@ function parseArgs(args) {
1772
2015
  case '--ots-skip-upgrade':
1773
2016
  options.otsSkipUpgrade = true;
1774
2017
  break;
2018
+ case '--zipstamp-auto-upgrade':
2019
+ options.zipstampAutoUpgrade = true;
2020
+ break;
2021
+ case '--zipstamp-skip-upgrade':
2022
+ options.zipstampSkipUpgrade = true;
2023
+ break;
1775
2024
  case '--in-memory':
1776
2025
  options.inMemory = true;
1777
2026
  break;
@@ -1843,7 +2092,7 @@ Options:
1843
2092
  --progress Enable enhanced progress reporting
1844
2093
  --debug Enable debug output
1845
2094
  -sf, --show-files Show files as they are extracted
1846
- -P, --password [pwd] Password for encrypted files. Provide password or will prompt
2095
+ -P, --password [pwd] Password for encrypted files (AES-256 and PKZIP supported)
1847
2096
  -x, --exclude <pattern> Exclude files matching pattern (can be used multiple times)
1848
2097
  -i, --include <pattern> Include only files matching pattern (can be used multiple times)
1849
2098
  -j, --junk-paths Extract files without directory structure (flatten to destination)
@@ -1854,6 +2103,8 @@ Options:
1854
2103
  -H, --hard-links Restore hard links efficiently (recreate link relationships)
1855
2104
  --ots-auto-upgrade Automatically upgrade OTS timestamps when available
1856
2105
  --ots-skip-upgrade Skip OTS timestamp upgrades (no prompt)
2106
+ --zipstamp-auto-upgrade Automatically upgrade Zipstamp timestamps when confirmed
2107
+ --zipstamp-skip-upgrade Skip Zipstamp timestamp upgrades (no prompt)
1857
2108
  --in-memory Force in-memory processing mode (browser compatible)
1858
2109
 
1859
2110
  Arguments:
@@ -1861,9 +2112,9 @@ Arguments:
1861
2112
  [destination] Output directory (default: current directory)
1862
2113
 
1863
2114
  Examples:
1864
- neounzip output/calgary.nzip ./extracted
1865
- neounzip -t output/calgary.nzip
1866
- neounzip -l output/calgary.nzip
2115
+ neounzip tests/output/calgary.nzip tests/extracted
2116
+ neounzip -t tests/output/calgary.nzip
2117
+ neounzip -l tests/output/calgary.nzip
1867
2118
  `);
1868
2119
  }
1869
2120
  /**
@@ -2025,6 +2276,13 @@ async function main() {
2025
2276
  log(`🚀 NEOUNZIP v${version_1.APP_VERSION} (${version_1.APP_RELEASE_DATE})`, options);
2026
2277
  log(`📦 Archive: ${archive}`, options);
2027
2278
  log(`📁 Destination: ${extractDestination}`, options);
2279
+ const allEntries = zip.getDirectory();
2280
+ const hasEncrypted = allEntries.some((e) => e.isEncrypted);
2281
+ if (hasEncrypted) {
2282
+ const hasAes = allEntries.some((e) => e.isEncrypted && e.aesVersion > 0);
2283
+ const encLabel = hasAes ? 'AES-256' : 'PKZIP';
2284
+ log(`🔐 Encryption: ${encLabel}`, options);
2285
+ }
2028
2286
  if (options.debug) {
2029
2287
  // Set global Logger level to 'debug' for debug output
2030
2288
  // Note: Individual class logging is controlled by their loggingEnabled static property
@@ -41,14 +41,14 @@ exports.getWalletPasskey = getWalletPasskey;
41
41
  exports.addTokenMetaToZip = addTokenMetaToZip;
42
42
  exports.handleTokenMinting = handleTokenMinting;
43
43
  const neozipkit_1 = require("neozipkit");
44
- const blockchain_1 = require("neozipkit/blockchain");
44
+ const neozip_blockchain_1 = require("neozip-blockchain");
45
45
  const ConfigStore_1 = require("../config/ConfigStore");
46
46
  const readline = __importStar(require("readline"));
47
47
  /**
48
48
  * Check if the network is supported (uses nameAliases from CONTRACT_CONFIGS)
49
49
  */
50
50
  function isSupportedNetwork(network) {
51
- return (0, blockchain_1.getChainIdByName)(network) !== null;
51
+ return (0, neozip_blockchain_1.getChainIdByName)(network) !== null;
52
52
  }
53
53
  /**
54
54
  * Check if the required wallet passkey is available and valid
@@ -78,7 +78,7 @@ function getWalletPasskey(cliWalletKey, showError = true) {
78
78
  return null;
79
79
  }
80
80
  // Validate the private key format
81
- if (!(0, blockchain_1.validatePrivateKey)(passkey)) {
81
+ if (!(0, neozip_blockchain_1.validatePrivateKey)(passkey)) {
82
82
  if (showError) {
83
83
  console.error('❌ Error: Invalid private key format');
84
84
  console.error(' Private key should be a 64-character hex string starting with 0x');
@@ -94,7 +94,7 @@ function getWalletPasskey(cliWalletKey, showError = true) {
94
94
  async function addTokenMetaToZip(zip, tokenMeta) {
95
95
  const tokenContent = JSON.stringify(tokenMeta, null, 2);
96
96
  const tokenBuffer = Buffer.from(tokenContent, 'utf8');
97
- const tokenPath = neozipkit_1.TOKENIZED_METADATA;
97
+ const tokenPath = neozip_blockchain_1.TOKENIZED_METADATA;
98
98
  // Create new token entry
99
99
  const tokenEntry = zip.createZipEntry(tokenPath);
100
100
  tokenEntry.timeDateDOS = tokenEntry.setDateTime(new Date());
@@ -193,7 +193,7 @@ async function handleTokenMinting(initialMinter, zip, nonInteractive = false, in
193
193
  else {
194
194
  // ZipkitMinter uses getChainIdByName internally which handles all nameAliases
195
195
  // So we can pass the original network name directly
196
- minter = new neozipkit_1.ZipkitMinter(merkleRoot, {
196
+ minter = new neozip_blockchain_1.ZipkitMinter(merkleRoot, {
197
197
  network: network,
198
198
  walletPrivateKey: walletPrivateKey,
199
199
  verbose: verbose,