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
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ /**
3
+ * Upgrade Zipstamp timestamp from pending (TS-SUBMIT.NZIP) to confirmed (TIMESTAMP.NZIP)
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.upgradeZipForTimestamp = upgradeZipForTimestamp;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const os = __importStar(require("os"));
46
+ const node_1 = __importDefault(require("neozipkit/node"));
47
+ const node_2 = require("neozipkit/node");
48
+ const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
49
+ const exit_codes_1 = require("../exit-codes");
50
+ /**
51
+ * Extract timestamp metadata from a ZIP entry.
52
+ * Tries the library's extractTimestampData first; if it fails (e.g. file-based ZIP),
53
+ * fall back to extracting to a temp file and parsing JSON.
54
+ */
55
+ async function extractTimestampMetadata(zip, entry) {
56
+ let metadata = await (0, zipstamp_server_1.extractTimestampData)(zip, entry);
57
+ if (metadata)
58
+ return metadata;
59
+ // Fallback for file-based ZIPs where zip.extract() may not work
60
+ try {
61
+ const tmpPath = path.join(os.tmpdir(), `neozip-upgrade-meta-${Date.now()}.json`);
62
+ await zip.extractToFile(entry, tmpPath, { skipHashCheck: true });
63
+ try {
64
+ const buf = fs.readFileSync(tmpPath);
65
+ return JSON.parse(buf.toString('utf-8'));
66
+ }
67
+ finally {
68
+ if (fs.existsSync(tmpPath))
69
+ fs.unlinkSync(tmpPath);
70
+ }
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ /**
77
+ * Upgrade a ZIP file with pending Zipstamp timestamp to confirmed status.
78
+ * Replaces TS-SUBMIT.NZIP with TIMESTAMP.NZIP when the batch is confirmed on the blockchain.
79
+ */
80
+ async function upgradeZipForTimestamp(inputPath, outputPath, options = {}) {
81
+ const { wait = false, verbose = false, debug = false } = options;
82
+ if (!fs.existsSync(inputPath)) {
83
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.CANT_FIND_ARCHIVE, `Error: ZIP file not found: ${inputPath}`);
84
+ }
85
+ const zip = new node_1.default();
86
+ await zip.loadZipFile(inputPath);
87
+ const entries = zip.getDirectory() || [];
88
+ const metadataResult = (0, zipstamp_server_1.findMetadataEntry)(entries);
89
+ if (!metadataResult) {
90
+ console.log('No Zipstamp timestamp found in ZIP file');
91
+ return;
92
+ }
93
+ if (metadataResult.type === 'confirmed') {
94
+ console.log('✓ Timestamp already confirmed - no upgrade needed');
95
+ return;
96
+ }
97
+ const metadata = await extractTimestampMetadata(zip, metadataResult.entry);
98
+ if (!metadata || !metadata.digest) {
99
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT, 'Error: Failed to extract timestamp metadata');
100
+ }
101
+ if (!(0, zipstamp_server_1.shouldUpgrade)(metadata, metadataResult.type)) {
102
+ console.log('✓ Timestamp does not need upgrade');
103
+ return;
104
+ }
105
+ const digest = metadata.digest;
106
+ const chainId = metadata.chainId && metadata.chainId > 0 ? metadata.chainId : undefined;
107
+ const batchId = metadata.batchId ?? undefined;
108
+ let verified = null;
109
+ if (wait) {
110
+ if (verbose || debug)
111
+ console.log('Polling for confirmation (--wait)...');
112
+ verified = await (0, zipstamp_server_1.pollForConfirmation)(digest, chainId, batchId, 300000, 5000, { debug });
113
+ }
114
+ else {
115
+ const verifyRes = await (0, zipstamp_server_1.verifyDigest)(digest, chainId, batchId, undefined, { debug });
116
+ if (verifyRes.verified) {
117
+ verified = verifyRes;
118
+ }
119
+ }
120
+ if (!verified || !verified.verified) {
121
+ console.log('⏳ Timestamp not yet confirmed on blockchain');
122
+ console.log(' Run with --wait to poll until confirmed, or try again later');
123
+ return;
124
+ }
125
+ console.log('✓ Timestamp confirmed on blockchain');
126
+ if (verified.transactionHash && (verbose || debug)) {
127
+ console.log(` Transaction: ${verified.transactionHash}`);
128
+ }
129
+ const confirmedMetadata = {
130
+ ...metadata,
131
+ status: 'confirmed',
132
+ merkleProof: verified.merkleProof ?? metadata.merkleProof,
133
+ merkleRoot: verified.merkleRoot ?? metadata.merkleRoot,
134
+ transactionHash: verified.transactionHash ?? metadata.transactionHash,
135
+ blockNumber: verified.blockNumber ?? metadata.blockNumber,
136
+ timestamp: verified.timestamp ?? metadata.timestamp
137
+ };
138
+ const metadataFiles = (0, zipstamp_server_1.getMetadataFileNames)();
139
+ const outPath = outputPath ?? inputPath.replace(/(\.nzip|\.zip)$/i, '-upgrade$1');
140
+ const tempDir = path.dirname(outPath) || '.';
141
+ const tempPath = path.join(tempDir, `.neozip-upgrade-${Date.now()}.tmp`);
142
+ try {
143
+ const zipCopy = new node_2.ZipCopyNode(new node_1.default());
144
+ const { destPath, dataEndOffset, copiedEntries } = await zipCopy.copyZipEntriesOnly(inputPath, tempPath, {
145
+ entryFilter: (entry) => !metadataFiles.includes(entry.filename || '')
146
+ });
147
+ const newEntry = (0, zipstamp_server_1.createTimestampMetadataEntry)(zip, confirmedMetadata);
148
+ if (!newEntry) {
149
+ throw new Error('Failed to create timestamp metadata entry');
150
+ }
151
+ newEntry.localHdrOffset = dataEndOffset;
152
+ newEntry.cmpMethod = 0;
153
+ newEntry.compressedSize = newEntry.fileBuffer?.length ?? newEntry.uncompressedSize ?? 0;
154
+ const localHdr = newEntry.createLocalHdr();
155
+ const data = newEntry.fileBuffer ?? Buffer.alloc(0);
156
+ const fd = fs.openSync(destPath, 'r+');
157
+ try {
158
+ fs.writeSync(fd, localHdr, 0, localHdr.length, dataEndOffset);
159
+ fs.writeSync(fd, data, 0, data.length, dataEndOffset + localHdr.length);
160
+ }
161
+ finally {
162
+ fs.closeSync(fd);
163
+ }
164
+ const allEntries = [...copiedEntries, newEntry];
165
+ zipCopy.writeCentralDirectoryAndEOCD(destPath, allEntries, { zipComment: '' });
166
+ fs.renameSync(destPath, outPath);
167
+ console.log(`✓ Upgraded ZIP written to: ${outPath}`);
168
+ }
169
+ catch (error) {
170
+ if (fs.existsSync(tempPath)) {
171
+ try {
172
+ fs.unlinkSync(tempPath);
173
+ }
174
+ catch {
175
+ // Ignore cleanup errors
176
+ }
177
+ }
178
+ const msg = error instanceof Error ? error.message : String(error);
179
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.CANT_WRITE_ARCHIVE, `Error upgrading ZIP: ${msg}`);
180
+ }
181
+ }
182
+ //# sourceMappingURL=upgradeZip.js.map
@@ -67,10 +67,19 @@ const user_interaction_1 = require("./neozip/user-interaction");
67
67
  const createZip_1 = require("./neozip/createZip");
