neozip-cli 0.75.1-beta → 0.80.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 +30 -0
  2. package/DOCUMENTATION.md +9 -1
  3. package/README.md +22 -10
  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 +18 -10
  10. package/dist/src/neounzip.js +305 -60
  11. package/dist/src/neozip/blockchain.js +5 -5
  12. package/dist/src/neozip/createZip.js +207 -41
  13. package/dist/src/neozip/upgradeZip.js +182 -0
  14. package/dist/src/neozip.js +143 -8
  15. package/env.example +10 -0
  16. package/package.json +91 -85
  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()
@@ -608,9 +642,11 @@ async function testArchive(zip, options) {
608
642
  // Check bitFlags for encryption flag (bit 0)
609
643
  const isEncrypted = entry.isEncrypted || (entry.bitFlags & 0x01) !== 0;
610
644
  // 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 &&
645
+ const isContentFile = name !== neozip_blockchain_1.TOKENIZED_METADATA &&
646
+ name !== ots_1.TIMESTAMP_METADATA &&
647
+ name !== ots_1.TIMESTAMP_SUBMITTED &&
648
+ name !== zipstamp_server_1.SUBMIT_METADATA &&
649
+ name !== zipstamp_server_1.TIMESTAMP_METADATA &&
614
650
  !entry.isDirectory;
615
651
  if (isEncrypted) {
616
652
  // For encrypted files, decompression errors after decryption usually mean wrong password
@@ -730,7 +766,7 @@ async function preVerifyArchive(zip, archiveName, options) {
730
766
  let verificationSuccess = false;
731
767
  let verificationError;
732
768
  try {
733
- const verifier = new neozipkit_1.ZipkitVerifier({
769
+ const verifier = new neozip_blockchain_1.ZipkitVerifier({
734
770
  debug: options.verbose,
735
771
  skipHash: options.skipBlockchain
736
772
  });
@@ -791,21 +827,21 @@ async function preVerifyArchive(zip, archiveName, options) {
791
827
  const chainId = tokenMetadata.networkChainId;
792
828
  let networkConfig = null;
793
829
  if (chainId) {
794
- networkConfig = (0, blockchain_1.getContractConfig)(chainId);
830
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(chainId);
795
831
  }
796
832
  else {
797
833
  const networkName = tokenMetadata.network.toLowerCase();
798
834
  if (networkName.includes('base sepolia') || networkName === 'base-sepolia') {
799
- networkConfig = (0, blockchain_1.getContractConfig)(84532);
835
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(84532);
800
836
  }
801
837
  else if (networkName.includes('base mainnet') || networkName === 'base-mainnet' || networkName === 'base') {
802
- networkConfig = (0, blockchain_1.getContractConfig)(8453);
838
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(8453);
803
839
  }
804
840
  else if (networkName.includes('arbitrum sepolia') || networkName === 'arbitrum-sepolia' || (networkName.includes('arbitrum') && networkName.includes('sepolia'))) {
805
- networkConfig = (0, blockchain_1.getContractConfig)(421614);
841
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(421614);
806
842
  }
807
843
  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);
844
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(11155111);
809
845
  }
810
846
  }
811
847
  // Perform verification (non-interactive mode for pre-verify)
@@ -926,9 +962,11 @@ async function extractArchive(zip, destination, options) {
926
962
  // Filter out metadata files for merkle root calculation
927
963
  const contentEntriesForMerkle = filteredEntries.filter(entry => {
928
964
  const filename = entry.filename || '';
929
- return filename !== neozipkit_1.TOKENIZED_METADATA &&
930
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
931
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED &&
965
+ return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
966
+ filename !== ots_1.TIMESTAMP_METADATA &&
967
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
968
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
969
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
932
970
  !entry.isDirectory;
933
971
  });
934
972
  for (const entry of filteredEntries) {
@@ -1005,9 +1043,11 @@ async function extractArchive(zip, destination, options) {
1005
1043
  // Collect verified SHA-256 hash for merkle root calculation (single pass - no double check)
1006
1044
  // Only if this is a content file (not metadata) and SHA-256 verification was performed
1007
1045
  if (!options.skipBlockchain && hasSha) {
1008
- const isContentFile = filename !== neozipkit_1.TOKENIZED_METADATA &&
1009
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1010
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED &&
1046
+ const isContentFile = filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1047
+ filename !== ots_1.TIMESTAMP_METADATA &&
1048
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1049
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1050
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1011
1051
  !entry.isDirectory;
1012
1052
  if (isContentFile && entry.sha256) {
1013
1053
  // Use the verified hash from the entry (already verified during extraction)
@@ -1019,9 +1059,11 @@ async function extractArchive(zip, destination, options) {
1019
1059
  else {
1020
1060
  log(`error: ${filename} (${result.error || 'unknown error'})`, options);
1021
1061
  // 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 &&
1062
+ const isContentFile = filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1063
+ filename !== ots_1.TIMESTAMP_METADATA &&
1064
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1065
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1066
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1025
1067
  !entry.isDirectory;
1026
1068
  if (isContentFile && !options.skipBlockchain) {
1027
1069
  // Check if failure was due to SHA-256 mismatch
@@ -1051,9 +1093,12 @@ async function extractArchive(zip, destination, options) {
1051
1093
  async function verifyTokenization(zip, archiveName, options, extractionResult) {
1052
1094
  try {
1053
1095
  const entries = await getDirectory(zip, false);
1054
- const tokenEntry = entries.find((e) => (e.filename || '') === 'META-INF/NZIP.TOKEN');
1096
+ const tokenEntryLegacy = entries.find((e) => (e.filename || '') === 'META-INF/NZIP.TOKEN');
1097
+ const tokenEntryNzip = entries.find((e) => (e.filename || '') === 'META-INF/TOKEN.NZIP');
1098
+ const tokenEntry = tokenEntryNzip || tokenEntryLegacy;
1099
+ const tokenMetaFile = tokenEntry ? (tokenEntry.filename || 'META-INF/TOKEN.NZIP') : '';
1055
1100
  if (tokenEntry) {
1056
- console.log('\nTokenization detected (META-INF/NZIP.TOKEN)');
1101
+ console.log(`\nTokenization detected (${tokenMetaFile})`);
1057
1102
  }
1058
1103
  // Only do token verification if there's a token entry
1059
1104
  if (tokenEntry) {
@@ -1063,7 +1108,7 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1063
1108
  return;
1064
1109
  }
1065
1110
  // Initialize blockchain verifier
1066
- const verifier = new neozipkit_1.ZipkitVerifier({
1111
+ const verifier = new neozip_blockchain_1.ZipkitVerifier({
1067
1112
  debug: options.verbose, // Enable debug output in verbose mode
1068
1113
  skipHash: options.skipBlockchain
1069
1114
  });
@@ -1155,9 +1200,11 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1155
1200
  // Filter out blockchain metadata files to ensure consistent Merkle Root calculation
1156
1201
  const contentEntries = entries.filter(entry => {
1157
1202
  const filename = entry.filename || '';
1158
- return filename !== neozipkit_1.TOKENIZED_METADATA &&
1159
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1160
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED &&
1203
+ return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1204
+ filename !== ots_1.TIMESTAMP_METADATA &&
1205
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1206
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1207
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1161
1208
  !entry.isDirectory; // Skip directories
1162
1209
  });
1163
1210
  // Extract each file once and verify SHA-256 during extraction
@@ -1270,22 +1317,22 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1270
1317
  const chainId = tokenMetadata.networkChainId;
1271
1318
  let networkConfig = null;
1272
1319
  if (chainId) {
1273
- networkConfig = (0, blockchain_1.getContractConfig)(chainId);
1320
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(chainId);
1274
1321
  }
1275
1322
  else {
1276
1323
  // Fallback: try to determine chain ID from network name
1277
1324
  const networkName = tokenMetadata.network.toLowerCase();
1278
1325
  if (networkName.includes('base sepolia') || networkName === 'base-sepolia') {
1279
- networkConfig = (0, blockchain_1.getContractConfig)(84532);
1326
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(84532);
1280
1327
  }
1281
1328
  else if (networkName.includes('base mainnet') || networkName === 'base-mainnet' || networkName === 'base') {
1282
- networkConfig = (0, blockchain_1.getContractConfig)(8453);
1329
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(8453);
1283
1330
  }
1284
1331
  else if (networkName.includes('arbitrum sepolia') || networkName === 'arbitrum-sepolia' || (networkName.includes('arbitrum') && networkName.includes('sepolia'))) {
1285
- networkConfig = (0, blockchain_1.getContractConfig)(421614);
1332
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(421614);
1286
1333
  }
1287
1334
  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);
1335
+ networkConfig = (0, neozip_blockchain_1.getContractConfig)(11155111);
1289
1336
  }
1290
1337
  }
1291
1338
  let verificationResult;
@@ -1437,26 +1484,36 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1437
1484
  }
1438
1485
  // Display results based on verification outcome
1439
1486
  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
- }
1487
+ const d = verificationResult.verificationDetails;
1488
+ const meta = verificationResult.tokenMetadata;
1489
+ // Stamp type and summary
1490
+ console.log('\n🔐 Tokenization (NFT) Verification:');
1491
+ console.log(' - Status: VERIFIED');
1492
+ console.log(' - Stamp type: NZIP-NFT (tokenized archive)');
1493
+ if (d.contractAddress) {
1494
+ console.log(` - Contract: ${d.contractAddress}`);
1449
1495
  }
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
- }
1496
+ if (meta?.contractVersion) {
1497
+ console.log(` - Contract version: ${meta.contractVersion}`);
1498
+ }
1499
+ if (d.network) {
1500
+ console.log(` - Network: ${d.network}`);
1501
+ }
1502
+ if (d.tokenId) {
1503
+ console.log(` - Token ID: ${d.tokenId}`);
1504
+ }
1505
+ if (d.merkleRoot && d.calculatedMerkleRoot) {
1506
+ const rootsMatch = d.merkleRoot === d.calculatedMerkleRoot;
1507
+ console.log(` - Merkle root: ${d.merkleRoot}`);
1508
+ console.log(` ${rootsMatch ? '✓' : '✗'} Contract Merkle Root ${rootsMatch ? 'matches' : 'does not match'} calculated Merkle Root`);
1509
+ }
1510
+ if (d.mintDate) {
1511
+ console.log(` - Minted: ${d.mintDate}`);
1512
+ }
1513
+ console.log(`✅ ${verificationResult.message}`);
1514
+ console.log(`The archive, tokenized as NFT #${d.tokenId} on the ${d.network}, was minted on ${d.mintDate}.`);
1515
+ if (options.verbose && d.declaredMerkleRoot) {
1516
+ console.log(` Declared Merkle Root: ${d.declaredMerkleRoot}`);
1460
1517
  }
1461
1518
  }
1462
1519
  else {
@@ -1497,6 +1554,8 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1497
1554
  } // End of token verification if statement
1498
1555
  // Check for OpenTimestamp verification and upgrade
1499
1556
  await verifyAndUpgradeOts(zip, archiveName, options);
1557
+ // Check for Zipstamp verification
1558
+ await verifyZipstampTimestamp(zip, archiveName, options, extractionResult);
1500
1559
  }
1501
1560
  catch (e) {
1502
1561
  const msg = e instanceof Error ? e.message : String(e);
@@ -1520,7 +1579,7 @@ async function verifyAndUpgradeOts(zip, archiveName, options) {
1520
1579
  if (!otsEntry) {
1521
1580
  return;
1522
1581
  }
1523
- const res = await (0, neozipkit_1.verifyOtsZip)(zip);
1582
+ const res = await (0, ots_1.verifyOtsZip)(zip);
1524
1583
  console.log('\n🕐 OpenTimestamps Verification:');
1525
1584
  if (res.status === 'none') {
1526
1585
  console.log(' - Status: No OpenTimestamps found');
@@ -1539,10 +1598,12 @@ async function verifyAndUpgradeOts(zip, archiveName, options) {
1539
1598
  hour12: false
1540
1599
  });
1541
1600
  console.log(` - Status: ✅ VERIFIED`);
1601
+ console.log(` - Stamp type: OpenTimestamps (Bitcoin)`);
1542
1602
  console.log(` - Attested: Bitcoin block ${res.blockHeight} on ${date} ${time}`);
1543
1603
  }
1544
1604
  else {
1545
1605
  console.log(' - Status: ✅ VERIFIED');
1606
+ console.log(' - Stamp type: OpenTimestamps (Bitcoin)');
1546
1607
  }
1547
1608
  if (res.upgraded && res.upgradedOts) {
1548
1609
  let doUpgrade = false;
@@ -1605,6 +1666,182 @@ async function verifyAndUpgradeOts(zip, archiveName, options) {
1605
1666
  }
1606
1667
  }
1607
1668
  }
1669
+ /**
1670
+ * Verify Zipstamp timestamp if present
1671
+ * Uses our extraction logic (extractEntry/extractToFile) for compatibility with file-based ZIPs
1672
+ * When batch is confirmed but archive has TS-SUBMIT.NZIP, offers upgrade (prompt/auto/skip)
1673
+ */
1674
+ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResult) {
1675
+ try {
1676
+ if (options.skipBlockchain) {
1677
+ return;
1678
+ }
1679
+ const entries = await getDirectory(zip, false);
1680
+ const zipstampEntry = entries.find((e) => (e.filename || '') === 'META-INF/TS-SUBMIT.NZIP' ||
1681
+ (e.filename || '') === 'META-INF/TIMESTAMP.NZIP');
1682
+ if (!zipstampEntry) {
1683
+ return;
1684
+ }
1685
+ // Extract timestamp metadata using our extraction (works for both buffer and file-based)
1686
+ let timestampData = null;
1687
+ const bufferBased = isBufferBased(zip);
1688
+ const fileBased = isFileBased(zip);
1689
+ if (bufferBased) {
1690
+ const buf = await extractEntry(zip, zipstampEntry, true);
1691
+ if (buf) {
1692
+ try {
1693
+ timestampData = JSON.parse(buf.toString('utf-8'));
1694
+ }
1695
+ catch {
1696
+ timestampData = null;
1697
+ }
1698
+ }
1699
+ }
1700
+ else if (fileBased) {
1701
+ const tmpPath = path.join(require('os').tmpdir(), `neounzip-zipstamp-${Date.now()}-${(zipstampEntry.filename || 'meta').replace(/[^a-zA-Z0-9]/g, '_')}`);
1702
+ try {
1703
+ await zip.extractToFile(zipstampEntry, tmpPath, { skipHashCheck: true });
1704
+ const buf = fs.readFileSync(tmpPath);
1705
+ timestampData = JSON.parse(buf.toString('utf-8'));
1706
+ }
1707
+ catch {
1708
+ timestampData = null;
1709
+ }
1710
+ finally {
1711
+ if (fs.existsSync(tmpPath)) {
1712
+ try {
1713
+ fs.unlinkSync(tmpPath);
1714
+ }
1715
+ catch {
1716
+ /* ignore */
1717
+ }
1718
+ }
1719
+ }
1720
+ }
1721
+ if (!timestampData || !timestampData.digest) {
1722
+ console.log('\n🔗 Zipstamp Verification:');
1723
+ console.log(' - Status: ❌ ERROR');
1724
+ console.log(' - Error: Failed to extract timestamp metadata');
1725
+ return;
1726
+ }
1727
+ const merkleRoot = extractionResult?.merkleRoot ??
1728
+ zip.getMerkleRoot?.() ??
1729
+ null;
1730
+ if (!merkleRoot) {
1731
+ console.log('\n🔗 Zipstamp Verification:');
1732
+ console.log(' - Status: ❌ ERROR');
1733
+ console.log(' - Error: Merkle root not found in ZIP file');
1734
+ return;
1735
+ }
1736
+ if (timestampData.digest.toLowerCase() !== merkleRoot.toLowerCase()) {
1737
+ console.log('\n🔗 Zipstamp Verification:');
1738
+ console.log(' - Status: ❌ ERROR');
1739
+ console.log(' - Error: Timestamp digest does not match ZIP merkle root');
1740
+ return;
1741
+ }
1742
+ // Use verifyDigest directly - pass undefined for chainId when 0/invalid (server may reject chainId 0)
1743
+ const chainId = timestampData.chainId && timestampData.chainId > 0 ? timestampData.chainId : undefined;
1744
+ const batchId = timestampData.batchId || undefined;
1745
+ const response = await (0, zipstamp_server_1.verifyDigest)(merkleRoot, chainId, batchId, undefined, {
1746
+ debug: options.debug ?? false,
1747
+ });
1748
+ const res = response.success
1749
+ ? response.verified
1750
+ ? {
1751
+ status: 'valid',
1752
+ transactionHash: response.transactionHash,
1753
+ blockNumber: response.blockNumber,
1754
+ timestamp: response.timestamp,
1755
+ network: response.network,
1756
+ contractAddress: response.contractAddress,
1757
+ chainId: response.chainId,
1758
+ }
1759
+ : { status: 'pending', message: 'Timestamp is pending confirmation' }
1760
+ : { status: 'error', message: response.error || 'Verification failed' };
1761
+ console.log('\n🔗 Zipstamp Verification:');
1762
+ if (res.status === 'valid') {
1763
+ console.log(' - Status: ✅ VERIFIED');
1764
+ console.log(' - Stamp type: Zipstamp (blockchain timestamp)');
1765
+ if (res.network) {
1766
+ console.log(` - Network: ${res.network}`);
1767
+ }
1768
+ if (res.contractAddress) {
1769
+ console.log(` - Contract: ${res.contractAddress}`);
1770
+ }
1771
+ if (res.transactionHash) {
1772
+ console.log(` - Transaction: ${res.transactionHash}`);
1773
+ }
1774
+ if (res.blockNumber !== undefined) {
1775
+ console.log(` - Block: ${res.blockNumber}`);
1776
+ }
1777
+ if (res.timestamp !== undefined && res.timestamp != null) {
1778
+ const blockDate = new Date(res.timestamp * 1000);
1779
+ const mintedStr = blockDate.toLocaleString(undefined, {
1780
+ month: '2-digit',
1781
+ day: '2-digit',
1782
+ year: 'numeric',
1783
+ hour: '2-digit',
1784
+ minute: '2-digit',
1785
+ second: '2-digit',
1786
+ hour12: true,
1787
+ });
1788
+ console.log(` - Block timestamp (minted): ${mintedStr}`);
1789
+ }
1790
+ // Offer upgrade when archive has TS-SUBMIT.NZIP but batch is confirmed
1791
+ const hasPendingMetadata = (zipstampEntry.filename || '') === 'META-INF/TS-SUBMIT.NZIP';
1792
+ if (hasPendingMetadata) {
1793
+ let doUpgrade = false;
1794
+ if (options.zipstampSkipUpgrade) {
1795
+ doUpgrade = false;
1796
+ console.log(' - Upgrade: Skipped (--zipstamp-skip-upgrade)');
1797
+ }
1798
+ else if (options.zipstampAutoUpgrade) {
1799
+ doUpgrade = true;
1800
+ console.log(' - Upgrade: Automatically applying (--zipstamp-auto-upgrade)');
1801
+ }
1802
+ else {
1803
+ doUpgrade = await promptForZipstampUpgrade(archiveName);
1804
+ }
1805
+ if (doUpgrade) {
1806
+ await performZipstampUpgrade(archiveName);
1807
+ }
1808
+ else if (!options.zipstampSkipUpgrade && !options.zipstampAutoUpgrade) {
1809
+ console.log(' - Note: Run "neozip upgrade <archive>" to upgrade when ready');
1810
+ }
1811
+ }
1812
+ return;
1813
+ }
1814
+ if (res.status === 'pending') {
1815
+ console.log(' - Status: ⏳ PENDING (batch not yet confirmed on blockchain)');
1816
+ if (res.message) {
1817
+ console.log(` - Info: ${res.message}`);
1818
+ }
1819
+ console.log(' - Note: Run "neozip upgrade <archive>" to upgrade when confirmed');
1820
+ return;
1821
+ }
1822
+ if (res.status === 'error') {
1823
+ console.log(' - Status: ❌ ERROR');
1824
+ if (res.message) {
1825
+ console.log(` - Error: ${res.message}`);
1826
+ }
1827
+ return;
1828
+ }
1829
+ }
1830
+ catch (error) {
1831
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1832
+ console.log('\n🔗 Zipstamp Verification:');
1833
+ if (errorMessage.includes('ESOCKETTIMEDOUT') || errorMessage.includes('ECONNRESET') ||
1834
+ errorMessage.includes('ENOTFOUND') || errorMessage.includes('ECONNREFUSED') ||
1835
+ errorMessage.includes('timeout') || errorMessage.includes('network')) {
1836
+ console.log(' - Status: 📡 COMMUNICATION ERROR (unable to contact Zipstamp server)');
1837
+ console.log(` - Error: ${errorMessage}`);
1838
+ }
1839
+ else {
1840
+ console.log(' - Status: ❌ ERROR during verification');
1841
+ console.log(` - Error: ${errorMessage}`);
1842
+ }
1843
+ }
1844
+ }
1608
1845
  /**
1609
1846
  * Parse command line arguments
1610
1847
  */
@@ -1772,6 +2009,12 @@ function parseArgs(args) {
1772
2009
  case '--ots-skip-upgrade':
1773
2010
  options.otsSkipUpgrade = true;
1774
2011
  break;
2012
+ case '--zipstamp-auto-upgrade':
2013
+ options.zipstampAutoUpgrade = true;
2014
+ break;
2015
+ case '--zipstamp-skip-upgrade':
2016
+ options.zipstampSkipUpgrade = true;
2017
+ break;
1775
2018
  case '--in-memory':
1776
2019
  options.inMemory = true;
1777
2020
  break;
@@ -1854,6 +2097,8 @@ Options:
1854
2097
  -H, --hard-links Restore hard links efficiently (recreate link relationships)
1855
2098
  --ots-auto-upgrade Automatically upgrade OTS timestamps when available
1856
2099
  --ots-skip-upgrade Skip OTS timestamp upgrades (no prompt)
2100
+ --zipstamp-auto-upgrade Automatically upgrade Zipstamp timestamps when confirmed
2101
+ --zipstamp-skip-upgrade Skip Zipstamp timestamp upgrades (no prompt)
1857
2102
  --in-memory Force in-memory processing mode (browser compatible)
1858
2103
 
1859
2104
  Arguments:
@@ -1861,9 +2106,9 @@ Arguments:
1861
2106
  [destination] Output directory (default: current directory)
1862
2107
 
1863
2108
  Examples:
1864
- neounzip output/calgary.nzip ./extracted
1865
- neounzip -t output/calgary.nzip
1866
- neounzip -l output/calgary.nzip
2109
+ neounzip tests/output/calgary.nzip tests/extracted
2110
+ neounzip -t tests/output/calgary.nzip
2111
+ neounzip -l tests/output/calgary.nzip
1867
2112
  `);
1868
2113
  }
1869
2114
  /**
@@ -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,