neozip-cli 0.80.0 → 0.95.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 (74) hide show
  1. package/AGENTS.md +93 -0
  2. package/CHANGELOG.md +117 -12
  3. package/DOCUMENTATION.md +33 -38
  4. package/README.md +137 -63
  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/types.js +3 -0
  13. package/dist/src/account/wallet-evm.js +46 -0
  14. package/dist/src/account/wallet-setup.js +33 -0
  15. package/dist/src/archive/crypto-self.js +117 -0
  16. package/dist/src/archive/identity-key.js +217 -0
  17. package/dist/src/archive/recipient-lookup.js +61 -0
  18. package/dist/src/cli/output.js +100 -0
  19. package/dist/src/cli/params.js +122 -0
  20. package/dist/src/cli/schema.js +186 -0
  21. package/dist/src/cli/validate.js +119 -0
  22. package/dist/src/commands/mintTimestampProof.js +26 -14
  23. package/dist/src/config/ConfigSetup.js +82 -423
  24. package/dist/src/connect/command.js +364 -0
  25. package/dist/src/connection/bootstrap.js +50 -0
  26. package/dist/src/connection/cli-guidance.js +101 -0
  27. package/dist/src/connection/cli-prefs.js +180 -0
  28. package/dist/src/connection/cli-settings.js +140 -0
  29. package/dist/src/connection/cli-types.js +14 -0
  30. package/dist/src/connection/coordinator.js +83 -0
  31. package/dist/src/connection/credentials.js +33 -0
  32. package/dist/src/connection/crypto.js +96 -0
  33. package/dist/src/connection/dump.js +117 -0
  34. package/dist/src/connection/funding.js +187 -0
  35. package/dist/src/connection/incomplete-setup.js +89 -0
  36. package/dist/src/connection/interactive.js +871 -0
  37. package/dist/src/connection/legacy-profile-reader.js +87 -0
  38. package/dist/src/connection/magic-link.js +142 -0
  39. package/dist/src/connection/migrate.js +115 -0
  40. package/dist/src/connection/onboarding.js +616 -0
  41. package/dist/src/connection/origin.js +69 -0
  42. package/dist/src/connection/phase.js +101 -0
  43. package/dist/src/connection/phone.js +26 -0
  44. package/dist/src/connection/promote-active.js +56 -0
  45. package/dist/src/connection/reset.js +56 -0
  46. package/dist/src/connection/status-report.js +52 -0
  47. package/dist/src/connection/store.js +406 -0
  48. package/dist/src/connection/token-auth.js +44 -0
  49. package/dist/src/connection/types.js +3 -0
  50. package/dist/src/connection/wallet-json-migration.js +155 -0
  51. package/dist/src/connection/wallet-login.js +65 -0
  52. package/dist/src/connection/wallet-setup.js +83 -0
  53. package/dist/src/constants/wallet-identity.js +14 -0
  54. package/dist/src/exit-codes.js +54 -10
  55. package/dist/src/neolist.js +100 -10
  56. package/dist/src/neounzip.js +236 -65
  57. package/dist/src/neozip/blockchain.js +18 -18
  58. package/dist/src/neozip/createZip.js +118 -94
  59. package/dist/src/neozip/upgradeZip.js +14 -11
  60. package/dist/src/neozip.js +269 -91
  61. package/dist/src/skills/command.js +256 -0
  62. package/dist/src/skills/locate.js +99 -0
  63. package/dist/src/util/mask.js +34 -0
  64. package/dist/src/util/token-service-fetch.js +26 -0
  65. package/env.example +18 -85
  66. package/package.json +89 -73
  67. package/skills/neozip-connect/SKILL.md +95 -0
  68. package/skills/neozip-create/SKILL.md +57 -0
  69. package/skills/neozip-extract/SKILL.md +58 -0
  70. package/skills/neozip-legacy/SKILL.md +48 -0
  71. package/skills/neozip-list/SKILL.md +56 -0
  72. package/skills/neozip-shared/SKILL.md +60 -0
  73. package/dist/src/commands/verifyEmail.js +0 -146
  74. package/dist/src/config/ConfigStore.js +0 -406
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Upgrade Zipstamp timestamp from pending (TS-SUBMIT.NZIP) to confirmed (TIMESTAMP.NZIP)
3
+ * Upgrade Token Service timestamp from pending (TS-SUBMIT.NZIP) to confirmed (TIMESTAMP.NZIP)
4
4
  */
5
5
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
6
  if (k2 === undefined) k2 = k;
@@ -45,15 +45,16 @@ const path = __importStar(require("path"));
45
45
  const os = __importStar(require("os"));
46
46
  const node_1 = __importDefault(require("neozipkit/node"));
47
47
  const node_2 = require("neozipkit/node");
48
- const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
48
+ const token_service_1 = require("neozip-blockchain/token-service");
49
49
  const exit_codes_1 = require("../exit-codes");
