neozip-cli 0.90.0 → 0.95.1

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 (76) hide show
  1. package/AGENTS.md +93 -0
  2. package/CHANGELOG.md +70 -1
  3. package/DOCUMENTATION.md +22 -30
  4. package/README.md +214 -48
  5. package/dist/src/account/account-state.js +95 -0
  6. package/dist/src/account/format-account-status.js +62 -0
  7. package/dist/src/account/identity-provision.js +79 -0
  8. package/dist/src/account/identity-wrap.js +106 -0
  9. package/dist/src/account/profile-crypto.js +85 -0
  10. package/dist/src/account/profile-store.js +129 -0
  11. package/dist/src/account/require-account.js +32 -0
  12. package/dist/src/account/token-service-funding.js +151 -0
  13. package/dist/src/account/token-service-identity.js +488 -0
  14. package/dist/src/account/types.js +3 -0
  15. package/dist/src/account/wallet-evm.js +46 -0
  16. package/dist/src/account/wallet-setup.js +33 -0
  17. package/dist/src/archive/crypto-self.js +117 -0
  18. package/dist/src/archive/identity-key.js +217 -0
  19. package/dist/src/archive/recipient-lookup.js +61 -0
  20. package/dist/src/cli/output.js +100 -0
  21. package/dist/src/cli/params.js +122 -0
  22. package/dist/src/cli/schema.js +186 -0
  23. package/dist/src/cli/validate.js +119 -0
  24. package/dist/src/commands/mintTimestampProof.js +26 -14
  25. package/dist/src/commands/verifyEmail.js +9 -9
  26. package/dist/src/config/ConfigSetup.js +82 -423
  27. package/dist/src/config/ConfigStore.js +0 -33
  28. package/dist/src/connect/command.js +364 -0
  29. package/dist/src/connection/bootstrap.js +50 -0
  30. package/dist/src/connection/cli-guidance.js +101 -0
  31. package/dist/src/connection/cli-prefs.js +180 -0
  32. package/dist/src/connection/cli-settings.js +140 -0
  33. package/dist/src/connection/cli-types.js +14 -0
  34. package/dist/src/connection/coordinator.js +83 -0
  35. package/dist/src/connection/credentials.js +33 -0
  36. package/dist/src/connection/crypto.js +96 -0
  37. package/dist/src/connection/dump.js +117 -0
  38. package/dist/src/connection/funding.js +187 -0
  39. package/dist/src/connection/incomplete-setup.js +89 -0
  40. package/dist/src/connection/interactive.js +871 -0
  41. package/dist/src/connection/legacy-profile-reader.js +87 -0
  42. package/dist/src/connection/magic-link.js +142 -0
  43. package/dist/src/connection/migrate.js +115 -0
  44. package/dist/src/connection/onboarding.js +616 -0
  45. package/dist/src/connection/origin.js +69 -0
  46. package/dist/src/connection/phase.js +101 -0
  47. package/dist/src/connection/phone.js +26 -0
  48. package/dist/src/connection/promote-active.js +56 -0
  49. package/dist/src/connection/reset.js +56 -0
  50. package/dist/src/connection/status-report.js +52 -0
  51. package/dist/src/connection/store.js +406 -0
  52. package/dist/src/connection/token-auth.js +44 -0
  53. package/dist/src/connection/types.js +3 -0
  54. package/dist/src/connection/wallet-json-migration.js +155 -0
  55. package/dist/src/connection/wallet-login.js +65 -0
  56. package/dist/src/connection/wallet-setup.js +83 -0
  57. package/dist/src/constants/wallet-identity.js +14 -0
  58. package/dist/src/exit-codes.js +54 -10
  59. package/dist/src/neolist.js +93 -9
  60. package/dist/src/neounzip.js +217 -59
  61. package/dist/src/neozip/blockchain.js +18 -18
  62. package/dist/src/neozip/createZip.js +114 -91
  63. package/dist/src/neozip/upgradeZip.js +14 -11
  64. package/dist/src/neozip.js +252 -75
  65. package/dist/src/skills/command.js +256 -0
  66. package/dist/src/skills/locate.js +99 -0
  67. package/dist/src/util/mask.js +34 -0
  68. package/dist/src/util/token-service-fetch.js +26 -0
  69. package/env.example +18 -85
  70. package/package.json +89 -82
  71. package/skills/neozip-connect/SKILL.md +95 -0
  72. package/skills/neozip-create/SKILL.md +57 -0
  73. package/skills/neozip-extract/SKILL.md +58 -0
  74. package/skills/neozip-legacy/SKILL.md +48 -0
  75. package/skills/neozip-list/SKILL.md +56 -0
  76. package/skills/neozip-shared/SKILL.md +60 -0
@@ -51,10 +51,14 @@ const neozipkit_1 = require("neozipkit");
51
51
  const node_1 = __importDefault(require("neozipkit/node"));
52
52
  const neozip_blockchain_1 = require("neozip-blockchain");
53
53
  const ots_1 = require("neozip-blockchain/ots");