68
68
  const file_operations_1 = require("./neozip/file-operations");
69
69
  // ConfigSetup is lazy-loaded only when needed (init/config commands)
70
- const blockchain_1 = require("neozipkit/blockchain");
70
+ const neozip_blockchain_1 = require("neozip-blockchain");
71
71
  const ConfigStore_1 = require("./config/ConfigStore");
72
72
  const exit_codes_1 = require("./exit-codes");
73
73
  const version_1 = require("./version");
74
+ /**
75
+ * Resolve dynamic import path so ts-node loads .ts and compiled dist loads .js
76
+ */
77
+ function resolveScriptPath(jsPath) {
78
+ if (typeof __filename !== 'undefined' && __filename.endsWith('.ts')) {
79
+ return jsPath.replace(/\.js$/, '.ts');
80
+ }
81
+ return jsPath;
82
+ }
74
83
  /**
75
84
  * Parse command line arguments
76
85
  */
@@ -85,6 +94,7 @@ function parseArgs(args) {
85
94
  blockchainMint: false, // Force mint new token disabled by default
86
95
  network: 'base-sepolia',
87
96
  walletPasskey: process.env.NEOZIP_WALLET_PASSKEY,
97
+ timestampEmail: process.env.NEOZIP_TIMESTAMP_EMAIL,
88
98
  compression: 'zstd',
89
99
  blockSize: 128 * 1024, // 128KB default
90
100
  enableProgress: true,
@@ -168,6 +178,17 @@ function parseArgs(args) {
168
178
  case '--opentimestamp':
169
179
  options.blockchainOts = true;
170
180
  break;
181
+ case '-ts':
182
+ case '--timestamp':
183
+ options.blockchainZipstamp = true;
184
+ break;
185
+ case '--timestamp-email':
186
+ options.timestampEmail = args[++i];
187
+ if (!options.timestampEmail) {
188
+ console.error('Error: --timestamp-email requires an email address');
189
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
190
+ }
191
+ break;
171
192
  case '-bt':
172
193
  case '--blockchain-token':
173
194
  options.blockchain = true;
@@ -185,8 +206,8 @@ function parseArgs(args) {
185
206
  case '-n':
186
207
  case '--network':
187
208
  const network = args[++i];
188
- if ((0, blockchain_1.getChainIdByName)(network) === null) {
189
- const supportedNetworks = (0, blockchain_1.getSupportedNetworkNames)();
209
+ if ((0, neozip_blockchain_1.getChainIdByName)(network) === null) {
210
+ const supportedNetworks = (0, neozip_blockchain_1.getSupportedNetworkNames)();
190
211
  console.error(`Error: Unsupported network: "${network}"`);
191
212
  console.error(`Supported networks: ${supportedNetworks.join(', ')}`);
192
213
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
@@ -239,45 +260,45 @@ function parseArgs(args) {
239
260
  break;
240
261
  case '-e':
241
262
  case '--encrypt':
242
- // InfoZip-compatible encryption option
243
263
  options.encrypt = true;
244
- // Only set encryption method if not already set
245
264
  if (!options.encryptionMethod) {
246
- options.encryptionMethod = 'pkzip'; // Default to PKZIP (InfoZip compatible)
265
+ options.encryptionMethod = 'aes256';
247
266
  }
248
- // Only prompt for password if not already provided
249
267
  if (!options.password) {
250
- options.password = 'PROMPT'; // Will prompt for password
268
+ options.password = 'PROMPT';
251
269
  }
252
270
  break;
253
271
  case '-P':
254
272
  case '--password':
255
- // InfoZip-compatible password option (uppercase P)
256
273
  options.encrypt = true;
257
- // Only set encryption method if not already set (e.g., by --pkzip-encrypt)
258
274
  if (!options.encryptionMethod) {
259
- options.encryptionMethod = 'pkzip'; // Default to PKZIP
275
+ options.encryptionMethod = 'aes256';
260
276
  }
261
277
  if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
262
- // Password provided directly
263
278
  options.password = args[++i];
264
279
  }
265
280
  else {
266
- // No password provided, will prompt later
267
281
  options.password = 'PROMPT';
268
282
  }
269
283
  break;
270
284
  case '-p':
271
285
  case '--show-progress':
272
- // InfoZip-compatible show-progress option (lowercase p)
273
286
  options.showFiles = true;
274
287
  break;
288
+ case '--aes256':
289
+ case '--aes-256':
290
+ options.encrypt = true;
291
+ options.encryptionMethod = 'aes256';
292
+ if (!options.password) {
293
+ options.password = 'PROMPT';
294
+ }
295
+ break;
296
+ case '--pkzip':
275
297
  case '--pkzip-encrypt':
276
298
  options.encrypt = true;
277
299
  options.encryptionMethod = 'pkzip';
278
- // Only prompt for password if not already provided
279
300
  if (!options.password) {
280
- options.password = 'PROMPT'; // Will prompt for password
301
+ options.password = 'PROMPT';
281
302
  }
282
303
  break;
283
304
  case '-x':
@@ -411,6 +432,12 @@ function parseArgs(args) {
411
432
  break;
412
433
  }
413
434
  }
435
+ // -ots and -ts are mutually exclusive (choose Bitcoin OTS or Ethereum Zipstamp)
436
+ if (options.blockchainOts && options.blockchainZipstamp) {
437
+ console.error('Error: Cannot use both -ots (OpenTimestamps) and -ts (Zipstamp) in the same run');
438
+ console.error(' Use -ts for Zipstamp timestamping or -ots for Bitcoin timestamping');
439
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
440
+ }
414
441
  if (!archive) {
415
442
  console.error('Error: Archive name is required');
416
443
  showHelp();
@@ -450,6 +477,9 @@ Usage: neozip [options] <archive> <files...>
450
477
  Configuration Commands:
451
478
  neozip init Interactive wallet setup wizard
452
479
  neozip config Show current configuration and manage settings (interactive menu)
480
+ neozip upgrade <archive> [output] [--wait] Upgrade pending Zipstamp timestamp to confirmed
481
+ neozip mint <archive> [output] Mint TimestampProofNFT from confirmed timestamped ZIP
482
+ neozip verify-email [--email <email>] [--set-default] Configure email for Zipstamp timestamping
453
483
 
454
484
  Options:
455
485
  -h, --help Show this help message
@@ -478,6 +508,8 @@ Options:
478
508
  -bd, --blockchain-default Auto-select default token option (use existing or mint new)
479
509
  -bm, --blockchain-mint Force mint new token (skip existing token selection)
480
510
  -ots, --opentimestamp Enable OpenTimestamp proof on Bitcoin blockchain
511
+ -ts, --timestamp Enable Ethereum Zipstamp timestamp (blockchain)
512
+ --timestamp-email <email> Email for Zipstamp server (some servers require verified email)
481
513
  -n, --network <network> Blockchain network (base-sepolia, base-mainnet, default: base-sepolia)
482
514
  -w, --wallet-key <key> Wallet private key (overrides NEOZIP_WALLET_PASSKEY)
483
515
  -c Add one-line comments for files
@@ -487,10 +519,11 @@ Options:
487
519
  --block-size <bytes> Block size for streaming (default: 131072)
488
520
  --no-progress Disable progress reporting
489
521
  --progress Enable enhanced progress reporting
490
- -e, --encrypt Encrypt files (InfoZip-compatible). Will prompt for password
491
- -P, --password [pwd] Encrypt files with password (InfoZip-compatible). Provide password or will prompt
522
+ -e, --encrypt Encrypt files with AES-256 (default). Will prompt for password
523
+ -P, --password [pwd] Encrypt files with password. Provide password or will prompt
524
+ --aes256 Use AES-256 encryption (default, recommended)
525
+ --pkzip, --pkzip-encrypt Use legacy PKZIP encryption (weak, for compatibility only)
492
526
  -p, --show-progress Show progress during compression (InfoZip-compatible)
493
- --pkzip-encrypt Use PKZIP encryption (legacy compatibility only, same as -e)
494
527
  -x, --exclude <pattern> Exclude files matching pattern (can be used multiple times)
495
528
  -i, --include <pattern> Include only files matching pattern (can be used multiple times)
496
529
  --suffixes <list> Don't compress files with these suffixes (can be used multiple times)
@@ -509,9 +542,9 @@ Arguments:
509
542
  <files...> Files and directories to compress
510
543
 
511
544
  Examples:
512
- neozip output/test.nzip file1.txt file2.txt
513
- neozip output/simple.nzip ./data/
514
- neozip -r output/recursive.nzip ./project/
545
+ neozip tests/output/test.nzip file1.txt file2.txt
546
+ neozip tests/output/simple.nzip ./data/
547
+ neozip -r tests/output/recursive.nzip ./project/
515
548
  `);
516
549
  }
517
550
  /**
@@ -671,7 +704,7 @@ async function main() {
671
704
  // Handle `neozip init` - interactive setup wizard
672
705
  if (command === 'init') {
673
706
  // Lazy-load ConfigSetup only when needed
674
- const { ConfigSetup } = await import('./config/ConfigSetup.js');
707
+ const { ConfigSetup } = await import(resolveScriptPath('./config/ConfigSetup.js'));
675
708
  const wizard = new ConfigSetup();
676
709
  const success = await wizard.run();
677
710
  process.exit(success ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
@@ -679,10 +712,113 @@ async function main() {
679
712
  // Handle `neozip config` - show interactive menu
680
713
  if (command === 'config') {
681
714
  // Lazy-load ConfigSetup only when needed
682
- const { ConfigSetup } = await import('./config/ConfigSetup.js');
715
+ const { ConfigSetup } = await import(resolveScriptPath('./config/ConfigSetup.js'));
683
716
  await ConfigSetup.showAndManage();
684
717
  // showAndManage handles its own exit
685
718
  }
719
+ // Handle `neozip verify-email` - configure email for Zipstamp timestamping
720
+ if (command === 'verify-email') {
721
+ const verifyArgs = args.slice(1);
722
+ let email;
723
+ let setDefaultOnly = false;
724
+ let debug = false;
725
+ for (let i = 0; i < verifyArgs.length; i++) {
726
+ const arg = verifyArgs[i];
727
+ if (arg === '--email' && verifyArgs[i + 1]) {
728
+ email = verifyArgs[++i];
729
+ }
730
+ else if (arg === '--set-default') {
731
+ setDefaultOnly = true;
732
+ }
733
+ else if (arg === '--debug') {
734
+ debug = true;
735
+ }
736
+ }
737
+ const { runVerifyEmail } = await import(resolveScriptPath('./commands/verifyEmail.js'));
738
+ const success = await runVerifyEmail({ email, setDefaultOnly, debug });
739
+ process.exit(success ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
740
+ }
741
+ // Handle `neozip upgrade` - upgrade pending Zipstamp timestamp to confirmed
742
+ if (command === 'upgrade') {
743
+ const upgradeArgs = args.slice(1);
744
+ let inputPath = '';
745
+ let outputPath;
746
+ let wait = false;
747
+ let verbose = false;
748
+ let debug = false;
749
+ for (let i = 0; i < upgradeArgs.length; i++) {
750
+ const arg = upgradeArgs[i];
751
+ if (arg === '--wait') {
752
+ wait = true;
753
+ }
754
+ else if (arg === '-v' || arg === '--verbose') {
755
+ verbose = true;
756
+ }
757
+ else if (arg === '--debug') {
758
+ debug = true;
759
+ }
760
+ else if (!arg.startsWith('-')) {
761
+ if (!inputPath) {
762
+ inputPath = arg;
763
+ }
764
+ else if (!outputPath) {
765
+ outputPath = arg;
766
+ }
767
+ }
768
+ }
769
+ if (!inputPath) {
770
+ console.error('Error: neozip upgrade requires an input archive path');
771
+ console.error(' Usage: neozip upgrade <archive> [output] [--wait]');
772
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
773
+ }
774
+ const { upgradeZipForTimestamp } = await import(resolveScriptPath('./neozip/upgradeZip.js'));
775
+ await upgradeZipForTimestamp(inputPath, outputPath, { wait, verbose, debug });
776
+ (0, utils_1.log)('✓ Upgrade completed successfully', { verbose, quiet: false });
777
+ process.exit(exit_codes_1.ZIP_EXIT_CODES.SUCCESS);
778
+ }
779
+ // Handle `neozip mint` - mint TimestampProofNFT from confirmed timestamped ZIP
780
+ if (command === 'mint') {
781
+ const mintArgs = args.slice(1);
782
+ let inputPath = '';
783
+ let outputPath;
784
+ let walletKey;
785
+ let network;
786
+ let debug = false;
787
+ for (let i = 0; i < mintArgs.length; i++) {
788
+ const arg = mintArgs[i];
789
+ if (arg === '-w' || arg === '--wallet-key') {
790
+ walletKey = mintArgs[++i];
791
+ }
792
+ else if (arg === '-n' || arg === '--network') {
793
+ network = mintArgs[++i];
794
+ }
795
+ else if (arg === '--debug') {
796
+ debug = true;
797
+ }
798
+ else if (!arg.startsWith('-')) {
799
+ if (!inputPath) {
800
+ inputPath = arg;
801
+ }
802
+ else if (outputPath === undefined) {
803
+ outputPath = arg;
804
+ }
805
+ }
806
+ }
807
+ if (!inputPath) {
808
+ console.error('Error: neozip mint requires an input archive path');
809
+ console.error(' Usage: neozip mint <archive> [output] [-w <wallet-key>] [-n <network>]');
810
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
811
+ }
812
+ const { runMintTimestampProof } = await import(resolveScriptPath('./commands/mintTimestampProof.js'));
813
+ const success = await runMintTimestampProof({
814
+ inputPath,
815
+ outputPath,
816
+ walletKey,
817
+ network,
818
+ debug
819
+ });
820
+ process.exit(success ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
821
+ }
686
822
  }
687
823
  let { archive, files, options } = parseArgs(args);
688
824
  // If network not explicitly provided via CLI, use from ConfigStore (which reads from ENV or wallet.json)
package/env.example CHANGED
@@ -55,6 +55,16 @@ NEOZIP_WALLET_PASSKEY=0x1234567890abcdef1234567890abcdef1234567890abcdef12345678
55
55
  # - Arbitrum One: https://arb1.arbitrum.io/rpc
56
56
  # - Ethereum Sepolia: https://rpc.sepolia.ethpandaops.io
57
57
 
58
+ # =============================================================================
59
+ # OPTIONAL: Zipstamp Timestamp Configuration
60
+ # =============================================================================
61
+
62
+ # Zipstamp server URL for Ethereum timestamping (default: https://zipstamp-dev.neozip.io)
63
+ # ZIPSTAMP_SERVER_URL=https://zipstamp-dev.neozip.io
64
+
65
+ # Email for Zipstamp server (some servers require verified email for stamping)
66
+ # NEOZIP_TIMESTAMP_EMAIL=your@email.com
67
+
58
68
  # =============================================================================
59
69
  # OPTIONAL: Debug Configuration
60
70
  # =============================================================================