50
+ const bootstrap_1 = require("../connection/bootstrap");
50
51
  /**
51
52
  * Extract timestamp metadata from a ZIP entry.
52
53
  * Tries the library's extractTimestampData first; if it fails (e.g. file-based ZIP),
53
54
  * fall back to extracting to a temp file and parsing JSON.
54
55
  */
55
56
  async function extractTimestampMetadata(zip, entry) {
56
- let metadata = await (0, zipstamp_server_1.extractTimestampData)(zip, entry);
57
+ let metadata = await (0, token_service_1.extractTimestampData)(zip, entry);
57
58
  if (metadata)
58
59
  return metadata;
59
60
  // Fallback for file-based ZIPs where zip.extract() may not work
@@ -74,7 +75,7 @@ async function extractTimestampMetadata(zip, entry) {
74
75
  }
75
76
  }
76
77
  /**
77
- * Upgrade a ZIP file with pending Zipstamp timestamp to confirmed status.
78
+ * Upgrade a ZIP file with pending Token Service timestamp to confirmed status.
78
79
  * Replaces TS-SUBMIT.NZIP with TIMESTAMP.NZIP when the batch is confirmed on the blockchain.
79
80
  */
80
81
  async function upgradeZipForTimestamp(inputPath, outputPath, options = {}) {
@@ -85,9 +86,9 @@ async function upgradeZipForTimestamp(inputPath, outputPath, options = {}) {
85
86
  const zip = new node_1.default();
86
87
  await zip.loadZipFile(inputPath);
87
88
  const entries = zip.getDirectory() || [];
88
- const metadataResult = (0, zipstamp_server_1.findMetadataEntry)(entries);
89
+ const metadataResult = (0, token_service_1.findMetadataEntry)(entries);
89
90
  if (!metadataResult) {
90
- console.log('No Zipstamp timestamp found in ZIP file');
91
+ console.log('No Token Service timestamp found in ZIP file');
91
92
  return;
92
93
  }
93
94
  if (metadataResult.type === 'confirmed') {
@@ -98,21 +99,23 @@ async function upgradeZipForTimestamp(inputPath, outputPath, options = {}) {
98
99
  if (!metadata || !metadata.digest) {
99
100
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT, 'Error: Failed to extract timestamp metadata');
100
101
  }
101
- if (!(0, zipstamp_server_1.shouldUpgrade)(metadata, metadataResult.type)) {
102
+ if (!(0, token_service_1.shouldUpgrade)(metadata, metadataResult.type)) {
102
103
  console.log('✓ Timestamp does not need upgrade');
103
104
  return;
104
105
  }
105
106
  const digest = metadata.digest;
106
107
  const chainId = metadata.chainId && metadata.chainId > 0 ? metadata.chainId : undefined;
107
108
  const batchId = metadata.batchId ?? undefined;
109
+ const serverUrl = (0, bootstrap_1.resolveVerifyServerUrl)(metadata.serverUrl);
110
+ const verifyOpts = { debug, serverUrl };
108
111
  let verified = null;
109
112
  if (wait) {
110
113
  if (verbose || debug)
111
114
  console.log('Polling for confirmation (--wait)...');
112
- verified = await (0, zipstamp_server_1.pollForConfirmation)(digest, chainId, batchId, 300000, 5000, { debug });
115
+ verified = await (0, token_service_1.pollForConfirmation)(digest, chainId, batchId, 300000, 5000, verifyOpts);
113
116
  }
114
117
  else {
115
- const verifyRes = await (0, zipstamp_server_1.verifyDigest)(digest, chainId, batchId, undefined, { debug });
118
+ const verifyRes = await (0, token_service_1.verifyDigest)(digest, chainId, batchId, undefined, verifyOpts);
116
119
  if (verifyRes.verified) {
117
120
  verified = verifyRes;
118
121
  }
@@ -135,7 +138,7 @@ async function upgradeZipForTimestamp(inputPath, outputPath, options = {}) {
135
138
  blockNumber: verified.blockNumber ?? metadata.blockNumber,
136
139
  timestamp: verified.timestamp ?? metadata.timestamp
137
140
  };
138
- const metadataFiles = (0, zipstamp_server_1.getMetadataFileNames)();
141
+ const metadataFiles = (0, token_service_1.getMetadataFileNames)();
139
142
  const outPath = outputPath ?? inputPath.replace(/(\.nzip|\.zip)$/i, '-upgrade$1');
140
143
  const tempDir = path.dirname(outPath) || '.';
141
144
  const tempPath = path.join(tempDir, `.neozip-upgrade-${Date.now()}.tmp`);
@@ -144,7 +147,7 @@ async function upgradeZipForTimestamp(inputPath, outputPath, options = {}) {
144
147
  const { destPath, dataEndOffset, copiedEntries } = await zipCopy.copyZipEntriesOnly(inputPath, tempPath, {
145
148
  entryFilter: (entry) => !metadataFiles.includes(entry.filename || '')
146
149
  });
147
- const newEntry = (0, zipstamp_server_1.createTimestampMetadataEntry)(zip, confirmedMetadata);
150
+ const newEntry = (0, token_service_1.createTimestampMetadataEntry)(zip, confirmedMetadata);
148
151
  if (!newEntry) {
149
152
  throw new Error('Failed to create timestamp metadata entry');
150
153
  }
@@ -4,11 +4,7 @@
4
4
  * NEOZIP - Standalone ZIP creation tool with blockchain tokenization
5
5
  * Usage: neozip [options] <archive> <files...>
6
6
  *
7
- * Configuration: Use `neozip init` to set up wallet credentials
8
- * For blockchain features, wallet key can be provided via:
9
- * 1. CLI flag: -w 0x...
10
- * 2. Config file: ~/.neozip/wallet.json (recommended)
11
- * 3. ENV var: NEOZIP_WALLET_PASSKEY
7
+ * Configuration: neozip connect (alias: neozip init) Token Service account + wallet
12
8
  */
13
9
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
10
  if (k2 === undefined) k2 = k;
@@ -56,11 +52,13 @@ const fs = __importStar(require("fs"));
56
52
  // Try to load .env.local first, then .env as fallback
57
53
  const envLocalPath = path.join(process.cwd(), '.env.local');
58
54
  const envPath = path.join(process.cwd(), '.env');
55
+ // quiet: true suppresses dotenv's startup banner so it never pollutes
56
+ // machine-readable JSON written to stdout.
59
57
  if (fs.existsSync(envLocalPath)) {
60
- dotenv.config({ path: envLocalPath });
58
+ dotenv.config({ path: envLocalPath, quiet: true });
61
59
  }
62
60
  else if (fs.existsSync(envPath)) {
63
- dotenv.config({ path: envPath });
61
+ dotenv.config({ path: envPath, quiet: true });
64
62
  }
65
63
  const utils_1 = require("./neozip/utils");
66
64
  const user_interaction_1 = require("./neozip/user-interaction");
@@ -68,8 +66,13 @@ const createZip_1 = require("./neozip/createZip");
68
66
  const file_operations_1 = require("./neozip/file-operations");
69
67
  // ConfigSetup is lazy-loaded only when needed (init/config commands)
70
68
  const neozip_blockchain_1 = require("neozip-blockchain");
71
- const ConfigStore_1 = require("./config/ConfigStore");
69
+ const bootstrap_1 = require("./connection/bootstrap");
70
+ const cli_prefs_1 = require("./connection/cli-prefs");
72
71
  const exit_codes_1 = require("./exit-codes");
72
+ const output_1 = require("./cli/output");
73
+ const params_1 = require("./cli/params");
74
+ const validate_1 = require("./cli/validate");
75
+ const schema_1 = require("./cli/schema");
73
76
  const version_1 = require("./version");
74
77
  /**
75
78
  * Resolve dynamic import path so ts-node loads .ts and compiled dist loads .js
@@ -80,10 +83,49 @@ function resolveScriptPath(jsPath) {
80
83
  }
81
84
  return jsPath;
82
85
  }
86
+ /**
87
+ * Map an agent-supplied `--params` object onto ZipOptions (whitelisted keys).
88
+ */
89
+ function zipParamsToOptions(params) {
90
+ const allowed = [
91
+ 'verbose', 'quiet', 'level', 'blockchain', 'blockchainOts', 'blockchainTokenService',
92
+ 'blockchainDefault', 'blockchainMint', 'network', 'walletPasskey', 'walletKey',
93
+ 'compression', 'blockSize', 'encrypt', 'password', 'encryptionMethod', 'exclude',
94
+ 'include', 'inMemory', 'recurse', 'junkPaths', 'update', 'freshen', 'move',
95
+ 'archiveComment', 'commentFile', 'suffixes', 'debug', 'showFiles', 'toCrlf',
96
+ 'fromCrlf', 'preservePerms', 'symlinks', 'hardLinks', 'oldTimestamp', 'tempPath',
97
+ 'testIntegrity', 'nonInteractive', 'legacy', 'dryRun',
98
+ ];
99
+ const out = {};
100
+ for (const key of allowed) {
101
+ if (key in params) {
102
+ out[key] = params[key];
103
+ }
104
+ }
105
+ return out;
106
+ }
107
+ /**
108
+ * Map an agent-supplied `--json` payload onto seed positional args.
109
+ */
110
+ function zipPayloadToSeed(payload) {
111
+ const seed = {};
112
+ if (typeof payload.archive === 'string')
113
+ seed.archive = payload.archive;
114
+ if (Array.isArray(payload.files))
115
+ seed.files = payload.files.map(String);
116
+ const options = {};
117
+ if (Array.isArray(payload.include))
118
+ options.include = payload.include.map(String);
119
+ if (Array.isArray(payload.exclude))
120
+ options.exclude = payload.exclude.map(String);
121
+ if (Object.keys(options).length > 0)
122
+ seed.options = options;
123
+ return seed;
124
+ }
83
125
  /**
84
126
  * Parse command line arguments
85
127
  */
86
- function parseArgs(args) {
128
+ function parseArgs(args, seed) {
87
129
  const options = {
88
130
  verbose: false,
89
131
  quiet: false,
@@ -93,8 +135,7 @@ function parseArgs(args) {
93
135
  blockchainDefault: false, // Auto-select default option disabled by default
94
136
  blockchainMint: false, // Force mint new token disabled by default
95
137
  network: 'base-sepolia',
96
- walletPasskey: process.env.NEOZIP_WALLET_PASSKEY,
97
- timestampEmail: process.env.NEOZIP_TIMESTAMP_EMAIL,
138
+ walletPasskey: undefined,
98
139
  compression: 'zstd',
99
140
  blockSize: 128 * 1024, // 128KB default
100
141
  enableProgress: true,
@@ -103,10 +144,12 @@ function parseArgs(args) {
103
144
  freshen: false,
104
145
  move: false,
105
146
  interactiveComments: false,
106
- walletKey: undefined
147
+ walletKey: undefined,
148
+ // Seed from agent --params/--json first; explicit CLI flags below override.
149
+ ...(seed?.options ?? {}),
107
150
  };
108
- const files = [];
109
- let archive = '';
151
+ const files = seed?.files ? [...seed.files] : [];
152
+ let archive = seed?.archive ?? '';
110
153
  for (let i = 0; i < args.length; i++) {
111
154
  const arg = args[i];
112
155
  switch (arg) {
@@ -180,14 +223,7 @@ function parseArgs(args) {
180
223
  break;
181
224
  case '-ts':
182
225
  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
- }
226
+ options.blockchainTokenService = true;
191
227
  break;
192
228
  case '-bt':
193
229
  case '--blockchain-token':
@@ -260,45 +296,45 @@ function parseArgs(args) {
260
296
  break;
261
297
  case '-e':
262
298
  case '--encrypt':
263
- // InfoZip-compatible encryption option
264
299
  options.encrypt = true;
265
- // Only set encryption method if not already set
266
300
  if (!options.encryptionMethod) {
267
- options.encryptionMethod = 'pkzip'; // Default to PKZIP (InfoZip compatible)
301
+ options.encryptionMethod = 'aes256';
268
302
  }
269
- // Only prompt for password if not already provided
270
303
  if (!options.password) {
271
- options.password = 'PROMPT'; // Will prompt for password
304
+ options.password = 'PROMPT';
272
305
  }
273
306
  break;
274
307
  case '-P':
275
308
  case '--password':
276
- // InfoZip-compatible password option (uppercase P)
277
309
  options.encrypt = true;
278
- // Only set encryption method if not already set (e.g., by --pkzip-encrypt)
279
310
  if (!options.encryptionMethod) {
280
- options.encryptionMethod = 'pkzip'; // Default to PKZIP
311
+ options.encryptionMethod = 'aes256';
281
312
  }
282
313
  if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
283
- // Password provided directly
284
314
  options.password = args[++i];
285
315
  }
286
316
  else {
287
- // No password provided, will prompt later
288
317
  options.password = 'PROMPT';
289
318
  }
290
319
  break;
291
320
  case '-p':
292
321
  case '--show-progress':
293
- // InfoZip-compatible show-progress option (lowercase p)
294
322
  options.showFiles = true;
295
323
  break;
324
+ case '--aes256':
325
+ case '--aes-256':
326
+ options.encrypt = true;
327
+ options.encryptionMethod = 'aes256';
328
+ if (!options.password) {
329
+ options.password = 'PROMPT';
330
+ }
331
+ break;
332
+ case '--pkzip':
296
333
  case '--pkzip-encrypt':
297
334
  options.encrypt = true;
298
335
  options.encryptionMethod = 'pkzip';
299
- // Only prompt for password if not already provided
300
336
  if (!options.password) {
301
- options.password = 'PROMPT'; // Will prompt for password
337
+ options.password = 'PROMPT';
302
338
  }
303
339
  break;
304
340
  case '-x':
@@ -414,6 +450,12 @@ function parseArgs(args) {
414
450
  case '--overwrite':
415
451
  options.overwrite = true;
416
452
  break;
453
+ case '--legacy':
454
+ options.legacy = true;
455
+ break;
456
+ case '--dry-run':
457
+ options.dryRun = true;
458
+ break;
417
459
  case '-@':
418
460
  options.stdinFilenames = true;
419
461
  break;
@@ -432,12 +474,34 @@ function parseArgs(args) {
432
474
  break;
433
475
  }
434
476
  }
435
- // -ots and -ts are mutually exclusive (choose Bitcoin OTS or 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 -ots for Bitcoin timestamping or -ts for Zipstamp timestamping');
477
+ // -ots and -ts are mutually exclusive (choose Bitcoin OTS or Token Service)
478
+ if (options.blockchainOts && options.blockchainTokenService) {
479
+ console.error('Error: Cannot use both -ots (OpenTimestamps) and -ts (Token Service) in the same run');
480
+ console.error(' Use -ts for Token Service timestamping or -ots for Bitcoin timestamping');
439
481
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
440
482
  }
483
+ // --legacy: produce an InfoZip/PKZIP-compatible archive.
484
+ if (options.legacy) {
485
+ if (options.blockchain ||
486
+ options.blockchainOts ||
487
+ options.blockchainTokenService ||
488
+ options.blockchainMint ||
489
+ options.blockchainDefault ||
490
+ options.upgrade) {
491
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR, 'Error: --legacy cannot be combined with blockchain features (-b, -bd, -bm, -ots, -ts, --upgrade)');
492
+ }
493
+ // Force standard deflate (or store at level 0); zstd is not InfoZip-compatible.
494
+ if (options.compression !== 'deflate') {
495
+ options.compression = 'deflate';
496
+ }
497
+ // AES-256 is not readable by stock InfoZip unzip; fall back to PKZIP.
498
+ if (options.encrypt && options.encryptionMethod !== 'pkzip') {
499
+ if (!(0, output_1.isJsonOutput)()) {
500
+ console.error('Warning: --legacy forces PKZIP encryption (AES-256 is not InfoZip-compatible)');
501
+ }
502
+ options.encryptionMethod = 'pkzip';
503
+ }
504
+ }
441
505
  if (!archive) {
442
506
  console.error('Error: Archive name is required');
443
507
  showHelp();
@@ -457,6 +521,16 @@ function parseArgs(args) {
457
521
  archive = archive + '.nzip';
458
522
  }
459
523
  }
524
+ // Input hardening: reject control characters / dangerous Unicode in the name.
525
+ try {
526
+ (0, validate_1.assertSafeArchiveName)(archive);
527
+ }
528
+ catch (err) {
529
+ if (err instanceof validate_1.ValidationError) {
530
+ (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR, `Error: ${err.message}`);
531
+ }
532
+ throw err;
533
+ }
460
534
  // Skip file validation for upgrade mode (only needs archive path)
461
535
  if (files.length === 0 && !options.stdinFilenames && !options.upgrade) {
462
536
  console.error('Error: At least one file or directory must be specified');
@@ -469,17 +543,20 @@ function parseArgs(args) {
469
543
  * Show help information
470
544
  */
471
545
  function showHelp() {
546
+ // In JSON mode, never print human help to stdout (keeps stdout pure JSON).
547
+ if ((0, output_1.isJsonOutput)())
548
+ return;
472
549
  console.log(`
473
550
  NeoZip Version: ${version_1.APP_VERSION} (${version_1.APP_RELEASE_DATE})
474
551
 
475
552
  Usage: neozip [options] <archive> <files...>
476
553
 
477
554
  Configuration Commands:
478
- neozip init Interactive wallet setup wizard
479
- neozip config Show current configuration and manage settings (interactive menu)
480
- neozip upgrade <archive> [output] [--wait] Upgrade pending Zipstamp timestamp to confirmed
555
+ neozip connect [subcommand] Token Service account setup (alias: neozip init)
556
+ neozip config Connection dashboard (status, prefs, migrate)
557
+ neozip skills [subcommand] Install bundled Agent Skills for Claude/Cursor
558
+ neozip upgrade <archive> [output] [--wait] Upgrade pending Token Service timestamp to confirmed
481
559
  neozip mint <archive> [output] Mint TimestampProofNFT from confirmed timestamped ZIP
482
- neozip verify-email [--email <email>] [--set-default] Configure email for Zipstamp timestamping
483
560
 
484
561
  Options:
485
562
  -h, --help Show this help message
@@ -500,6 +577,12 @@ Options:
500
577
  --non-interactive Skip interactive prompts (exit on minting errors)
501
578
  --upgrade Upgrade existing ZIP file for tokenization (add SHA-256 hashes)
502
579
  --overwrite Overwrite input file when upgrading (default: create new file)
580
+ --legacy Create an InfoZip/PKZIP-compatible archive (deflate/store + PKZIP,
581
+ no blockchain/zstd; readable by stock unzip)
582
+ --dry-run Show what would be created without writing the archive
583
+ --format <fmt> Output format: text (default) or json (machine-readable)
584
+ --params <json> Provide options as a JSON object (agent input; '-' = stdin)
585
+ --json <json> Provide payload {archive, files, include, exclude} as JSON ('-' = stdin)
503
586
  -@ Read file names from stdin (one per line)
504
587
  -0 Store only (no compression)
505
588
  -1 to -9 Compression level (1=fastest, 9=best, default: 6)
@@ -508,10 +591,9 @@ Options:
508
591
  -bd, --blockchain-default Auto-select default token option (use existing or mint new)
509
592
  -bm, --blockchain-mint Force mint new token (skip existing token selection)
510
593
  -ots, --opentimestamp Enable OpenTimestamp proof on Bitcoin blockchain
511
- -ts, --timestamp Enable Zipstamp timestamp (blockchain)
512
- --timestamp-email <email> Email for Zipstamp server (some servers require verified email)
594
+ -ts, --timestamp Enable Token Service timestamp (blockchain; requires neozip connect)
513
595
  -n, --network <network> Blockchain network (base-sepolia, base-mainnet, default: base-sepolia)
514
- -w, --wallet-key <key> Wallet private key (overrides NEOZIP_WALLET_PASSKEY)
596
+ -w, --wallet-key <key> Wallet private key (one-off override; default: connection store)
515
597
  -c Add one-line comments for files
516
598
  --compression <alg> Compression algorithm (zstd, deflate, default: zstd)
517
599
  --deflate Use deflate compression (same as --compression deflate)
@@ -519,10 +601,11 @@ Options:
519
601
  --block-size <bytes> Block size for streaming (default: 131072)
520
602
  --no-progress Disable progress reporting
521
603
  --progress Enable enhanced progress reporting
522
- -e, --encrypt Encrypt files (InfoZip-compatible). Will prompt for password
523
- -P, --password [pwd] Encrypt files with password (InfoZip-compatible). Provide password or will prompt
604
+ -e, --encrypt Encrypt files with AES-256 (default). Will prompt for password
605
+ -P, --password [pwd] Encrypt files with password. Provide password or will prompt
606
+ --aes256 Use AES-256 encryption (default, recommended)
607
+ --pkzip, --pkzip-encrypt Use legacy PKZIP encryption (weak, for compatibility only)
524
608
  -p, --show-progress Show progress during compression (InfoZip-compatible)
525
- --pkzip-encrypt Use PKZIP encryption (legacy compatibility only, same as -e)
526
609
  -x, --exclude <pattern> Exclude files matching pattern (can be used multiple times)
527
610
  -i, --include <pattern> Include only files matching pattern (can be used multiple times)
528
611
  --suffixes <list> Don't compress files with these suffixes (can be used multiple times)
@@ -550,6 +633,8 @@ Examples:
550
633
  * Show extended help information (equivalent to InfoZip's -h2)
551
634
  */
552
635
  function showExtendedHelp() {
636
+ if ((0, output_1.isJsonOutput)())
637
+ return;
553
638
  console.log('Extended Help for NeoZip');
554
639
  console.log('');
555
640
  console.log('NeoZip is a next-generation ZIP utility that builds upon the strengths of the ZIP format');
@@ -650,12 +735,23 @@ function showExtendedHelp() {
650
735
  console.log('');
651
736
  console.log('Blockchain Features:');
652
737
  console.log(' -b, --blockchain enable blockchain tokenization');
653
- console.log(' --skip-blockchain disable blockchain features');
654
- console.log(' --network <network> specify blockchain network (base-sepolia, ethereum, etc.)');
655
- console.log(' --wallet <address> specify wallet address');
656
- console.log(' --private-key <key> specify private key (not recommended)');
657
- console.log(' --gas-limit <limit> set gas limit for transactions');
658
- console.log(' --gas-price <price> set gas price for transactions');
738
+ console.log(' -bd, --blockchain-default auto-select default token option');
739
+ console.log(' -bm, --blockchain-mint force mint a new token');
740
+ console.log(' -ots, --opentimestamp add an OpenTimestamps (Bitcoin) proof');
741
+ console.log(' -ts, --timestamp add a Token Service timestamp (requires neozip connect)');
742
+ console.log(' -n, --network <network> specify blockchain network (base-sepolia, base-mainnet, etc.)');
743
+ console.log(' -w, --wallet-key <key> one-off wallet private key (default: connection store)');
744
+ console.log('');
745
+ console.log('Legacy / InfoZip compatibility:');
746
+ console.log(' --legacy create a standard ZIP readable by stock InfoZip unzip');
747
+ console.log(' (forces deflate/store + PKZIP, disables zstd and blockchain extensions)');
748
+ console.log('');
749
+ console.log('Agent / automation:');
750
+ console.log(' --format json emit a machine-readable JSON result on stdout');
751
+ console.log(' --params <json> supply options as a JSON object ("-" reads stdin)');
752
+ console.log(' --json <json> supply payload {archive, files, include, exclude} ("-" reads stdin)');
753
+ console.log(' --dry-run resolve and print the plan without writing');
754
+ console.log(' neozip schema neozip print the machine-readable command schema');
659
755
  console.log('');
660
756
  console.log('Testing archives:');
661
757
  console.log(' -T test completed archive with neounzip before finalizing');
@@ -695,49 +791,56 @@ function showExtendedHelp() {
695
791
  */
696
792
  async function main() {
697
793
  try {
794
+ (0, bootstrap_1.bootstrapConnectionContext)();
698
795
  // Parse command line arguments
699
796
  const args = process.argv.slice(2);
797
+ // Resolve machine-readable output mode early so error exits are structured.
798
+ (0, output_1.setOutputFormat)((0, output_1.detectOutputFormat)(args));
700
799
  // Handle configuration commands first
701
800
  if (args.length > 0) {
702
801
  const command = args[0];
703
- // Handle `neozip init` - interactive setup wizard
802
+ // `neozip schema <command>` — machine-readable command introspection
803
+ if (command === 'schema') {
804
+ const code = (0, schema_1.handleSchemaCommand)(args.slice(1));
805
+ process.exit(code);
806
+ }
807
+ // `neozip init` is an alias for `neozip connect`
704
808
  if (command === 'init') {
705
- // Lazy-load ConfigSetup only when needed
706
- const { ConfigSetup } = await import(resolveScriptPath('./config/ConfigSetup.js'));
707
- const wizard = new ConfigSetup();
708
- const success = await wizard.run();
709
- process.exit(success ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
809
+ console.error('Note: neozip init is an alias for neozip connect.\n');
810
+ const { runConnectCommand } = await import(resolveScriptPath('./connect/command.js'));
811
+ const code = await runConnectCommand([]);
812
+ process.exit(code === 0 ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
710
813
  }
711
- // Handle `neozip config` - show interactive menu
814
+ // Handle `neozip config` connection dashboard
712
815
  if (command === 'config') {
713
- // Lazy-load ConfigSetup only when needed
714
816
  const { ConfigSetup } = await import(resolveScriptPath('./config/ConfigSetup.js'));
715
817
  await ConfigSetup.showAndManage();
716
- // showAndManage handles its own exit
717
818
  }
718
- // Handle `neozip verify-email` - configure email for Zipstamp timestamping
719
- if (command === 'verify-email') {
720
- const verifyArgs = args.slice(1);
721
- let email;
722
- let setDefaultOnly = false;
723
- let debug = false;
724
- for (let i = 0; i < verifyArgs.length; i++) {
725
- const arg = verifyArgs[i];
726
- if (arg === '--email' && verifyArgs[i + 1]) {
727
- email = verifyArgs[++i];
728
- }
729
- else if (arg === '--set-default') {
730
- setDefaultOnly = true;
731
- }
732
- else if (arg === '--debug') {
733
- debug = true;
734
- }
819
+ // Handle `neozip connect` Token Service account setup
820
+ if (command === 'connect') {
821
+ const { runConnectCommand, connectUsage } = await import(resolveScriptPath('./connect/command.js'));
822
+ const connectArgv = args.slice(1);
823
+ if (connectArgv.length === 1 && (connectArgv[0] === '--help' || connectArgv[0] === '-h')) {
824
+ connectUsage();
825
+ process.exit(exit_codes_1.ZIP_EXIT_CODES.SUCCESS);
735
826
  }
736
- const { runVerifyEmail } = await import(resolveScriptPath('./commands/verifyEmail.js'));
737
- const success = await runVerifyEmail({ email, setDefaultOnly, debug });
738
- process.exit(success ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
827
+ const code = await runConnectCommand(connectArgv);
828
+ process.exit(code === 0 ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
829
+ }
830
+ // Handle `neozip skills` — install bundled Agent Skills
831
+ if (command === 'skills') {
832
+ const { runSkillsCommand, skillsUsage } = await import(resolveScriptPath('./skills/command.js'));
833
+ const skillsArgv = args.slice(1);
834
+ if (skillsArgv.length === 0 ||
835
+ skillsArgv.includes('--help') ||
836
+ skillsArgv.includes('-h')) {
837
+ skillsUsage();
838
+ process.exit(exit_codes_1.ZIP_EXIT_CODES.SUCCESS);
839
+ }
840
+ const code = runSkillsCommand(skillsArgv);
841
+ process.exit(code === 0 ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
739
842
  }
740
- // Handle `neozip upgrade` - upgrade pending Zipstamp timestamp to confirmed
843
+ // Handle `neozip upgrade` - upgrade pending Token Service timestamp to confirmed
741
844
  if (command === 'upgrade') {
742
845
  const upgradeArgs = args.slice(1);
743
846
  let inputPath = '';
@@ -819,14 +922,30 @@ async function main() {
819
922
  process.exit(success ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
820
923
  }
821
924
  }
822
- let { archive, files, options } = parseArgs(args);
823
- // If network not explicitly provided via CLI, use from ConfigStore (which reads from ENV or wallet.json)
925
+ // Extract agent input (--params/--json) and strip the --format flag, then
926
+ // parse the remaining argv. Seed values are overridden by explicit flags.
927
+ const agent = (0, params_1.extractAgentInput)((0, output_1.stripFormatFlag)(args));
928
+ const seed = {};
929
+ if (agent.params)
930
+ seed.options = zipParamsToOptions(agent.params);
931
+ if (agent.payload) {
932
+ const payloadSeed = zipPayloadToSeed(agent.payload);
933
+ if (payloadSeed.archive)
934
+ seed.archive = payloadSeed.archive;
935
+ if (payloadSeed.files)
936
+ seed.files = payloadSeed.files;
937
+ if (payloadSeed.options) {
938
+ seed.options = { ...(seed.options ?? {}), ...payloadSeed.options };
939
+ }
940
+ }
941
+ let { archive, files, options } = parseArgs(agent.argv, seed);
942
+ // In JSON mode, suppress decorative human logs so stdout stays pure JSON.
943
+ if ((0, output_1.isJsonOutput)()) {
944
+ options.quiet = true;
945
+ }
824
946
  const networkProvidedViaCLI = args.includes('--network') || args.includes('-n');
825
947
  if (!networkProvidedViaCLI) {
826
- const config = ConfigStore_1.ConfigStore.getConfig();
827
- if (config.network) {
828
- options.network = config.network;
829
- }
948
+ options.network = (0, cli_prefs_1.resolveNetwork)(options.network);
830
949
  }
831
950
  // Handle stdin filenames
832
951
  if (options.stdinFilenames) {
@@ -852,6 +971,41 @@ async function main() {
852
971
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
853
972
  }
854
973
  }
974
+ // Dry-run: resolve and report the plan without writing anything.
975
+ if (options.dryRun) {
976
+ const action = options.delete ? 'delete' : options.upgrade ? 'upgrade' : 'create';
977
+ const plan = {
978
+ command: 'neozip',
979
+ action,
980
+ dryRun: true,
981
+ archive,
982
+ files: options.delete ? undefined : files,
983
+ deleteFiles: options.delete ? files : undefined,
984
+ legacy: !!options.legacy,
985
+ resolvedOptions: {
986
+ level: options.level,
987
+ compression: options.level === 0 ? 'store' : options.compression,
988
+ encrypt: !!options.encrypt,
989
+ encryptionMethod: options.encrypt ? options.encryptionMethod : undefined,
990
+ blockchain: !!options.blockchain,
991
+ blockchainOts: !!options.blockchainOts,
992
+ blockchainTokenService: !!options.blockchainTokenService,
993
+ network: options.network,
994
+ recurse: !!options.recurse,
995
+ junkPaths: !!options.junkPaths,
996
+ include: options.include,
997
+ exclude: options.exclude,
998
+ },
999
+ };
1000
+ if ((0, output_1.isJsonOutput)()) {
1001
+ (0, output_1.emitResult)(plan);
1002
+ }
1003
+ else {
1004
+ console.log('Dry run — no archive will be written.');
1005
+ console.log(JSON.stringify(plan, null, 2));
1006
+ }
1007
+ process.exit(exit_codes_1.ZIP_EXIT_CODES.SUCCESS);
1008
+ }
855
1009
  // Handle upgrade operation
856
1010
  if (options.upgrade) {
857
1011
  // Validate: upgrade requires exactly one argument (the ZIP file path)
@@ -878,6 +1032,9 @@ async function main() {
878
1032
  if (options.delete) {
879
1033
  await (0, file_operations_1.deleteFromArchive)(archive, files, options);
880
1034
  (0, utils_1.log)('✓ Delete operation completed successfully', options);
1035
+ if ((0, output_1.isJsonOutput)()) {
1036
+ (0, output_1.emitResult)({ command: 'neozip', action: 'delete', archive, deleteFiles: files });
1037
+ }
881
1038
  return;
882
1039
  }
883
1040
  // Create ZIP archive
@@ -887,6 +1044,27 @@ async function main() {
887
1044
  await (0, file_operations_1.moveFiles)(files, options);
888
1045
  }
889
1046
  (0, utils_1.log)('✓ NeoZip completed successfully', options);
1047
+ if ((0, output_1.isJsonOutput)()) {
1048
+ let size;
1049
+ try {
1050
+ size = fs.existsSync(archive) ? fs.statSync(archive).size : undefined;
1051
+ }
1052
+ catch {
1053
+ size = undefined;
1054
+ }
1055
+ (0, output_1.emitResult)({
1056
+ command: 'neozip',
1057
+ action: 'create',
1058
+ archive,
1059
+ size,
1060
+ fileCount: files.length,
1061
+ legacy: !!options.legacy,
1062
+ compression: options.level === 0 ? 'store' : options.compression,
1063
+ encrypted: !!options.encrypt,
1064
+ encryptionMethod: options.encrypt ? options.encryptionMethod : undefined,
1065
+ blockchain: !!options.blockchain,
1066
+ });
1067
+ }
890
1068
  // Explicitly exit to ensure process terminates (provider cleanup should allow this)
891
1069
  process.exit(exit_codes_1.ZIP_EXIT_CODES.SUCCESS);
892
1070
  }