54
- const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
54
+ const token_service_1 = require("neozip-blockchain/token-service");
55
55
  const readline = __importStar(require("readline"));
56
56
  const version_1 = require("./version");
57
57
  const exit_codes_1 = require("./exit-codes");
58
+ const bootstrap_1 = require("./connection/bootstrap");
59
+ const output_1 = require("./cli/output");
60
+ const params_1 = require("./cli/params");
61
+ const validate_1 = require("./cli/validate");
58
62
  /**
59
63
  * Format bytes for display
60
64
  */
@@ -286,9 +290,9 @@ async function promptForTimeoutAction(networkConfig, currentRpcIndex, nonInterac
286
290
  });
287
291
  }
288
292
  /**
289
- * Perform Zipstamp upgrade on the archive (TS-SUBMIT.NZIP -> TIMESTAMP.NZIP)
293
+ * Perform Token Service upgrade on the archive (TS-SUBMIT.NZIP -> TIMESTAMP.NZIP)
290
294
  */
291
- async function performZipstampUpgrade(archivePath) {
295
+ async function performTokenServiceUpgrade(archivePath) {
292
296
  try {
293
297
  // Dynamic import: use path without .js for ts-node; tsc emits .js for dist
294
298
  // @ts-expect-error TS2835 - node16 requires .js but ts-node resolves .ts from source
@@ -302,9 +306,9 @@ async function performZipstampUpgrade(archivePath) {
302
306
  }
303
307
  }
304
308
  /**
305
- * Prompt user for Zipstamp upgrade confirmation
309
+ * Prompt user for Token Service upgrade confirmation
306
310
  */
307
- function promptForZipstampUpgrade(archiveName) {
311
+ function promptForTokenServiceUpgrade(archiveName) {
308
312
  return new Promise((resolve) => {
309
313
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
310
314
  rl.question(` - Batch confirmed. Upgrade archive to TIMESTAMP.NZIP? (y/N): `, (answer) => {
@@ -582,8 +586,8 @@ async function testArchive(zip, options) {
582
586
  return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
583
587
  filename !== ots_1.TIMESTAMP_METADATA &&
584
588
  filename !== ots_1.TIMESTAMP_SUBMITTED &&
585
- filename !== zipstamp_server_1.SUBMIT_METADATA &&
586
- filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
589
+ filename !== token_service_1.SUBMIT_METADATA &&
590
+ filename !== token_service_1.TIMESTAMP_METADATA &&
587
591
  !entry.isDirectory;
588
592
  });
589
593
  for (const entry of entries) {
@@ -611,8 +615,8 @@ async function testArchive(zip, options) {
611
615
  const isContentFile = name !== neozip_blockchain_1.TOKENIZED_METADATA &&
612
616
  name !== ots_1.TIMESTAMP_METADATA &&
613
617
  name !== ots_1.TIMESTAMP_SUBMITTED &&
614
- name !== zipstamp_server_1.SUBMIT_METADATA &&
615
- name !== zipstamp_server_1.TIMESTAMP_METADATA &&
618
+ name !== token_service_1.SUBMIT_METADATA &&
619
+ name !== token_service_1.TIMESTAMP_METADATA &&
616
620
  !entry.isDirectory;
617
621
  if (isContentFile) {
618
622
  // Use the verified hash returned from testEntry()
@@ -648,8 +652,8 @@ async function testArchive(zip, options) {
648
652
  const isContentFile = name !== neozip_blockchain_1.TOKENIZED_METADATA &&
649
653
  name !== ots_1.TIMESTAMP_METADATA &&
650
654
  name !== ots_1.TIMESTAMP_SUBMITTED &&
651
- name !== zipstamp_server_1.SUBMIT_METADATA &&
652
- name !== zipstamp_server_1.TIMESTAMP_METADATA &&
655
+ name !== token_service_1.SUBMIT_METADATA &&
656
+ name !== token_service_1.TIMESTAMP_METADATA &&
653
657
  !entry.isDirectory;
654
658
  if (isEncrypted) {
655
659
  // For encrypted files, decompression errors after decryption usually mean wrong password
@@ -968,8 +972,8 @@ async function extractArchive(zip, destination, options) {
968
972
  return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
969
973
  filename !== ots_1.TIMESTAMP_METADATA &&
970
974
  filename !== ots_1.TIMESTAMP_SUBMITTED &&
971
- filename !== zipstamp_server_1.SUBMIT_METADATA &&
972
- filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
975
+ filename !== token_service_1.SUBMIT_METADATA &&
976
+ filename !== token_service_1.TIMESTAMP_METADATA &&
973
977
  !entry.isDirectory;
974
978
  });
975
979
  for (const entry of filteredEntries) {
@@ -1052,8 +1056,8 @@ async function extractArchive(zip, destination, options) {
1052
1056
  const isContentFile = filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1053
1057
  filename !== ots_1.TIMESTAMP_METADATA &&
1054
1058
  filename !== ots_1.TIMESTAMP_SUBMITTED &&
1055
- filename !== zipstamp_server_1.SUBMIT_METADATA &&
1056
- filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1059
+ filename !== token_service_1.SUBMIT_METADATA &&
1060
+ filename !== token_service_1.TIMESTAMP_METADATA &&
1057
1061
  !entry.isDirectory;
1058
1062
  if (isContentFile && entry.sha256) {
1059
1063
  // Use the verified hash from the entry (already verified during extraction)
@@ -1068,8 +1072,8 @@ async function extractArchive(zip, destination, options) {
1068
1072
  const isContentFile = filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1069
1073
  filename !== ots_1.TIMESTAMP_METADATA &&
1070
1074
  filename !== ots_1.TIMESTAMP_SUBMITTED &&
1071
- filename !== zipstamp_server_1.SUBMIT_METADATA &&
1072
- filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1075
+ filename !== token_service_1.SUBMIT_METADATA &&
1076
+ filename !== token_service_1.TIMESTAMP_METADATA &&
1073
1077
  !entry.isDirectory;
1074
1078
  if (isContentFile && !options.skipBlockchain) {
1075
1079
  // Check if failure was due to SHA-256 mismatch
@@ -1209,8 +1213,8 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1209
1213
  return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
1210
1214
  filename !== ots_1.TIMESTAMP_METADATA &&
1211
1215
  filename !== ots_1.TIMESTAMP_SUBMITTED &&
1212
- filename !== zipstamp_server_1.SUBMIT_METADATA &&
1213
- filename !== zipstamp_server_1.TIMESTAMP_METADATA &&
1216
+ filename !== token_service_1.SUBMIT_METADATA &&
1217
+ filename !== token_service_1.TIMESTAMP_METADATA &&
1214
1218
  !entry.isDirectory; // Skip directories
1215
1219
  });
1216
1220
  // Extract each file once and verify SHA-256 during extraction
@@ -1560,8 +1564,8 @@ async function verifyTokenization(zip, archiveName, options, extractionResult) {
1560
1564
  } // End of token verification if statement
1561
1565
  // Check for OpenTimestamp verification and upgrade
1562
1566
  await verifyAndUpgradeOts(zip, archiveName, options);
1563
- // Check for Zipstamp verification
1564
- await verifyZipstampTimestamp(zip, archiveName, options, extractionResult);
1567
+ // Check for Token Service verification
1568
+ await verifyTokenServiceTimestamp(zip, archiveName, options, extractionResult);
1565
1569
  }
1566
1570
  catch (e) {
1567
1571
  const msg = e instanceof Error ? e.message : String(e);
@@ -1673,19 +1677,19 @@ async function verifyAndUpgradeOts(zip, archiveName, options) {
1673
1677
  }
1674
1678
  }
1675
1679
  /**
1676
- * Verify Zipstamp timestamp if present
1680
+ * Verify Token Service timestamp if present
1677
1681
  * Uses our extraction logic (extractEntry/extractToFile) for compatibility with file-based ZIPs
1678
1682
  * When batch is confirmed but archive has TS-SUBMIT.NZIP, offers upgrade (prompt/auto/skip)
1679
1683
  */
1680
- async function verifyZipstampTimestamp(zip, archiveName, options, extractionResult) {
1684
+ async function verifyTokenServiceTimestamp(zip, archiveName, options, extractionResult) {
1681
1685
  try {
1682
1686
  if (options.skipBlockchain) {
1683
1687
  return;
1684
1688
  }
1685
1689
  const entries = await getDirectory(zip, false);
1686
- const zipstampEntry = entries.find((e) => (e.filename || '') === 'META-INF/TS-SUBMIT.NZIP' ||
1690
+ const tokenServiceEntry = entries.find((e) => (e.filename || '') === 'META-INF/TS-SUBMIT.NZIP' ||
1687
1691
  (e.filename || '') === 'META-INF/TIMESTAMP.NZIP');
1688
- if (!zipstampEntry) {
1692
+ if (!tokenServiceEntry) {
1689
1693
  return;
1690
1694
  }
1691
1695
  // Extract timestamp metadata using our extraction (works for both buffer and file-based)
@@ -1693,7 +1697,7 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1693
1697
  const bufferBased = isBufferBased(zip);
1694
1698
  const fileBased = isFileBased(zip);
1695
1699
  if (bufferBased) {
1696
- const buf = await extractEntry(zip, zipstampEntry, true);
1700
+ const buf = await extractEntry(zip, tokenServiceEntry, true);
1697
1701
  if (buf) {
1698
1702
  try {
1699
1703
  timestampData = JSON.parse(buf.toString('utf-8'));
@@ -1704,9 +1708,9 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1704
1708
  }
1705
1709
  }
1706
1710
  else if (fileBased) {
1707
- const tmpPath = path.join(require('os').tmpdir(), `neounzip-zipstamp-${Date.now()}-${(zipstampEntry.filename || 'meta').replace(/[^a-zA-Z0-9]/g, '_')}`);
1711
+ const tmpPath = path.join(require('os').tmpdir(), `neounzip-token-service-${Date.now()}-${(tokenServiceEntry.filename || 'meta').replace(/[^a-zA-Z0-9]/g, '_')}`);
1708
1712
  try {
1709
- await zip.extractToFile(zipstampEntry, tmpPath, { skipHashCheck: true });
1713
+ await zip.extractToFile(tokenServiceEntry, tmpPath, { skipHashCheck: true });
1710
1714
  const buf = fs.readFileSync(tmpPath);
1711
1715
  timestampData = JSON.parse(buf.toString('utf-8'));
1712
1716
  }
@@ -1725,7 +1729,7 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1725
1729
  }
1726
1730
  }
1727
1731
  if (!timestampData || !timestampData.digest) {
1728
- console.log('\n🔗 Zipstamp Verification:');
1732
+ console.log('\n🔗 Token Service Verification:');
1729
1733
  console.log(' - Status: ❌ ERROR');
1730
1734
  console.log(' - Error: Failed to extract timestamp metadata');
1731
1735
  return;
@@ -1734,13 +1738,13 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1734
1738
  zip.getMerkleRoot?.() ??
1735
1739
  null;
1736
1740
  if (!merkleRoot) {
1737
- console.log('\n🔗 Zipstamp Verification:');
1741
+ console.log('\n🔗 Token Service Verification:');
1738
1742
  console.log(' - Status: ❌ ERROR');
1739
1743
  console.log(' - Error: Merkle root not found in ZIP file');
1740
1744
  return;
1741
1745
  }
1742
1746
  if (timestampData.digest.toLowerCase() !== merkleRoot.toLowerCase()) {
1743
- console.log('\n🔗 Zipstamp Verification:');
1747
+ console.log('\n🔗 Token Service Verification:');
1744
1748
  console.log(' - Status: ❌ ERROR');
1745
1749
  console.log(' - Error: Timestamp digest does not match ZIP merkle root');
1746
1750
  return;
@@ -1748,8 +1752,9 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1748
1752
  // Use verifyDigest directly - pass undefined for chainId when 0/invalid (server may reject chainId 0)
1749
1753
  const chainId = timestampData.chainId && timestampData.chainId > 0 ? timestampData.chainId : undefined;
1750
1754
  const batchId = timestampData.batchId || undefined;
1751
- const response = await (0, zipstamp_server_1.verifyDigest)(merkleRoot, chainId, batchId, undefined, {
1755
+ const response = await (0, token_service_1.verifyDigest)(merkleRoot, chainId, batchId, undefined, {
1752
1756
  debug: options.debug ?? false,
1757
+ serverUrl: (0, bootstrap_1.resolveVerifyServerUrl)(timestampData.serverUrl),
1753
1758
  });
1754
1759
  const res = response.success
1755
1760
  ? response.verified
@@ -1764,10 +1769,10 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1764
1769
  }
1765
1770
  : { status: 'pending', message: 'Timestamp is pending confirmation' }
1766
1771
  : { status: 'error', message: response.error || 'Verification failed' };
1767
- console.log('\n🔗 Zipstamp Verification:');
1772
+ console.log('\n🔗 Token Service Verification:');
1768
1773
  if (res.status === 'valid') {
1769
1774
  console.log(' - Status: ✅ VERIFIED');
1770
- console.log(' - Stamp type: Zipstamp (blockchain timestamp)');
1775
+ console.log(' - Stamp type: Token Service (blockchain timestamp)');
1771
1776
  if (res.network) {
1772
1777
  console.log(` - Network: ${res.network}`);
1773
1778
  }
@@ -1794,24 +1799,24 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1794
1799
  console.log(` - Block timestamp (minted): ${mintedStr}`);
1795
1800
  }
1796
1801
  // Offer upgrade when archive has TS-SUBMIT.NZIP but batch is confirmed
1797
- const hasPendingMetadata = (zipstampEntry.filename || '') === 'META-INF/TS-SUBMIT.NZIP';
1802
+ const hasPendingMetadata = (tokenServiceEntry.filename || '') === 'META-INF/TS-SUBMIT.NZIP';
1798
1803
  if (hasPendingMetadata) {
1799
1804
  let doUpgrade = false;
1800
- if (options.zipstampSkipUpgrade) {
1805
+ if (options.tokenServiceSkipUpgrade) {
1801
1806
  doUpgrade = false;
1802
- console.log(' - Upgrade: Skipped (--zipstamp-skip-upgrade)');
1807
+ console.log(' - Upgrade: Skipped (--token-service-skip-upgrade)');
1803
1808
  }
1804
- else if (options.zipstampAutoUpgrade) {
1809
+ else if (options.tokenServiceAutoUpgrade) {
1805
1810
  doUpgrade = true;
1806
- console.log(' - Upgrade: Automatically applying (--zipstamp-auto-upgrade)');
1811
+ console.log(' - Upgrade: Automatically applying (--token-service-auto-upgrade)');
1807
1812
  }
1808
1813
  else {
1809
- doUpgrade = await promptForZipstampUpgrade(archiveName);
1814
+ doUpgrade = await promptForTokenServiceUpgrade(archiveName);
1810
1815
  }
1811
1816
  if (doUpgrade) {
1812
- await performZipstampUpgrade(archiveName);
1817
+ await performTokenServiceUpgrade(archiveName);
1813
1818
  }
1814
- else if (!options.zipstampSkipUpgrade && !options.zipstampAutoUpgrade) {
1819
+ else if (!options.tokenServiceSkipUpgrade && !options.tokenServiceAutoUpgrade) {
1815
1820
  console.log(' - Note: Run "neozip upgrade <archive>" to upgrade when ready');
1816
1821
  }
1817
1822
  }
@@ -1835,11 +1840,11 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1835
1840
  }
1836
1841
  catch (error) {
1837
1842
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1838
- console.log('\n🔗 Zipstamp Verification:');
1843
+ console.log('\n🔗 Token Service Verification:');
1839
1844
  if (errorMessage.includes('ESOCKETTIMEDOUT') || errorMessage.includes('ECONNRESET') ||
1840
1845
  errorMessage.includes('ENOTFOUND') || errorMessage.includes('ECONNREFUSED') ||
1841
1846
  errorMessage.includes('timeout') || errorMessage.includes('network')) {
1842
- console.log(' - Status: 📡 COMMUNICATION ERROR (unable to contact Zipstamp server)');
1847
+ console.log(' - Status: 📡 COMMUNICATION ERROR (unable to contact Token Service)');
1843
1848
  console.log(` - Error: ${errorMessage}`);
1844
1849
  }
1845
1850
  else {
@@ -1851,7 +1856,23 @@ async function verifyZipstampTimestamp(zip, archiveName, options, extractionResu
1851
1856
  /**
1852
1857
  * Parse command line arguments
1853
1858
  */
1854
- function parseArgs(args) {
1859
+ function unzipParamsToOptions(params) {
1860
+ const allowed = [
1861
+ 'test', 'skipBlockchain', 'verbose', 'quiet', 'overwrite', 'testOnly', 'bufferSize',
1862
+ 'password', 'debug', 'showFiles', 'encryptionMethod', 'exclude', 'include', 'junkPaths',
1863
+ 'never', 'exdir', 'preservePerms', 'symlinks', 'hardLinks', 'listOnly', 'freshenOnly',
1864
+ 'updateOnly', 'commentOnly', 'otsAutoUpgrade', 'otsSkipUpgrade', 'tokenServiceAutoUpgrade',
1865
+ 'tokenServiceSkipUpgrade', 'inMemory', 'nonInteractive', 'preVerify', 'dryRun',
1866
+ ];
1867
+ const out = {};
1868
+ for (const key of allowed) {
1869
+ if (key in params) {
1870
+ out[key] = params[key];
1871
+ }
1872
+ }
1873
+ return out;
1874
+ }
1875
+ function parseArgs(args, seed) {
1855
1876
  const options = {
1856
1877
  dest: '.',
1857
1878
  test: false,
@@ -1863,11 +1884,13 @@ function parseArgs(args) {
1863
1884
  bufferSize: 512 * 1024, // 512KB default (optimal for modern systems)
1864
1885
  enableProgress: true,
1865
1886
  junkPaths: false,
1866
- never: false
1887
+ never: false,
1888
+ // Seed from agent --params/--json first; explicit CLI flags below override.
1889
+ ...(seed?.options ?? {}),
1867
1890
  };
1868
- let archive = '';
1869
- let destination = '.';
1870
- let filePatterns = [];
1891
+ let archive = seed?.archive ?? '';
1892
+ let destination = seed?.destination ?? '.';
1893
+ let filePatterns = seed?.filePatterns ? [...seed.filePatterns] : [];
1871
1894
  for (let i = 0; i < args.length; i++) {
1872
1895
  const arg = args[i];
1873
1896
  switch (arg) {
@@ -2015,11 +2038,11 @@ function parseArgs(args) {
2015
2038
  case '--ots-skip-upgrade':
2016
2039
  options.otsSkipUpgrade = true;
2017
2040
  break;
2018
- case '--zipstamp-auto-upgrade':
2019
- options.zipstampAutoUpgrade = true;
2041
+ case '--token-service-auto-upgrade':
2042
+ options.tokenServiceAutoUpgrade = true;
2020
2043
  break;
2021
- case '--zipstamp-skip-upgrade':
2022
- options.zipstampSkipUpgrade = true;
2044
+ case '--token-service-skip-upgrade':
2045
+ options.tokenServiceSkipUpgrade = true;
2023
2046
  break;
2024
2047
  case '--in-memory':
2025
2048
  options.inMemory = true;
@@ -2027,6 +2050,9 @@ function parseArgs(args) {
2027
2050
  case '--non-interactive':
2028
2051
  options.nonInteractive = true;
2029
2052
  break;
2053
+ case '--dry-run':
2054
+ options.dryRun = true;
2055
+ break;
2030
2056
  default:
2031
2057
  if (arg.startsWith('-')) {
2032
2058
  console.error(`Error: Unknown option ${arg}`);
@@ -2067,6 +2093,8 @@ function parseArgs(args) {
2067
2093
  * Show help information
2068
2094
  */
2069
2095
  function showHelp() {
2096
+ if ((0, output_1.isJsonOutput)())
2097
+ return;
2070
2098
  console.log(`
2071
2099
  NeoUnZip Version: ${version_1.APP_VERSION} (${version_1.APP_RELEASE_DATE})
2072
2100
 
@@ -2103,9 +2131,13 @@ Options:
2103
2131
  -H, --hard-links Restore hard links efficiently (recreate link relationships)
2104
2132
  --ots-auto-upgrade Automatically upgrade OTS timestamps when available
2105
2133
  --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)
2134
+ --token-service-auto-upgrade Automatically upgrade Token Service timestamps when confirmed
2135
+ --token-service-skip-upgrade Skip Token Service timestamp upgrades (no prompt)
2108
2136
  --in-memory Force in-memory processing mode (browser compatible)
2137
+ --dry-run Show the extraction plan without writing any files
2138
+ --format <fmt> Output format: text (default) or json (machine-readable)
2139
+ --params <json> Provide options as a JSON object (agent input; '-' = stdin)
2140
+ --json <json> Provide payload {archive, exdir, filePatterns} as JSON ('-' = stdin)
2109
2141
 
2110
2142
  Arguments:
2111
2143
  <archive> ZIP file to extract or test
@@ -2121,6 +2153,8 @@ Examples:
2121
2153
  * Show extended help information (equivalent to InfoZip's -h2)
2122
2154
  */
2123
2155
  function showExtendedHelp() {
2156
+ if ((0, output_1.isJsonOutput)())
2157
+ return;
2124
2158
  console.log('Extended Help for NeoUnzip');
2125
2159
  console.log('');
2126
2160
  console.log('NeoUnzip is a next-generation ZIP extraction utility that builds upon the strengths');
@@ -2243,13 +2277,47 @@ async function main() {
2243
2277
  let zip = null;
2244
2278
  let options = null;
2245
2279
  try {
2280
+ (0, bootstrap_1.bootstrapConnectionContext)();
2246
2281
  // Parse command line arguments
2247
2282
  const args = process.argv.slice(2);
2248
- const parsed = parseArgs(args);
2283
+ // Resolve machine-readable output mode early so error exits are structured.
2284
+ (0, output_1.setOutputFormat)((0, output_1.detectOutputFormat)(args));
2285
+ // Extract agent input (--params/--json) and strip --format, then parse.
2286
+ const agent = (0, params_1.extractAgentInput)((0, output_1.stripFormatFlag)(args));
2287
+ const seed = {};
2288
+ if (agent.params)
2289
+ seed.options = unzipParamsToOptions(agent.params);
2290
+ if (agent.payload) {
2291
+ if (typeof agent.payload.archive === 'string')
2292
+ seed.archive = agent.payload.archive;
2293
+ if (typeof agent.payload.destination === 'string')
2294
+ seed.destination = agent.payload.destination;
2295
+ if (typeof agent.payload.exdir === 'string') {
2296
+ seed.options = { ...(seed.options ?? {}), exdir: agent.payload.exdir };
2297
+ }
2298
+ if (Array.isArray(agent.payload.filePatterns)) {
2299
+ seed.filePatterns = agent.payload.filePatterns.map(String);
2300
+ }
2301
+ }
2302
+ const parsed = parseArgs(agent.argv, seed);
2249
2303
  const { archive, destination, filePatterns } = parsed;
2250
2304
  options = parsed.options;
2305
+ // In JSON mode, suppress decorative human logs so stdout stays pure JSON.
2306
+ if ((0, output_1.isJsonOutput)()) {
2307
+ options.quiet = true;
2308
+ }
2251
2309
  // Use exdir option if provided, otherwise use destination
2252
2310
  const extractDestination = options.exdir || destination;
2311
+ // Input hardening: reject control characters in the extraction directory.
2312
+ try {
2313
+ (0, validate_1.validateSafeOutputDir)(extractDestination, 'Extraction directory');
2314
+ }
2315
+ catch (err) {
2316
+ if (err instanceof validate_1.ValidationError) {
2317
+ (0, exit_codes_1.exitUnzip)(exit_codes_1.UNZIP_EXIT_CODES.INVALID_OPTIONS, `Error: ${err.message}`);
2318
+ }
2319
+ throw err;
2320
+ }
2253
2321
  // Load the ZIP file
2254
2322
  zip = new node_1.default();
2255
2323
  if (options.inMemory) {
@@ -2297,17 +2365,71 @@ async function main() {
2297
2365
  log('', options);
2298
2366
  }
2299
2367
  }
2368
+ // Dry-run: report the plan without extracting or writing anything.
2369
+ if (options.dryRun) {
2370
+ const dirEntries = zip.getDirectory();
2371
+ const planned = dirEntries
2372
+ .filter((e) => !e.isDirectory && !(typeof e.filename === 'string' && e.filename.endsWith('/')))
2373
+ .map((e) => ({
2374
+ entry: e.filename,
2375
+ target: zip.prepareExtractionPath(e, extractDestination, { junkPaths: options.junkPaths }),
2376
+ size: e.size ?? e.uncompressedSize,
2377
+ }));
2378
+ const plan = {
2379
+ command: 'neounzip',
2380
+ action: options.testOnly ? 'test' : options.listOnly ? 'list' : 'extract',
2381
+ dryRun: true,
2382
+ archive,
2383
+ destination: extractDestination,
2384
+ entryCount: planned.length,
2385
+ entries: planned,
2386
+ };
2387
+ if ((0, output_1.isJsonOutput)()) {
2388
+ (0, output_1.emitResult)(plan);
2389
+ }
2390
+ else {
2391
+ console.log('Dry run — no files will be written.');
2392
+ console.log(JSON.stringify(plan, null, 2));
2393
+ }
2394
+ process.exit(exit_codes_1.UNZIP_EXIT_CODES.SUCCESS);
2395
+ }
2300
2396
  if (options.testOnly) {
2301
2397
  const testResult = await testArchive(zip, options);
2302
2398
  // Always verify tokenization info after testing so users see results at the end
2303
2399
  // Pass pre-calculated merkle root to avoid double extraction (single pass)
2304
2400
  await verifyTokenization(zip, archive, options, testResult.extractionResult);
2401
+ if ((0, output_1.isJsonOutput)()) {
2402
+ (0, output_1.emitResult)({
2403
+ command: 'neounzip',
2404
+ action: 'test',
2405
+ archive,
2406
+ failures: testResult.failures,
2407
+ passed: testResult.failures === 0,
2408
+ });
2409
+ }
2305
2410
  process.exit(testResult.failures === 0 ? exit_codes_1.UNZIP_EXIT_CODES.SUCCESS : exit_codes_1.UNZIP_EXIT_CODES.WARNING);
2306
2411
  }
2307
2412
  else if (options.listOnly) {
2308
- await listArchive(zip, archive, options);
2309
- // Show tokenization info after listing (no pre-calculated result, will extract once)
2310
- await verifyTokenization(zip, archive, options);
2413
+ if ((0, output_1.isJsonOutput)()) {
2414
+ const dirEntries = zip.getDirectory();
2415
+ (0, output_1.emitResult)({
2416
+ command: 'neounzip',
2417
+ action: 'list',
2418
+ archive,
2419
+ entryCount: dirEntries.length,
2420
+ entries: dirEntries.map((e) => ({
2421
+ name: e.filename,
2422
+ size: e.size ?? e.uncompressedSize,
2423
+ compressedSize: e.compressedSize,
2424
+ encrypted: !!e.isEncrypted,
2425
+ })),
2426
+ });
2427
+ }
2428
+ else {
2429
+ await listArchive(zip, archive, options);
2430
+ // Show tokenization info after listing (no pre-calculated result, will extract once)
2431
+ await verifyTokenization(zip, archive, options);
2432
+ }
2311
2433
  }
2312
2434
  else if (options.commentOnly) {
2313
2435
  // Comment mode - show only archive comment, no other output
@@ -2343,12 +2465,48 @@ async function main() {
2343
2465
  // Pre-verification passed - proceed with extraction
2344
2466
  console.log('\n✅ Pre-verification passed - proceeding with extraction...\n');
2345
2467
  }
2468
+ // Zip-slip protection: validate the ACTUAL output path each entry would be
2469
+ // written to (as computed by neozipkit) and refuse any that escape the
2470
+ // target directory.
2471
+ try {
2472
+ const resolvedDest = path.resolve(extractDestination);
2473
+ const withSep = resolvedDest.endsWith(path.sep) ? resolvedDest : resolvedDest + path.sep;
2474
+ const dirEntries = zip.getDirectory();
2475
+ for (const e of dirEntries) {
2476
+ if (e.isDirectory)
2477
+ continue;
2478
+ const name = e.filename;
2479
+ if (typeof name !== 'string' || name.endsWith('/'))
2480
+ continue;
2481
+ const outPath = zip.prepareExtractionPath(e, extractDestination, { junkPaths: options.junkPaths });
2482
+ const resolved = path.resolve(outPath);
2483
+ if (resolved !== resolvedDest && !resolved.startsWith(withSep)) {
2484
+ throw new validate_1.ValidationError(`Entry "${name}" escapes the extraction directory (path traversal)`);
2485
+ }
2486
+ }
2487
+ }
2488
+ catch (err) {
2489
+ if (err instanceof validate_1.ValidationError) {
2490
+ (0, exit_codes_1.exitUnzip)(exit_codes_1.UNZIP_EXIT_CODES.ZIP_FORMAT_ERROR, `Error: ${err.message}`);
2491
+ }
2492
+ throw err;
2493
+ }
2346
2494
  const extractionResult = await extractArchive(zip, extractDestination, options);
2347
2495
  // Show tokenization info after extraction (pass pre-calculated merkle root to avoid double extraction)
2348
2496
  // Skip blockchain verification if we already did it during pre-verify
2349
2497
  if (!options.preVerify) {
2350
2498
  await verifyTokenization(zip, archive, options, extractionResult);
2351
2499
  }
2500
+ if ((0, output_1.isJsonOutput)()) {
2501
+ (0, output_1.emitResult)({
2502
+ command: 'neounzip',
2503
+ action: 'extract',
2504
+ archive,
2505
+ destination: extractDestination,
2506
+ allFilesValid: extractionResult.allFilesValid,
2507
+ failedFiles: extractionResult.failedFiles,
2508
+ });
2509
+ }
2352
2510
  }
2353
2511
  log('✓ NeoUnZip completed successfully', options);
2354
2512
  // Close file handle if file-based ZIP was loaded
@@ -37,12 +37,13 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.isSupportedNetwork = isSupportedNetwork;
40
+ exports.getConnectionWalletKey = getConnectionWalletKey;
40
41
  exports.getWalletPasskey = getWalletPasskey;
41
42
  exports.addTokenMetaToZip = addTokenMetaToZip;
42
43
  exports.handleTokenMinting = handleTokenMinting;
43
44
  const neozipkit_1 = require("neozipkit");
44
45
  const neozip_blockchain_1 = require("neozip-blockchain");
45
- const ConfigStore_1 = require("../config/ConfigStore");
46
+ const credentials_1 = require("../connection/credentials");
46
47
  const readline = __importStar(require("readline"));
47
48
  /**
48
49
  * Check if the network is supported (uses nameAliases from CONTRACT_CONFIGS)
@@ -51,34 +52,29 @@ function isSupportedNetwork(network) {
51
52
  return (0, neozip_blockchain_1.getChainIdByName)(network) !== null;
52
53
  }
53
54
  /**
54
- * Check if the required wallet passkey is available and valid
55
- * Priority: CLI arg > wallet.json > ENV var
56
- * @param cliWalletKey - Optional wallet key from CLI
57
- * @param showError - Whether to show error messages (default: true)
55
+ * Resolve wallet key from connection store (optional CLI override for automation).
58
56
  */
59
- function getWalletPasskey(cliWalletKey, showError = true) {
60
- // Get config with CLI wallet key taking precedence
61
- const config = ConfigStore_1.ConfigStore.getConfig(cliWalletKey);
62
- const passkey = config.walletKey;
57
+ function getConnectionWalletKey(cliWalletKey, showError = true) {
58
+ const passkey = cliWalletKey?.trim() || (0, credentials_1.readConnectionCredentials)().walletKey;
63
59
  if (!passkey) {
64
60
  if (showError) {
65
61
  console.error('❌ Error: Wallet private key is required for blockchain operations');
66
62
  console.error('');
67
- console.error(' Option 1 (Recommended): Run interactive setup');
68
- console.error(' $ neozip init');
63
+ console.error(' Run Token Service setup:');
64
+ console.error(' $ neozip connect');
69
65
  console.error('');
70
- console.error(' Option 2: Use command-line flag');
66
+ console.error(' Or use a one-off override:');
71
67
  console.error(' $ neozip -b -w 0x... <archive> <files>');
72
68
  console.error('');
73
- console.error(' Option 3: Set environment variable');
74
- console.error(' $ export NEOZIP_WALLET_PASSKEY="0x..."');
75
69
  console.error('');
76
- console.error(' Get testnet ETH from: https://www.coinbase.com/faucets/base-ethereum-sepolia-faucet');
70
+ console.error(' Fund your Data Wallet from the Token Service:');
71
+ console.error(' $ neozip connect fund');
72
+ console.error(' (or get testnet ETH from https://www.coinbase.com/faucets/base-ethereum-sepolia-faucet)');
77
73
  }
78
74
  return null;
79
75
  }
80
- // Validate the private key format
81
- if (!(0, neozip_blockchain_1.validatePrivateKey)(passkey)) {
76
+ const normalized = passkey.startsWith('0x') ? passkey : `0x${passkey}`;
77
+ if (!(0, neozip_blockchain_1.validatePrivateKey)(normalized)) {
82
78
  if (showError) {
83
79
  console.error('❌ Error: Invalid private key format');
84
80
  console.error(' Private key should be a 64-character hex string starting with 0x');
@@ -86,7 +82,11 @@ function getWalletPasskey(cliWalletKey, showError = true) {
86
82
  }
87
83
  return null;
88
84
  }
89
- return passkey;
85
+ return normalized;
86
+ }
87
+ /** @deprecated Use getConnectionWalletKey */
88
+ function getWalletPasskey(cliWalletKey, showError = true) {
89
+ return getConnectionWalletKey(cliWalletKey, showError);
90
90
  }
91
91
  /**
92
92
  * Add token metadata to ZIP file using the constant