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.
- package/AGENTS.md +93 -0
- package/CHANGELOG.md +70 -1
- package/DOCUMENTATION.md +22 -30
- package/README.md +214 -48
- package/dist/src/account/account-state.js +95 -0
- package/dist/src/account/format-account-status.js +62 -0
- package/dist/src/account/identity-provision.js +79 -0
- package/dist/src/account/identity-wrap.js +106 -0
- package/dist/src/account/profile-crypto.js +85 -0
- package/dist/src/account/profile-store.js +129 -0
- package/dist/src/account/require-account.js +32 -0
- package/dist/src/account/token-service-funding.js +151 -0
- package/dist/src/account/token-service-identity.js +488 -0
- package/dist/src/account/types.js +3 -0
- package/dist/src/account/wallet-evm.js +46 -0
- package/dist/src/account/wallet-setup.js +33 -0
- package/dist/src/archive/crypto-self.js +117 -0
- package/dist/src/archive/identity-key.js +217 -0
- package/dist/src/archive/recipient-lookup.js +61 -0
- package/dist/src/cli/output.js +100 -0
- package/dist/src/cli/params.js +122 -0
- package/dist/src/cli/schema.js +186 -0
- package/dist/src/cli/validate.js +119 -0
- package/dist/src/commands/mintTimestampProof.js +26 -14
- package/dist/src/commands/verifyEmail.js +9 -9
- package/dist/src/config/ConfigSetup.js +82 -423
- package/dist/src/config/ConfigStore.js +0 -33
- package/dist/src/connect/command.js +364 -0
- package/dist/src/connection/bootstrap.js +50 -0
- package/dist/src/connection/cli-guidance.js +101 -0
- package/dist/src/connection/cli-prefs.js +180 -0
- package/dist/src/connection/cli-settings.js +140 -0
- package/dist/src/connection/cli-types.js +14 -0
- package/dist/src/connection/coordinator.js +83 -0
- package/dist/src/connection/credentials.js +33 -0
- package/dist/src/connection/crypto.js +96 -0
- package/dist/src/connection/dump.js +117 -0
- package/dist/src/connection/funding.js +187 -0
- package/dist/src/connection/incomplete-setup.js +89 -0
- package/dist/src/connection/interactive.js +871 -0
- package/dist/src/connection/legacy-profile-reader.js +87 -0
- package/dist/src/connection/magic-link.js +142 -0
- package/dist/src/connection/migrate.js +115 -0
- package/dist/src/connection/onboarding.js +616 -0
- package/dist/src/connection/origin.js +69 -0
- package/dist/src/connection/phase.js +101 -0
- package/dist/src/connection/phone.js +26 -0
- package/dist/src/connection/promote-active.js +56 -0
- package/dist/src/connection/reset.js +56 -0
- package/dist/src/connection/status-report.js +52 -0
- package/dist/src/connection/store.js +406 -0
- package/dist/src/connection/token-auth.js +44 -0
- package/dist/src/connection/types.js +3 -0
- package/dist/src/connection/wallet-json-migration.js +155 -0
- package/dist/src/connection/wallet-login.js +65 -0
- package/dist/src/connection/wallet-setup.js +83 -0
- package/dist/src/constants/wallet-identity.js +14 -0
- package/dist/src/exit-codes.js +54 -10
- package/dist/src/neolist.js +93 -9
- package/dist/src/neounzip.js +217 -59
- package/dist/src/neozip/blockchain.js +18 -18
- package/dist/src/neozip/createZip.js +114 -91
- package/dist/src/neozip/upgradeZip.js +14 -11
- package/dist/src/neozip.js +252 -75
- package/dist/src/skills/command.js +256 -0
- package/dist/src/skills/locate.js +99 -0
- package/dist/src/util/mask.js +34 -0
- package/dist/src/util/token-service-fetch.js +26 -0
- package/env.example +18 -85
- package/package.json +89 -82
- package/skills/neozip-connect/SKILL.md +95 -0
- package/skills/neozip-create/SKILL.md +57 -0
- package/skills/neozip-extract/SKILL.md +58 -0
- package/skills/neozip-legacy/SKILL.md +48 -0
- package/skills/neozip-list/SKILL.md +56 -0
- package/skills/neozip-shared/SKILL.md +60 -0
package/dist/src/neounzip.js
CHANGED
|
@@ -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
|
|
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
|
|
293
|
+
* Perform Token Service upgrade on the archive (TS-SUBMIT.NZIP -> TIMESTAMP.NZIP)
|
|
290
294
|
*/
|
|
291
|
-
async function
|
|
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
|
|
309
|
+
* Prompt user for Token Service upgrade confirmation
|
|
306
310
|
*/
|
|
307
|
-
function
|
|
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 !==
|
|
586
|
-
filename !==
|
|
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 !==
|
|
615
|
-
name !==
|
|
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 !==
|
|
652
|
-
name !==
|
|
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 !==
|
|
972
|
-
filename !==
|
|
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 !==
|
|
1056
|
-
filename !==
|
|
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 !==
|
|
1072
|
-
filename !==
|
|
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 !==
|
|
1213
|
-
filename !==
|
|
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
|
|
1564
|
-
await
|
|
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
|
|
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
|
|
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
|
|
1690
|
+
const tokenServiceEntry = entries.find((e) => (e.filename || '') === 'META-INF/TS-SUBMIT.NZIP' ||
|
|
1687
1691
|
(e.filename || '') === 'META-INF/TIMESTAMP.NZIP');
|
|
1688
|
-
if (!
|
|
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,
|
|
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-
|
|
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(
|
|
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🔗
|
|
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🔗
|
|
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🔗
|
|
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,
|
|
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🔗
|
|
1772
|
+
console.log('\n🔗 Token Service Verification:');
|
|
1768
1773
|
if (res.status === 'valid') {
|
|
1769
1774
|
console.log(' - Status: ✅ VERIFIED');
|
|
1770
|
-
console.log(' - Stamp type:
|
|
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 = (
|
|
1802
|
+
const hasPendingMetadata = (tokenServiceEntry.filename || '') === 'META-INF/TS-SUBMIT.NZIP';
|
|
1798
1803
|
if (hasPendingMetadata) {
|
|
1799
1804
|
let doUpgrade = false;
|
|
1800
|
-
if (options.
|
|
1805
|
+
if (options.tokenServiceSkipUpgrade) {
|
|
1801
1806
|
doUpgrade = false;
|
|
1802
|
-
console.log(' - Upgrade: Skipped (--
|
|
1807
|
+
console.log(' - Upgrade: Skipped (--token-service-skip-upgrade)');
|
|
1803
1808
|
}
|
|
1804
|
-
else if (options.
|
|
1809
|
+
else if (options.tokenServiceAutoUpgrade) {
|
|
1805
1810
|
doUpgrade = true;
|
|
1806
|
-
console.log(' - Upgrade: Automatically applying (--
|
|
1811
|
+
console.log(' - Upgrade: Automatically applying (--token-service-auto-upgrade)');
|
|
1807
1812
|
}
|
|
1808
1813
|
else {
|
|
1809
|
-
doUpgrade = await
|
|
1814
|
+
doUpgrade = await promptForTokenServiceUpgrade(archiveName);
|
|
1810
1815
|
}
|
|
1811
1816
|
if (doUpgrade) {
|
|
1812
|
-
await
|
|
1817
|
+
await performTokenServiceUpgrade(archiveName);
|
|
1813
1818
|
}
|
|
1814
|
-
else if (!options.
|
|
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🔗
|
|
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
|
|
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
|
|
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 '--
|
|
2019
|
-
options.
|
|
2041
|
+
case '--token-service-auto-upgrade':
|
|
2042
|
+
options.tokenServiceAutoUpgrade = true;
|
|
2020
2043
|
break;
|
|
2021
|
-
case '--
|
|
2022
|
-
options.
|
|
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
|
-
--
|
|
2107
|
-
--
|
|
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
|
-
|
|
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
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
|
60
|
-
|
|
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('
|
|
68
|
-
console.error(' $ neozip
|
|
63
|
+
console.error(' Run Token Service setup:');
|
|
64
|
+
console.error(' $ neozip connect');
|
|
69
65
|
console.error('');
|
|
70
|
-
console.error('
|
|
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('
|
|
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
|
-
|
|
81
|
-
if (!(0, neozip_blockchain_1.validatePrivateKey)(
|
|
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
|
|
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
|