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
@@ -46,11 +46,14 @@ const fs = __importStar(require("fs"));
46
46
  const path = __importStar(require("path"));
47
47
  const neozipkit_1 = require("neozipkit");
48
48
  const node_1 = __importDefault(require("neozipkit/node"));
49
- const blockchain_1 = require("neozipkit/blockchain");
49
+ const neozip_blockchain_1 = require("neozip-blockchain");
50
+ const ots_1 = require("neozip-blockchain/ots");
51
+ const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
50
52
  const CommentManager_1 = require("./CommentManager");
51
53
  const utils_1 = require("./utils");
52
54
  const file_operations_1 = require("./file-operations");
53
- const blockchain_2 = require("./blockchain");
55
+ const blockchain_1 = require("./blockchain");
56
+ const ConfigStore_1 = require("../config/ConfigStore");
54
57
  const user_interaction_1 = require("./user-interaction");
55
58
  const exit_codes_1 = require("../exit-codes");
56
59
  const version_1 = require("../version");
@@ -254,7 +257,7 @@ class ZipCreator {
254
257
  this.centralDirOffset = 0;
255
258
  this.zip = new node_1.default(); // Use ZipkitNode for ZIP operations
256
259
  // Initialize HashAccumulator if blockchain features are enabled
257
- if (this.options.blockchain || this.options.blockchainOts) {
260
+ if (this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp) {
258
261
  this.hashAccumulator = new neozipkit_1.HashCalculator({ enableAccumulation: true });
259
262
  }
260
263
  }
@@ -284,7 +287,7 @@ class ZipCreator {
284
287
  */
285
288
  async writeChunk(data) {
286
289
  // For in-memory non-blockchain mode, write to buffer
287
- if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts) {
290
+ if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts && !this.options.blockchainZipstamp) {
288
291
  const bufferWriter = this.zip.bufferWriter;
289
292
  if (bufferWriter) {
290
293
  // Determine if this is a header or data by checking signature
@@ -333,7 +336,7 @@ class ZipCreator {
333
336
  // Initialize output stream for file-based mode OR in-memory blockchain mode
334
337
  // For in-memory blockchain mode, we need to write directly to file during compression
335
338
  // For in-memory non-blockchain mode, output is written directly during compression
336
- if (!this.options.inMemory || (this.options.blockchain || this.options.blockchainOts)) {
339
+ if (!this.options.inMemory || (this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp)) {
337
340
  await this.initializeOutput();
338
341
  }
339
342
  await this.processFiles();
@@ -401,9 +404,12 @@ class ZipCreator {
401
404
  const compressionDesc = this.options.level === 0 ? 'store (no compression)' : `${this.options.compression} (level ${this.options.level})`;
402
405
  (0, utils_1.log)(`๐Ÿ”ง Compression: ${compressionDesc}`, this.options);
403
406
  (0, utils_1.log)(`๐Ÿ“Š Block size: ${(0, utils_1.formatBytes)(this.options.blockSize || 64 * 1024)}`, this.options);
404
- (0, utils_1.log)(`๐Ÿ”— Tokenization: ${this.options.blockchain ? 'enabled' : 'disabled'}`, this.options);
405
- const encryptionMethod = this.options.encryptionMethod || 'pkzip';
406
- (0, utils_1.log)(`๐Ÿ” Encryption: ${this.options.encrypt ? `${encryptionMethod.toUpperCase()} enabled` : 'disabled'}`, this.options);
407
+ if (!this.options.blockchainZipstamp && !this.options.blockchainOts) {
408
+ (0, utils_1.log)(`๐Ÿ”— Tokenization: ${this.options.blockchain ? 'enabled' : 'disabled'}`, this.options);
409
+ }
410
+ const encryptionMethod = this.options.encryptionMethod || 'aes256';
411
+ const encryptionLabel = encryptionMethod === 'aes256' ? 'AES-256' : 'PKZIP';
412
+ (0, utils_1.log)(`๐Ÿ” Encryption: ${this.options.encrypt ? `${encryptionLabel} enabled` : 'disabled'}`, this.options);
407
413
  (0, utils_1.log)('', this.options);
408
414
  if (this.options.debug) {
409
415
  (0, utils_1.logDebug)('Debug mode enabled', this.options);
@@ -433,6 +439,10 @@ class ZipCreator {
433
439
  (0, utils_1.log)(`Blockchain: enabled (OpenTimestamp)`, this.options);
434
440
  (0, utils_1.log)(`Bitcoin blockchain: enabled`, this.options);
435
441
  }
442
+ else if (this.options.blockchainZipstamp) {
443
+ (0, utils_1.log)(`Blockchain: enabled (Zipstamp)`, this.options);
444
+ (0, utils_1.log)(`Zipstamp: enabled`, this.options);
445
+ }
436
446
  else {
437
447
  (0, utils_1.log)(`Blockchain: disabled`, this.options);
438
448
  (0, utils_1.log)(`Tokenization: disabled`, this.options);
@@ -811,9 +821,9 @@ class ZipCreator {
811
821
  const compressionOptions = {
812
822
  level: level,
813
823
  useZstd: level === 0 ? false : (this.options.compression || 'zstd') === 'zstd',
814
- useSHA256: this.options.blockchain || this.options.blockchainOts || false,
824
+ useSHA256: this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp || false,
815
825
  password: this.options.encrypt ? this.options.password : null,
816
- encryptionMethod: this.options.encryptionMethod || 'pkzip'
826
+ encryptionMethod: (this.options.encryptionMethod === 'pkzip' ? 'zipcrypto' : 'aes256')
817
827
  };
818
828
  // Get entries from zip.getDirectory() (populated by processFiles())
819
829
  // This is the single source of truth for entry order
@@ -829,7 +839,7 @@ class ZipCreator {
829
839
  // Create ZipkitNode instance for buffer-based compression
830
840
  const zipkit = new node_1.default();
831
841
  // Determine if we need to write to file (for blockchain mode)
832
- const writeToFile = this.options.blockchain || this.options.blockchainOts;
842
+ const writeToFile = this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp;
833
843
  // Create appropriate output writer
834
844
  let writer;
835
845
  const positionRef = { current: this.currentPosition };
@@ -911,9 +921,11 @@ class ZipCreator {
911
921
  if (this.hashAccumulator && entry.sha256) {
912
922
  const filename = entry.filename || '';
913
923
  // Skip blockchain metadata files
914
- if (filename !== neozipkit_1.TOKENIZED_METADATA &&
915
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
916
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED) {
924
+ if (filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
925
+ filename !== ots_1.TIMESTAMP_METADATA &&
926
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
927
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
928
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA) {
917
929
  const hashBuffer = Buffer.from(entry.sha256, 'hex');
918
930
  this.hashAccumulator.addHash(hashBuffer);
919
931
  if (this.options.verbose) {
@@ -978,9 +990,11 @@ class ZipCreator {
978
990
  for (let index = 0; index < entryCnt; index++) {
979
991
  const entry = zipEntries[index];
980
992
  // Filter out metadata entries (handled separately in handleTokenization)
981
- if (entry.filename === neozipkit_1.TOKENIZED_METADATA ||
982
- entry.filename === neozipkit_1.TIMESTAMP_METADATA ||
983
- entry.filename === neozipkit_1.TIMESTAMP_SUBMITTED) {
993
+ if (entry.filename === neozip_blockchain_1.TOKENIZED_METADATA ||
994
+ entry.filename === ots_1.TIMESTAMP_METADATA ||
995
+ entry.filename === ots_1.TIMESTAMP_SUBMITTED ||
996
+ entry.filename === zipstamp_server_1.SUBMIT_METADATA ||
997
+ entry.filename === zipstamp_server_1.TIMESTAMP_METADATA) {
984
998
  zipEntries.splice(index, 1);
985
999
  entryCnt--;
986
1000
  index--;
@@ -1059,9 +1073,11 @@ class ZipCreator {
1059
1073
  if (this.hashAccumulator) {
1060
1074
  const filename = entry.filename || '';
1061
1075
  // Skip blockchain metadata files
1062
- if (filename !== neozipkit_1.TOKENIZED_METADATA &&
1063
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1064
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED) {
1076
+ if (filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1077
+ filename !== ots_1.TIMESTAMP_METADATA &&
1078
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1079
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1080
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA) {
1065
1081
  this.hashAccumulator.addHash(hash);
1066
1082
  if (this.options.verbose) {
1067
1083
  (0, utils_1.logDebug)(`Added SHA-256 hash to accumulator for ${filename}`, this.options);
@@ -1139,9 +1155,11 @@ class ZipCreator {
1139
1155
  // Filter out blockchain metadata files to ensure consistent Merkle Root calculation
1140
1156
  const contentEntries = entries.filter(entry => {
1141
1157
  const filename = entry.filename || '';
1142
- return filename !== neozipkit_1.TOKENIZED_METADATA &&
1143
- filename !== neozipkit_1.TIMESTAMP_METADATA &&
1144
- filename !== neozipkit_1.TIMESTAMP_SUBMITTED;
1158
+ return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1159
+ filename !== ots_1.TIMESTAMP_METADATA &&
1160
+ filename !== ots_1.TIMESTAMP_SUBMITTED &&
1161
+ filename !== zipstamp_server_1.SUBMIT_METADATA &&
1162
+ filename !== zipstamp_server_1.TIMESTAMP_METADATA;
1145
1163
  });
1146
1164
  // Add SHA-256 hashes to accumulator
1147
1165
  for (const entry of contentEntries) {
@@ -1158,12 +1176,13 @@ class ZipCreator {
1158
1176
  let tokenMeta = null;
1159
1177
  let tokenInfoEntry = null;
1160
1178
  let otsMetaEntry = null;
1179
+ let zipstampMetaEntry = null;
1161
1180
  let merkleRoot = null;
1162
1181
  if (this.options.blockchain) {
1163
1182
  // Check for wallet passkey AFTER compression is complete
1164
1183
  // Priority: -w option > wallet.json > environment variable
1165
1184
  // Don't show error yet - we'll prompt user if key is missing
1166
- const walletPasskey = (0, blockchain_2.getWalletPasskey)(this.options.walletKey, false);
1185
+ const walletPasskey = (0, blockchain_1.getWalletPasskey)(this.options.walletKey, false);
1167
1186
  if (!walletPasskey) {
1168
1187
  // Show error message and prompt user
1169
1188
  console.error('\nโŒ Error: Wallet private key is required for blockchain operations');
@@ -1186,6 +1205,7 @@ class ZipCreator {
1186
1205
  (0, utils_1.log)('โš ๏ธ Continuing without blockchain tokenization (ZIP file is already created)...', this.options);
1187
1206
  this.options.blockchain = false;
1188
1207
  this.options.blockchainOts = false;
1208
+ this.options.blockchainZipstamp = false;
1189
1209
  return; // Skip tokenization, ZIP is already complete
1190
1210
  }
1191
1211
  else {
@@ -1276,7 +1296,7 @@ class ZipCreator {
1276
1296
  // Always display network when tokenization is enabled
1277
1297
  console.log(`๐ŸŒ Network: ${network}`);
1278
1298
  // Get network config for contract address and version info
1279
- const networkConfig = (0, blockchain_1.getNetworkByName)(network);
1299
+ const networkConfig = (0, neozip_blockchain_1.getNetworkByName)(network);
1280
1300
  let contractAddress;
1281
1301
  let networkChainId;
1282
1302
  let contractVersion;
@@ -1290,7 +1310,7 @@ class ZipCreator {
1290
1310
  }
1291
1311
  else {
1292
1312
  // Fallback to default (Base Sepolia)
1293
- const defaultConfig = (0, blockchain_1.getContractConfig)(84532);
1313
+ const defaultConfig = (0, neozip_blockchain_1.getContractConfig)(84532);
1294
1314
  contractAddress = defaultConfig.address;
1295
1315
  networkChainId = defaultConfig.chainId;
1296
1316
  const versionStr = defaultConfig.version || '0';
@@ -1300,8 +1320,8 @@ class ZipCreator {
1300
1320
  // Display contract address and version right after network
1301
1321
  console.log(`๐Ÿ“„ Contract: ${contractAddress}`);
1302
1322
  console.log(`๐Ÿ”ข Version: ${contractVersion}`);
1303
- if (!(0, blockchain_2.isSupportedNetwork)(network)) {
1304
- const supportedNetworks = (0, blockchain_1.getSupportedNetworkNames)();
1323
+ if (!(0, blockchain_1.isSupportedNetwork)(network)) {
1324
+ const supportedNetworks = (0, neozip_blockchain_1.getSupportedNetworkNames)();
1305
1325
  console.error(`โŒ Error: Unsupported network: ${network}`);
1306
1326
  console.error(` Currently supported networks: ${supportedNetworks.join(', ')}`);
1307
1327
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BLOCKCHAIN_CONFIG_ERROR);
@@ -1309,7 +1329,7 @@ class ZipCreator {
1309
1329
  let minter = null;
1310
1330
  try {
1311
1331
  // Initialize blockchain minter
1312
- minter = new neozipkit_1.ZipkitMinter(merkleRoot, {
1332
+ minter = new neozip_blockchain_1.ZipkitMinter(merkleRoot, {
1313
1333
  network: network,
1314
1334
  walletPrivateKey: this.options.walletPasskey,
1315
1335
  verbose: this.options.verbose,
@@ -1380,7 +1400,7 @@ class ZipCreator {
1380
1400
  }
1381
1401
  else if (userChoice.action === 'mint-new') {
1382
1402
  // Mint new token
1383
- const mintResult = await (0, blockchain_2.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
1403
+ const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
1384
1404
  if (mintResult.success && mintResult.tokenInfo) {
1385
1405
  tokenMeta = mintResult.tokenInfo;
1386
1406
  // Add token metadata directly to ZIP (after compression, at the end)
@@ -1417,7 +1437,7 @@ class ZipCreator {
1417
1437
  }
1418
1438
  else {
1419
1439
  // No existing tokens, mint new one
1420
- const mintResult = await (0, blockchain_2.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
1440
+ const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
1421
1441
  if (mintResult.success && mintResult.tokenInfo) {
1422
1442
  tokenMeta = mintResult.tokenInfo;
1423
1443
  // Add token metadata directly to ZIP (after compression, at the end)
@@ -1537,16 +1557,16 @@ class ZipCreator {
1537
1557
  console.log(`๐Ÿ“‹ Merkle Root: ${merkleRoot}`);
1538
1558
  // Create actual OpenTimestamp proof
1539
1559
  try {
1540
- const otsProof = await (0, neozipkit_1.createTimestamp)(merkleRoot, { debug: this.options.verbose });
1560
+ const otsProof = await (0, ots_1.createTimestamp)(merkleRoot, { debug: this.options.verbose });
1541
1561
  if (!otsProof) {
1542
1562
  throw new Error('Failed to create OpenTimestamp proof');
1543
1563
  }
1544
1564
  console.log('๐Ÿ“„ OpenTimestamp metadata prepared');
1545
1565
  // Use the createOtsMetadataEntry function like in the example
1546
- const metaEntry = (0, blockchain_1.createOtsMetadataEntry)(this.zip, otsProof);
1566
+ const metaEntry = (0, ots_1.createOtsMetadataEntry)(this.zip, otsProof);
1547
1567
  if (metaEntry) {
1548
1568
  // Ensure the filename is set correctly
1549
- metaEntry.filename = neozipkit_1.TIMESTAMP_SUBMITTED;
1569
+ metaEntry.filename = ots_1.TIMESTAMP_SUBMITTED;
1550
1570
  // Ensure OTS entry uses STORED compression (no compression for small metadata file)
1551
1571
  metaEntry.cmpMethod = 0; // STORED
1552
1572
  metaEntry.compressedSize = metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0;
@@ -1586,10 +1606,143 @@ class ZipCreator {
1586
1606
  }
1587
1607
  }
1588
1608
  }
1609
+ else if (this.options.blockchainZipstamp) {
1610
+ // Handle Zipstamp timestamp for blockchain
1611
+ if (this.options.debug) {
1612
+ (0, utils_1.logDebug)('Zipstamp process starting...', this.options);
1613
+ }
1614
+ console.log('๐Ÿ”— Zipstamp: Creating timestamp proof on blockchain...');
1615
+ // Calculate merkle root (reuse same logic as OTS)
1616
+ let merkleRoot = null;
1617
+ if (this.hashAccumulator && this.hashAccumulator.leafCount() > 0) {
1618
+ merkleRoot = this.hashAccumulator.merkleRoot();
1619
+ if (this.options.verbose) {
1620
+ console.log(`โœ… Calculated merkle root from ${this.hashAccumulator.leafCount()} accumulated SHA-256 hashes`);
1621
+ }
1622
+ }
1623
+ else {
1624
+ let entriesForMerkle = [];
1625
+ const zipEntries = this.zip.getDirectory();
1626
+ if (zipEntries.length > 0) {
1627
+ entriesForMerkle = zipEntries;
1628
+ }
1629
+ else {
1630
+ try {
1631
+ if (fs.existsSync(this.archiveName)) {
1632
+ const zipkit = new node_1.default();
1633
+ if (this.options.inMemory) {
1634
+ const zipBuffer = fs.readFileSync(this.archiveName);
1635
+ zipkit.loadZip(zipBuffer);
1636
+ }
1637
+ else {
1638
+ await zipkit.loadZipFile(this.archiveName);
1639
+ }
1640
+ entriesForMerkle = zipkit.getDirectory() || [];
1641
+ }
1642
+ else {
1643
+ entriesForMerkle = await this.zip.getDirectory() || [];
1644
+ }
1645
+ }
1646
+ catch (error) {
1647
+ console.error('โŒ Error loading ZIP file for merkle root calculation:', error instanceof Error ? error.message : String(error));
1648
+ entriesForMerkle = await this.zip.getDirectory() || [];
1649
+ }
1650
+ }
1651
+ merkleRoot = this.calculateMerkleRootFromEntries(entriesForMerkle);
1652
+ }
1653
+ if (!merkleRoot) {
1654
+ console.error('โŒ Error calculating merkle root for Zipstamp');
1655
+ console.error(' Make sure files have been compressed with SHA-256 hashes.');
1656
+ if (this.options.verbose) {
1657
+ const hashCount = this.hashAccumulator ? this.hashAccumulator.leafCount() : 0;
1658
+ console.error(` Found ${hashCount} accumulated SHA-256 hashes`);
1659
+ }
1660
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
1661
+ }
1662
+ if (this.options.verbose && this.hashAccumulator) {
1663
+ console.log(`โœ… Calculated merkle root from ${this.hashAccumulator.leafCount()} accumulated SHA-256 hashes`);
1664
+ }
1665
+ if (merkleRoot) {
1666
+ console.log(`๐Ÿ“‹ Merkle Root: ${merkleRoot}`);
1667
+ const recipientEmail = this.options.timestampEmail ||
1668
+ process.env.NEOZIP_TIMESTAMP_EMAIL ||
1669
+ ConfigStore_1.ConfigStore.getConfig().timestampEmail;
1670
+ console.log('๐Ÿ”— Submitting hash digest to ZipStamp server...');
1671
+ console.log(` Zipstamp Server: ${(0, zipstamp_server_1.getZipStampServerUrl)()}`);
1672
+ console.log(`๐Ÿ“ง Email: ${recipientEmail || '(none set)'}`);
1673
+ try {
1674
+ const zipstampMetadata = await (0, zipstamp_server_1.createTimestamp)(merkleRoot, {
1675
+ recipientEmail: recipientEmail || undefined,
1676
+ debug: this.options.verbose
1677
+ });
1678
+ if (!zipstampMetadata) {
1679
+ throw new Error('Failed to create Zipstamp timestamp');
1680
+ }
1681
+ console.log('๐Ÿ“„ Zipstamp metadata prepared');
1682
+ const metaEntry = (0, zipstamp_server_1.createTimestampMetadataEntry)(this.zip, zipstampMetadata);
1683
+ if (metaEntry) {
1684
+ // createTimestampMetadataEntry sets filename based on status (TS-SUBMIT.NZIP or TIMESTAMP.NZIP)
1685
+ metaEntry.cmpMethod = 0; // STORED
1686
+ metaEntry.compressedSize = metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0;
1687
+ if (this.options.verbose) {
1688
+ console.log(`๐Ÿ“‚ Final timestamp entry: ${metaEntry.filename}`);
1689
+ console.log(`๐Ÿ“ Final timestamp entry size: ${metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0} bytes`);
1690
+ }
1691
+ console.log('โœ… Zipstamp proof created and queued for inclusion');
1692
+ const isConfirmed = (metaEntry.filename || '').includes('TIMESTAMP') && !(metaEntry.filename || '').includes('TS-SUBMIT');
1693
+ console.log(isConfirmed
1694
+ ? ' Timestamp proof is recorded on the blockchain.'
1695
+ : ' Timestamp proof will be recorded on the blockchain once confirmed.');
1696
+ metaEntry.localHdrOffset = this.currentPosition;
1697
+ if (this.zipWriter) {
1698
+ this.zipWriter.entryPositions.set(metaEntry.filename, this.currentPosition);
1699
+ }
1700
+ const localHdr = metaEntry.createLocalHdr();
1701
+ if (this.options.verbose) {
1702
+ (0, utils_1.log)(`๐Ÿ“ Writing Zipstamp entry: ${metaEntry.filename} at offset ${this.currentPosition}`, this.options);
1703
+ (0, utils_1.log)(`๐Ÿ“Š Zipstamp entry size: ${metaEntry.fileBuffer?.length || 0} bytes`, this.options);
1704
+ }
1705
+ await this.writeChunk(localHdr);
1706
+ if (metaEntry.fileBuffer) {
1707
+ await this.writeChunk(metaEntry.fileBuffer);
1708
+ }
1709
+ zipstampMetaEntry = metaEntry;
1710
+ }
1711
+ else {
1712
+ console.warn('โš ๏ธ Warning: Failed to create Zipstamp metadata entry');
1713
+ }
1714
+ }
1715
+ catch (error) {
1716
+ const errorMessage = error instanceof Error ? error.message : String(error);
1717
+ console.error('โŒ Failed to create Zipstamp proof:', errorMessage);
1718
+ const isValidationOrEmailError = errorMessage.toLowerCase().includes('validation') ||
1719
+ errorMessage.toLowerCase().includes('email') ||
1720
+ errorMessage.toLowerCase().includes('verify');
1721
+ if (isValidationOrEmailError) {
1722
+ console.error('');
1723
+ if (recipientEmail) {
1724
+ console.error(` Email used: ${recipientEmail}`);
1725
+ console.error('');
1726
+ }
1727
+ else {
1728
+ console.error(' No email was set (use --timestamp-email, NEOZIP_TIMESTAMP_EMAIL, or neozip verify-email).');
1729
+ console.error('');
1730
+ }
1731
+ console.error('๐Ÿ’ก Zipstamp requires a verified email. Run:');
1732
+ console.error(' neozip verify-email');
1733
+ console.error('');
1734
+ console.error(' Or use: --timestamp-email <your-verified-email>');
1735
+ console.error(' Or set: NEOZIP_TIMESTAMP_EMAIL environment variable');
1736
+ console.error('');
1737
+ }
1738
+ throw error;
1739
+ }
1740
+ }
1741
+ }
1589
1742
  }
1590
1743
  async finalize() {
1591
1744
  // Check if this is in-memory non-blockchain mode (needs special handling)
1592
- if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts) {
1745
+ if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts && !this.options.blockchainZipstamp) {
1593
1746
  // For in-memory non-blockchain mode, write complete ZIP file from buffer
1594
1747
  // Metadata should already be in the buffer (added via writeChunk() in handleTokenization())
1595
1748
  const bufferWriter = this.zip.bufferWriter;
@@ -1752,6 +1905,9 @@ async function testArchiveIntegrity(archiveName, options) {
1752
1905
  try {
1753
1906
  // Always show when integrity testing starts
1754
1907
  (0, utils_1.log)(`\n๐Ÿงช Testing archive integrity: ${archiveName}`, options);
1908
+ if (options.blockchainZipstamp) {
1909
+ (0, utils_1.log)(` (Validating file integrity only; timestamp not yet confirmed on blockchain)`, options);
1910
+ }
1755
1911
  // Import child_process for shelling out
1756
1912
  const { spawn } = require('child_process');
1757
1913
  // Use the installed neounzip command directly (works in both dev and installed)
@@ -1760,6 +1916,11 @@ async function testArchiveIntegrity(archiveName, options) {
1760
1916
  return new Promise((resolve) => {
1761
1917
  // Build neounzip command arguments
1762
1918
  const args = ['-t'];
1919
+ // For Zipstamp timestamped files: skip blockchain verification during integrity test.
1920
+ // We only validate file integrity (SHA-256); timestamp is pending and not yet minted.
1921
+ if (options.blockchainZipstamp) {
1922
+ args.push('--skip-blockchain');
1923
+ }
1763
1924
  // Add password if encryption is enabled
1764
1925
  if (options.encrypt && options.password) {
1765
1926
  args.push('-P', options.password);
@@ -1806,6 +1967,10 @@ async function testArchiveIntegrity(archiveName, options) {
1806
1967
  else {
1807
1968
  (0, utils_1.log)(`โœ… Archive integrity test passed`, options);
1808
1969
  }
1970
+ // For Zipstamp: clarify that timestamp is not validated and not minted
1971
+ if (options.blockchainZipstamp) {
1972
+ (0, utils_1.log)(` Note: File integrity validated. Timestamp is pending (not yet confirmed on blockchain).`, options);
1973
+ }
1809
1974
  resolve(true);
1810
1975
  }
1811
1976
  else {
@@ -1910,7 +2075,7 @@ async function upgradeZipForTokenization(inputZipPath, options) {
1910
2075
  }
1911
2076
  (0, utils_1.log)(` Found ${entries.length} entries in ZIP`, options);
1912
2077
  // Check for existing token metadata (ignore per requirement 3a)
1913
- const hasTokenMetadata = entries.some((e) => e.filename === neozipkit_1.TOKENIZED_METADATA);
2078
+ const hasTokenMetadata = entries.some((e) => e.filename === neozip_blockchain_1.TOKENIZED_METADATA);
1914
2079
  if (hasTokenMetadata && options.verbose) {
1915
2080
  (0, utils_1.log)(` Note: ZIP already contains token metadata (will create new token)`, options);
1916
2081
  }
@@ -1922,9 +2087,11 @@ async function upgradeZipForTokenization(inputZipPath, options) {
1922
2087
  for (const entry of entries) {
1923
2088
  const filename = entry.filename || '';
1924
2089
  // Skip metadata entries and directories
1925
- if (filename === neozipkit_1.TOKENIZED_METADATA ||
1926
- filename === neozipkit_1.TIMESTAMP_METADATA ||
1927
- filename === neozipkit_1.TIMESTAMP_SUBMITTED ||
2090
+ if (filename === neozip_blockchain_1.TOKENIZED_METADATA ||
2091
+ filename === ots_1.TIMESTAMP_METADATA ||
2092
+ filename === ots_1.TIMESTAMP_SUBMITTED ||
2093
+ filename === zipstamp_server_1.SUBMIT_METADATA ||
2094
+ filename === zipstamp_server_1.TIMESTAMP_METADATA ||
1928
2095
  entry.isDirectory) {
1929
2096
  continue;
1930
2097
  }
@@ -2080,7 +2247,7 @@ async function upgradeZipForTokenization(inputZipPath, options) {
2080
2247
  }
2081
2248
  // Mint new token before finalizing ZIP (so we can add metadata)
2082
2249
  // Check for wallet passkey - don't show error yet
2083
- const walletPasskey = (0, blockchain_2.getWalletPasskey)(options.walletKey, false);
2250
+ const walletPasskey = (0, blockchain_1.getWalletPasskey)(options.walletKey, false);
2084
2251
  if (!walletPasskey) {
2085
2252
  // Show error message and prompt user
2086
2253
  console.error('โŒ Error: Wallet private key is required for blockchain operations');
@@ -2118,10 +2285,10 @@ async function upgradeZipForTokenization(inputZipPath, options) {
2118
2285
  if (walletPasskey) {
2119
2286
  (0, utils_1.log)(` Minting new token on blockchain...`, options);
2120
2287
  const network = options.network || 'base-sepolia';
2121
- if (!(0, blockchain_2.isSupportedNetwork)(network)) {
2288
+ if (!(0, blockchain_1.isSupportedNetwork)(network)) {
2122
2289
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BLOCKCHAIN_CONFIG_ERROR, `Error: Unsupported network: ${network}`);
2123
2290
  }
2124
- const minter = new neozipkit_1.ZipkitMinter(merkleRoot, {
2291
+ const minter = new neozip_blockchain_1.ZipkitMinter(merkleRoot, {
2125
2292
  network,
2126
2293
  walletPrivateKey: walletPasskey,
2127
2294
  verbose: options.verbose,
@@ -2131,12 +2298,12 @@ async function upgradeZipForTokenization(inputZipPath, options) {
2131
2298
  // Create a temporary ZipkitNode instance for handleTokenMinting
2132
2299
  // (it needs a zip instance, but we'll add metadata manually)
2133
2300
  const tempZip = new node_1.default();
2134
- const mintResult = await (0, blockchain_2.handleTokenMinting)(minter, tempZip, options.nonInteractive || false, 0);
2301
+ const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, tempZip, options.nonInteractive || false, 0);
2135
2302
  if (mintResult.success && mintResult.tokenInfo) {
2136
2303
  // Add token metadata entry before central directory
2137
2304
  const tokenContent = JSON.stringify(mintResult.tokenInfo, null, 2);
2138
2305
  const tokenBuffer = Buffer.from(tokenContent, 'utf8');
2139
- const tokenEntry = new neozipkit_1.ZipEntry(neozipkit_1.TOKENIZED_METADATA, null, false);
2306
+ const tokenEntry = new neozipkit_1.ZipEntry(neozip_blockchain_1.TOKENIZED_METADATA, null, false);
2140
2307
  tokenEntry.timeDateDOS = tokenEntry.setDateTime(new Date());
2141
2308
  tokenEntry.compressedSize = tokenBuffer.length;
2142
2309
  tokenEntry.uncompressedSize = tokenBuffer.length;