neozip-cli 0.90.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 +58 -1
  3. package/DOCUMENTATION.md +22 -30
  4. package/README.md +104 -42
  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 +93 -9
  56. package/dist/src/neounzip.js +217 -59
  57. package/dist/src/neozip/blockchain.js +18 -18
  58. package/dist/src/neozip/createZip.js +114 -91
  59. package/dist/src/neozip/upgradeZip.js +14 -11
  60. package/dist/src/neozip.js +252 -75
  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 -82
  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
@@ -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':
@@ -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 Ethereum Zipstamp)
436
- if (options.blockchainOts && options.blockchainZipstamp) {
437
- console.error('Error: Cannot use both -ots (OpenTimestamps) and -ts (Zipstamp) in the same run');
438
- console.error(' Use -ts for Zipstamp timestamping or -ots for Bitcoin timestamping');
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 Ethereum 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)
@@ -551,6 +633,8 @@ Examples:
551
633
  * Show extended help information (equivalent to InfoZip's -h2)
552
634
  */
553
635
  function showExtendedHelp() {
636
+ if ((0, output_1.isJsonOutput)())
637
+ return;
554
638
  console.log('Extended Help for NeoZip');
555
639
  console.log('');
556
640
  console.log('NeoZip is a next-generation ZIP utility that builds upon the strengths of the ZIP format');
@@ -651,12 +735,23 @@ function showExtendedHelp() {
651
735
  console.log('');
652
736
  console.log('Blockchain Features:');
653
737
  console.log(' -b, --blockchain enable blockchain tokenization');
654
- console.log(' --skip-blockchain disable blockchain features');
655
- console.log(' --network <network> specify blockchain network (base-sepolia, ethereum, etc.)');
656
- console.log(' --wallet <address> specify wallet address');
657
- console.log(' --private-key <key> specify private key (not recommended)');
658
- console.log(' --gas-limit <limit> set gas limit for transactions');
659
- 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');
660
755
  console.log('');
661
756
  console.log('Testing archives:');
662
757
  console.log(' -T test completed archive with neounzip before finalizing');
@@ -696,49 +791,56 @@ function showExtendedHelp() {
696
791
  */
697
792
  async function main() {
698
793
  try {
794
+ (0, bootstrap_1.bootstrapConnectionContext)();
699
795
  // Parse command line arguments
700
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));
701
799
  // Handle configuration commands first
702
800
  if (args.length > 0) {
703
801
  const command = args[0];
704
- // 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`
705
808
  if (command === 'init') {
706
- // Lazy-load ConfigSetup only when needed
707
- const { ConfigSetup } = await import(resolveScriptPath('./config/ConfigSetup.js'));
708
- const wizard = new ConfigSetup();
709
- const success = await wizard.run();
710
- 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);
711
813
  }
712
- // Handle `neozip config` - show interactive menu
814
+ // Handle `neozip config` connection dashboard
713
815
  if (command === 'config') {
714
- // Lazy-load ConfigSetup only when needed
715
816
  const { ConfigSetup } = await import(resolveScriptPath('./config/ConfigSetup.js'));
716
817
  await ConfigSetup.showAndManage();
717
- // showAndManage handles its own exit
718
818
  }
719
- // Handle `neozip verify-email` - configure email for Zipstamp timestamping
720
- if (command === 'verify-email') {
721
- const verifyArgs = args.slice(1);
722
- let email;
723
- let setDefaultOnly = false;
724
- let debug = false;
725
- for (let i = 0; i < verifyArgs.length; i++) {
726
- const arg = verifyArgs[i];
727
- if (arg === '--email' && verifyArgs[i + 1]) {
728
- email = verifyArgs[++i];
729
- }
730
- else if (arg === '--set-default') {
731
- setDefaultOnly = true;
732
- }
733
- else if (arg === '--debug') {
734
- debug = true;
735
- }
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);
736
826
  }
737
- const { runVerifyEmail } = await import(resolveScriptPath('./commands/verifyEmail.js'));
738
- const success = await runVerifyEmail({ email, setDefaultOnly, debug });
739
- process.exit(success ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
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);
740
829
  }
741
- // Handle `neozip upgrade` - upgrade pending Zipstamp timestamp to confirmed
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);
842
+ }
843
+ // Handle `neozip upgrade` - upgrade pending Token Service timestamp to confirmed
742
844
  if (command === 'upgrade') {
743
845
  const upgradeArgs = args.slice(1);
744
846
  let inputPath = '';
@@ -820,14 +922,30 @@ async function main() {
820
922
  process.exit(success ? exit_codes_1.ZIP_EXIT_CODES.SUCCESS : exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
821
923
  }
822
924
  }
823
- let { archive, files, options } = parseArgs(args);
824
- // 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
+ }
825
946
  const networkProvidedViaCLI = args.includes('--network') || args.includes('-n');
826
947
  if (!networkProvidedViaCLI) {
827
- const config = ConfigStore_1.ConfigStore.getConfig();
828
- if (config.network) {
829
- options.network = config.network;
830
- }
948
+ options.network = (0, cli_prefs_1.resolveNetwork)(options.network);
831
949
  }
832
950
  // Handle stdin filenames
833
951
  if (options.stdinFilenames) {
@@ -853,6 +971,41 @@ async function main() {
853
971
  (0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.PARAMETER_ERROR);
854
972
  }
855
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
+ }
856
1009
  // Handle upgrade operation
857
1010
  if (options.upgrade) {
858
1011
  // Validate: upgrade requires exactly one argument (the ZIP file path)
@@ -879,6 +1032,9 @@ async function main() {
879
1032
  if (options.delete) {
880
1033
  await (0, file_operations_1.deleteFromArchive)(archive, files, options);
881
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
+ }
882
1038
  return;
883
1039
  }
884
1040
  // Create ZIP archive
@@ -888,6 +1044,27 @@ async function main() {
888
1044
  await (0, file_operations_1.moveFiles)(files, options);
889
1045
  }
890
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
+ }
891
1068
  // Explicitly exit to ensure process terminates (provider cleanup should allow this)
892
1069
  process.exit(exit_codes_1.ZIP_EXIT_CODES.SUCCESS);
893
1070
  }