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
package/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D16.0.0-brightgreen)](https://nodejs.org/)
8
8
 
9
- ## ⚠️ Beta Release Notice
9
+ ## Release Notice
10
10
 
11
- **This is a beta release (v0.75.0-beta).** This software is in active development and may contain bugs or incomplete features. While more stable than alpha releases, please use with caution in production environments.
11
+ **Current version: v0.90.0.** This release adds full AES-256 encryption support. See [CHANGELOG.md](CHANGELOG.md) for details.
12
12
 
13
13
  - **API Stability**: Command-line interface is becoming more stable but may still change
14
14
  - **Testing**: Please test thoroughly and report any issues
@@ -21,7 +21,7 @@ NeoZip CLI is a modern, full-featured ZIP utility that provides:
21
21
  - **Standard ZIP Compatibility**: ~99% compatible with InfoZip utilities
22
22
  - **Blockchain Integration**: Tokenize archives and verify integrity on-chain
23
23
  - **Advanced Compression**: Zstd, Deflate, and Store compression methods
24
- - **Encryption Support**: PKZIP encryption
24
+ - **Encryption Support**: AES-256 (default) and PKZIP encryption
25
25
  - **Cross-Platform**: Works on macOS, Linux, and Windows
26
26
 
27
27
  ## Features
@@ -42,14 +42,15 @@ NeoZip CLI is a modern, full-featured ZIP utility that provides:
42
42
  - Compression levels: 0-9 (0=store, 9=best compression)
43
43
 
44
44
  ### Encryption
45
- - **PKZIP**: Legacy encryption (maximum compatibility)
45
+ - **AES-256**: Strong encryption (default, recommended)
46
+ - **PKZIP**: Legacy encryption (for compatibility only)
46
47
  - Password-protected archives
47
- - InfoZip-compatible encrypted archives
48
+ - Auto-detection of encryption method during extraction
48
49
 
49
50
  ### Blockchain Features
50
51
  - **Tokenization**: Create on-chain tokens for archives
51
52
  - **OpenTimestamp**: Bitcoin blockchain timestamping
52
- - **Zipstamp**: Ethereum blockchain timestamping (Zipstamp server)
53
+ - **Token Service**: Ethereum blockchain timestamping (NeoZip Token Service)
53
54
  - **Integrity Verification**: Verify archive integrity on-chain
54
55
  - **Network Support**: Base Sepolia, Base Mainnet, Arbitrum, and more
55
56
 
@@ -68,12 +69,13 @@ NeoZip CLI is a modern, full-featured ZIP utility that provides:
68
69
  ### Prerequisites
69
70
 
70
71
  - Node.js 16.0.0 or higher
71
- - npm or yarn package manager
72
+ - [pnpm](https://pnpm.io/) 10+ (required for development from source)
73
+ - npm (optional — for global install of the published package only)
72
74
 
73
- ### Install from npm (Beta Release)
75
+ ### Install from npm
74
76
 
75
77
  ```bash
76
- npm install -g neozip-cli@beta
78
+ npm install -g neozip-cli
77
79
  ```
78
80
 
79
81
  After installation, the following commands will be available:
@@ -138,13 +140,19 @@ neozip -0 output.nzip ./src/
138
140
  ### Encryption Examples
139
141
 
140
142
  ```bash
141
- # Create encrypted archive (will prompt for password)
143
+ # Create AES-256 encrypted archive (default, will prompt for password)
142
144
  neozip -e secure.nzip file.txt
143
145
 
144
- # Create encrypted archive with password
146
+ # Create AES-256 encrypted archive with password
145
147
  neozip -e -P "mypassword" secure.nzip file.txt
146
148
 
147
- # Extract encrypted archive
149
+ # Explicitly use AES-256
150
+ neozip --aes256 -P "mypassword" secure.nzip file.txt
151
+
152
+ # Use legacy PKZIP encryption (weak, for compatibility only)
153
+ neozip --pkzip -P "mypassword" legacy.nzip file.txt
154
+
155
+ # Extract encrypted archive (auto-detects AES-256 or PKZIP)
148
156
  neounzip -P "mypassword" secure.nzip tests/extracted/
149
157
  ```
150
158
 
@@ -157,11 +165,11 @@ neozip -b tokenized.nzip file.txt
157
165
  # Tokenize with OpenTimestamp proof (Bitcoin)
158
166
  neozip -ots timestamped.nzip file.txt
159
167
 
160
- # Create Zipstamp timestamp (Ethereum)
161
- neozip -ts zipstamp.nzip file.txt
168
+ # Create Token Service timestamp (Ethereum)
169
+ neozip -ts timestamped.nzip file.txt
162
170
 
163
- # Upgrade pending Zipstamp timestamp to confirmed
164
- neozip upgrade zipstamp.nzip [output.nzip] [--wait]
171
+ # Upgrade pending Token Service timestamp to confirmed
172
+ neozip upgrade timestamped.nzip [output.nzip] [--wait]
165
173
 
166
174
  # Extract and verify blockchain integrity
167
175
  neounzip tokenized.nzip tests/extracted/
@@ -183,21 +191,22 @@ neozip [options] <archive> [files...]
183
191
  - `-0` to `-9` - Compression level (0=store, 9=best)
184
192
  - `-x, --exclude <pattern>` - Exclude files matching pattern
185
193
  - `-i, --include <pattern>` - Include only matching files
186
- - `-e, --encrypt` - Encrypt files (will prompt for password)
194
+ - `-e, --encrypt` - Encrypt files with AES-256 (default, will prompt for password)
187
195
  - `-P, --password <pwd>` - Encrypt with password
196
+ - `--aes256` - Use AES-256 encryption (default, recommended)
197
+ - `--pkzip` - Use legacy PKZIP encryption (weak, for compatibility only)
188
198
  - `-b, --blockchain` - Enable blockchain tokenization
189
199
  - `-ots, --opentimestamp` - Enable OpenTimestamp proof (Bitcoin)
190
- - `-ts, --timestamp` - Enable Zipstamp timestamp (Ethereum)
191
- - `--timestamp-email <email>` - Email for Zipstamp server (some servers require verified email)
200
+ - `-ts, --timestamp` - Enable Token Service timestamp (Ethereum; requires `neozip connect`)
192
201
  - `-T, --test-integrity` - Test archive after creation
193
202
  - `-v, --verbose` - Enable verbose output
194
203
  - `-q, --quiet` - Suppress output
195
204
 
196
205
  **Configuration Commands:**
197
206
  ```bash
198
- neozip init # Interactive wallet setup wizard
199
- neozip config # Show current configuration
200
- neozip upgrade <archive> [output] [--wait] # Upgrade pending Zipstamp timestamp to confirmed
207
+ neozip connect # Token Service account + wallet setup (alias: neozip init)
208
+ neozip config # Connection dashboard (status, prefs, migrate)
209
+ neozip upgrade <archive> [output] [--wait] # Upgrade pending Token Service timestamp to confirmed
201
210
  ```
202
211
 
203
212
  **Examples:**
@@ -232,7 +241,7 @@ neounzip [options] <archive> [output]
232
241
  - `-o, --overwrite` - Overwrite files without prompting
233
242
  - `-t, --test` - Test archive integrity
234
243
  - `-l, --list` - List contents without extracting
235
- - `-P, --password <pwd>` - Password for encrypted archives
244
+ - `-P, --password <pwd>` - Password for encrypted archives (AES-256 and PKZIP auto-detected)
236
245
  - `-x, --exclude <pattern>` - Exclude files from extraction
237
246
  - `-i, --include <pattern>` - Include only matching files
238
247
  - `-j, --junk-paths` - Extract files without directory structure
@@ -292,41 +301,26 @@ neolist -1 archive.nzip
292
301
 
293
302
  ## Configuration
294
303
 
295
- ### Wallet Setup (for Blockchain Features)
304
+ ### Account and wallet setup
296
305
 
297
- To use blockchain features, you need to configure a wallet:
306
+ All wallet identity and Token Service credentials live in the shared connection store (`~/.neozip/connection/`). Use the same profile as NeoZip Desktop and MCP.
298
307
 
299
308
  ```bash
300
- # Interactive setup wizard
301
- neozip init
309
+ neozip connect # Interactive setup (alias: neozip init)
310
+ neozip connect status # Show connection phase and credentials
311
+ neozip connect settings # Default network, RPC overrides, gas prefs
312
+ neozip connect migrate # Import legacy wallet.json / MCP profiles
313
+ neozip config # Connection dashboard (status, prefs, migrate)
302
314
  ```
303
315
 
304
- This will guide you through:
305
- 1. Creating or importing a wallet
306
- 2. Selecting a blockchain network
307
- 3. Saving configuration to `~/.neozip/wallet.json`
316
+ On-chain preferences (`defaultNetwork`, custom RPC URLs, gas) are stored in each connection's `public.json`.
308
317
 
309
- ### Environment Variables
310
-
311
- You can also configure via environment variables:
318
+ Optional overrides:
312
319
 
313
320
  ```bash
314
- export NEOZIP_WALLET_PASSKEY="your-wallet-key"
315
- export NEOZIP_NETWORK="base-sepolia" # or base-mainnet, arbitrum-sepolia, etc.
316
- export ZIPSTAMP_SERVER_URL="https://zipstamp-dev.neozip.io" # Zipstamp server (optional)
317
- export NEOZIP_TIMESTAMP_EMAIL="your@email.com" # Email for Zipstamp (some servers require verified email)
318
- ```
319
-
320
- ### Configuration File
321
-
322
- Configuration is stored in `~/.neozip/wallet.json`:
323
-
324
- ```json
325
- {
326
- "walletKey": "0x...",
327
- "network": "base-sepolia",
328
- "rpcUrl": "https://..."
329
- }
321
+ export NEOZIP_CONNECTION_DIR="/path/to/connection" # Store path override
322
+ export NEOZIP_UNLOCK_PASSPHRASE="..." # Decrypt secrets.enc non-interactively
323
+ export NEOZIP_NETWORK="base-sepolia" # Override default network for this session
330
324
  ```
331
325
 
332
326
  ## Advanced Examples
@@ -376,10 +370,13 @@ neozip -r -x "node_modules" -x "*.log" project.nzip ./
376
370
  ### Encryption
377
371
 
378
372
  ```bash
379
- # Create encrypted archive
373
+ # Create AES-256 encrypted archive (default)
380
374
  neozip -e -P "secure-password" secret.nzip sensitive-files/
381
375
 
382
- # Extract encrypted archive
376
+ # Create legacy PKZIP encrypted archive
377
+ neozip --pkzip -P "secure-password" legacy.nzip sensitive-files/
378
+
379
+ # Extract encrypted archive (auto-detects encryption method)
383
380
  neounzip -P "secure-password" secret.nzip decrypted/
384
381
  ```
385
382
 
@@ -415,6 +412,73 @@ neozip -X archive.nzip ./files/
415
412
  neozip -y archive.nzip ./symlinks/
416
413
  ```
417
414
 
415
+ ## For AI agents
416
+
417
+ NeoZip ships an agent-friendly surface. See [AGENTS.md](AGENTS.md) and the
418
+ [skills/](skills/) directory for the full guide.
419
+
420
+ ### Install Agent Skills
421
+
422
+ Six bundled skills (`neozip-shared`, `neozip-create`, `neozip-extract`, `neozip-list`,
423
+ `neozip-connect`, `neozip-legacy`) ship inside the npm package. Install them into
424
+ Claude Code or Cursor after `npm install -g neozip-cli`:
425
+
426
+ ```bash
427
+ # Symlink bundled skills into ~/.claude/skills and ~/.cursor/skills
428
+ neozip skills install
429
+
430
+ # Claude Code only
431
+ neozip skills install --target claude
432
+
433
+ # Show where skills live in your install
434
+ neozip skills path
435
+ neozip skills list
436
+
437
+ # ZIP files for Claude Cowork / claude.ai upload (Customize > Skills)
438
+ neozip skills pack --output-dir ./neozip-skills-zips
439
+ ```
440
+
441
+ Alternative (skills registry, same format as Google Workspace CLI):
442
+
443
+ ```bash
444
+ npx skills add https://github.com/NeoWareInc/neozip-cli
445
+ npx skills add https://github.com/NeoWareInc/neozip-cli/tree/main/skills/neozip-create
446
+ ```
447
+
448
+ ### CLI conventions
449
+
450
+ - **Machine-readable output:** add `--format json` to any command to get a
451
+ single JSON object on stdout (`{ "ok": true, ... }` or
452
+ `{ "ok": false, "error": { ... } }`). Human hints go to stderr.
453
+ - **JSON input:** `--params '<JSON>'` (options) and `--json '<JSON>'` (payload:
454
+ archive + files/targets). Either accepts `-` to read from stdin. Explicit CLI
455
+ flags override JSON input.
456
+ - **Schema introspection:** `neozip schema <neozip|neounzip|neolist|connect>`
457
+ emits the accepted fields, types, defaults, and exit codes.
458
+ - **Dry-run:** `neozip`/`neounzip` accept `--dry-run` to resolve and print the
459
+ plan as JSON without writing anything.
460
+ - **Safety:** archive names and extraction dirs are validated for control
461
+ characters; `neounzip` refuses zip-slip (entries escaping the target dir).
462
+ - **Exit codes:** InfoZip-compatible and echoed in `error.exitCode`.
463
+
464
+ ```bash
465
+ # Introspect, then create fully from JSON
466
+ neozip schema neozip
467
+ neozip --format json \
468
+ --params '{"level":9,"encrypt":true,"password":"s3cret"}' \
469
+ --json '{"archive":"out.zip","files":["src/","README.md"]}'
470
+
471
+ # Preview an extraction, then run it
472
+ neounzip --format json --dry-run --params '{"exdir":"out"}' in.zip
473
+ neounzip --format json --params '{"exdir":"out","overwrite":true}' in.zip
474
+
475
+ # List as JSON
476
+ neolist --format json in.zip
477
+ ```
478
+
479
+ For an archive that stock InfoZip `unzip` can read, add `--legacy` (forces
480
+ deflate/store + PKZIP, disables NeoZip extensions).
481
+
418
482
  ## Requirements
419
483
 
420
484
  - **Node.js**: 16.0.0 or higher
@@ -467,11 +531,11 @@ neozip new-archive.nzip original-files/
467
531
 
468
532
  **Blockchain errors:**
469
533
  ```bash
470
- # Check wallet configuration
471
- neozip config
534
+ # Check connection status
535
+ neozip connect status
472
536
 
473
- # Reconfigure wallet
474
- neozip init
537
+ # Re-run setup
538
+ neozip connect
475
539
  ```
476
540
 
477
541
  **Encryption errors:**
@@ -479,19 +543,29 @@ neozip init
479
543
  # Verify password is correct
480
544
  neounzip -P "your-password" archive.nzip
481
545
 
482
- # Try different encryption method
483
- neozip --pkzip-encrypt -P "password" archive.nzip files/
546
+ # Use legacy PKZIP if AES-256 is not supported by target tool
547
+ neozip --pkzip -P "password" archive.nzip files/
484
548
  ```
485
549
 
486
550
  ## Contributing
487
551
 
488
- We welcome feedback and contributions! Since this is a beta release, we're actively seeking input:
552
+ We welcome feedback and contributions:
489
553
 
490
554
  1. **Report Issues**: Use [GitHub Issues](https://github.com/NeoWareInc/neozip-support/issues) or email [support@neozip.io](mailto:support@neozip.io)
491
555
  2. **Suggest Features**: Share your ideas and use cases
492
556
  3. **Test and Provide Feedback**: Help us improve by testing and reporting your experience
493
557
 
494
- **Important**: This is a beta release. API and behavior may still change. Please test thoroughly.
558
+ ### Development setup
559
+
560
+ Requires [pnpm](https://pnpm.io/) (`packageManager: pnpm@10.12.1`):
561
+
562
+ ```bash
563
+ pnpm install
564
+ pnpm build
565
+ pnpm test
566
+ ```
567
+
568
+ **Important**: Please test thoroughly and report issues. See [CHANGELOG.md](CHANGELOG.md) for version history.
495
569
 
496
570
  ## License
497
571
 
@@ -511,6 +585,6 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
511
585
 
512
586
  ---
513
587
 
514
- **Version**: 0.75.0-beta
515
- **Status**: Beta Release
516
- **Last Updated**: December 26, 2025
588
+ **Version**: 0.90.0
589
+ **Status**: Release (see [CHANGELOG.md](CHANGELOG.md) for latest changes)
590
+ **Last Updated**: March 18, 2026
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeAccountPhase = computeAccountPhase;
4
+ exports.buildAccountStatusReport = buildAccountStatusReport;
5
+ const token_service_1 = require("neozip-blockchain/token-service");
6
+ const mask_1 = require("../util/mask");
7
+ const profile_store_1 = require("./profile-store");
8
+ const token_auth_1 = require("../connection/token-auth");
9
+ function nextStepsForPhase(phase) {
10
+ switch (phase) {
11
+ case "no_profile":
12
+ return (0, profile_store_1.listProfiles)().length > 0
13
+ ? ["account_select", "account_list", "account_create"]
14
+ : ["account_create", "account_list"];
15
+ case "email_unverified":
16
+ return ["account_register_email", "account_verify_email"];
17
+ case "no_wallet":
18
+ return ["account_create_wallet", "account_import_wallet"];
19
+ case "wallet_unlinked":
20
+ return ["account_link_wallet"];
21
+ case "identity_missing":
22
+ return ["account_provision_identity"];
23
+ case "ready":
24
+ return ["account_logout"];
25
+ case "degraded":
26
+ return ["account_status"];
27
+ default:
28
+ return ["account_status"];
29
+ }
30
+ }
31
+ function computeAccountPhase() {
32
+ const profileId = (0, profile_store_1.getActiveProfileId)();
33
+ if (!profileId) {
34
+ return "no_profile";
35
+ }
36
+ const profile = (0, profile_store_1.getActiveProfile)();
37
+ const secrets = (0, profile_store_1.readActiveSecrets)();
38
+ if (!profile || !secrets)
39
+ return "no_profile";
40
+ if (!secrets.accessToken || !profile.emailVerified) {
41
+ return "email_unverified";
42
+ }
43
+ if (!secrets.evmPrivateKey || !profile.evmAddress) {
44
+ return "no_wallet";
45
+ }
46
+ if (!profile.walletId || profile.linkId == null) {
47
+ return "wallet_unlinked";
48
+ }
49
+ if (!profile.identityKeyId) {
50
+ return "identity_missing";
51
+ }
52
+ return "ready";
53
+ }
54
+ async function buildAccountStatusReport(options) {
55
+ const profile = (0, profile_store_1.getActiveProfile)();
56
+ const secrets = (0, profile_store_1.readActiveSecrets)();
57
+ const phase = computeAccountPhase();
58
+ const tokenExpiresAt = secrets?.accessTokenExpiresAt ?? null;
59
+ const tokenExpired = Boolean(secrets?.accessToken) && (0, token_auth_1.isAccessTokenExpired)(secrets);
60
+ const tokenServiceUrl = (0, token_service_1.getTokenServiceUrl)({
61
+ serverUrl: profile?.tokenServiceUrl || undefined,
62
+ });
63
+ let tokenServiceReachable = null;
64
+ if (options?.probeTokenService !== false) {
65
+ try {
66
+ tokenServiceReachable = await (0, token_service_1.fetchCoordinatorConfig)(tokenServiceUrl);
67
+ }
68
+ catch {
69
+ tokenServiceReachable = false;
70
+ }
71
+ }
72
+ const nextSteps = nextStepsForPhase(tokenServiceReachable === false && phase !== "ready" ? "degraded" : phase);
73
+ const reloadRequired = phase === "ready" &&
74
+ Boolean(profile &&
75
+ (!process.env.NEOZIP_TOKEN_SERVICE_ACCESS_TOKEN ||
76
+ !process.env.NEOZIP_WALLET_PASSKEY));
77
+ return {
78
+ success: true,
79
+ phase: tokenServiceReachable === false && phase !== "ready" ? "degraded" : phase,
80
+ activeProfileId: (0, profile_store_1.getActiveProfileId)(),
81
+ profile: profile
82
+ ? {
83
+ ...profile,
84
+ email: profile.email ? (0, mask_1.maskEmail)(profile.email) : null,
85
+ }
86
+ : null,
87
+ tokenServiceReachable,
88
+ tokenServiceUrl,
89
+ tokenExpiresAt,
90
+ tokenExpired,
91
+ nextSteps,
92
+ reloadRequired,
93
+ };
94
+ }
95
+ //# sourceMappingURL=account-state.js.map
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatAccountStatusText = formatAccountStatusText;
4
+ function formatExpiry(expiresAt) {
5
+ const deltaMs = expiresAt - Date.now();
6
+ const abs = Math.abs(deltaMs);
7
+ const minutes = Math.round(abs / 60000);
8
+ const hours = Math.round(abs / 3600000);
9
+ const days = Math.round(abs / 86400000);
10
+ let rel;
11
+ if (abs < 3600000)
12
+ rel = `${minutes} min`;
13
+ else if (abs < 86400000)
14
+ rel = `${hours} hr`;
15
+ else
16
+ rel = `${days} day${days === 1 ? "" : "s"}`;
17
+ const when = new Date(expiresAt).toISOString();
18
+ return deltaMs >= 0 ? `in ${rel} (${when})` : `${rel} ago (${when})`;
19
+ }
20
+ function formatAccountStatusText(report) {
21
+ const lines = [];
22
+ lines.push("NeoZip account status");
23
+ lines.push(`Phase: ${report.phase}`);
24
+ lines.push(`Token Service: ${report.tokenServiceUrl}`);
25
+ if (report.tokenServiceReachable != null) {
26
+ lines.push(`Token Service reachable: ${report.tokenServiceReachable ? "yes" : "no"}`);
27
+ }
28
+ if (report.tokenExpired) {
29
+ lines.push("Access token: EXPIRED — run neozip connect refresh");
30
+ }
31
+ else if (report.tokenExpiresAt != null) {
32
+ lines.push(`Access token: valid (expires ${formatExpiry(report.tokenExpiresAt)})`);
33
+ }
34
+ if (report.activeProfileId) {
35
+ lines.push(`Active profile: ${report.activeProfileId}`);
36
+ }
37
+ if (report.profile) {
38
+ lines.push(`Label: ${report.profile.label}`);
39
+ if (report.profile.email)
40
+ lines.push(`Email: ${report.profile.email}`);
41
+ if (report.profile.evmAddress) {
42
+ lines.push(`EVM address: ${report.profile.evmAddress}`);
43
+ }
44
+ if (report.profile.walletId != null) {
45
+ lines.push(`Wallet ID: ${report.profile.walletId}`);
46
+ }
47
+ if (report.profile.identityKeyId != null) {
48
+ lines.push(`Identity key ID: ${report.profile.identityKeyId}`);
49
+ }
50
+ if (report.profile.x25519Fingerprint) {
51
+ lines.push(`X25519 fingerprint: ${report.profile.x25519Fingerprint}`);
52
+ }
53
+ }
54
+ if (report.nextSteps.length) {
55
+ lines.push(`Next steps: ${report.nextSteps.join(", ")}`);
56
+ }
57
+ if (report.reloadRequired) {
58
+ lines.push("Reload MCP in Cursor so the active connection credentials are loaded into this session.");
59
+ }
60
+ return lines.join("\n");
61
+ }
62
+ //# sourceMappingURL=format-account-status.js.map
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.provisionWalletIdentityKey = provisionWalletIdentityKey;
4
+ exports.refreshIdentityFromCryptoSelf = refreshIdentityFromCryptoSelf;
5
+ const identity_key_1 = require("../archive/identity-key");
6
+ const crypto_self_1 = require("../archive/crypto-self");
7
+ const identity_wrap_1 = require("./identity-wrap");
8
+ const token_service_1 = require("neozip-blockchain/token-service");
9
+ const wallet_evm_1 = require("./wallet-evm");
10
+ async function provisionWalletIdentityKey(params) {
11
+ const existing = await (0, token_service_1.getIdentityKeyBundle)(params.baseUrl, params.accessToken, params.walletId);
12
+ if (existing.found) {
13
+ return {
14
+ identityKeyId: existing.identityKeyId,
15
+ x25519PublicKey: existing.x25519PublicKey,
16
+ provisioned: false,
17
+ };
18
+ }
19
+ const eoaPriv = (0, wallet_evm_1.rawEoaBytes)(params.evmPrivateKey);
20
+ try {
21
+ const wrap = (0, identity_wrap_1.wrapNewX25519Keypair)({
22
+ rawEoaPrivBytes: eoaPriv,
23
+ evmAddress: params.evmAddress,
24
+ });
25
+ const bundleBody = {
26
+ x25519PublicKeyB64: wrap.x25519PublicKeyB64,
27
+ ...wrap.bundle,
28
+ };
29
+ const challenge = await (0, token_service_1.requestIdentityKeyInitChallenge)(params.baseUrl, params.accessToken, params.walletId, bundleBody);
30
+ const expectedMessage = (0, identity_wrap_1.buildIdentityKeyInitMessage)({
31
+ walletId: params.walletId,
32
+ challengeId: challenge.challengeId,
33
+ evmAddress: params.evmAddress,
34
+ formatVersion: wrap.bundle.wrapFormatVersion,
35
+ x25519PublicKeyB64: wrap.x25519PublicKeyB64,
36
+ wrappedSha256Hex: wrap.wrappedSha256Hex,
37
+ });
38
+ if (challenge.message !== expectedMessage) {
39
+ throw new identity_key_1.IdentityKeyTamperError("Server init message does not match canonical form expected by this client.");
40
+ }
41
+ const evmSignature = await (0, wallet_evm_1.signEvmMessage)(params.evmPrivateKey, challenge.message);
42
+ try {
43
+ const completed = await (0, token_service_1.completeIdentityKeyInit)(params.baseUrl, params.accessToken, params.walletId, bundleBody, challenge.challengeId, evmSignature);
44
+ return {
45
+ identityKeyId: completed.identityKeyId,
46
+ x25519PublicKey: completed.x25519PublicKey,
47
+ provisioned: true,
48
+ };
49
+ }
50
+ catch (err) {
51
+ if (err instanceof token_service_1.IdentityKeyConflictError) {
52
+ const reloaded = await (0, token_service_1.getIdentityKeyBundle)(params.baseUrl, params.accessToken, params.walletId);
53
+ if (reloaded.found) {
54
+ return {
55
+ identityKeyId: reloaded.identityKeyId,
56
+ x25519PublicKey: reloaded.x25519PublicKey,
57
+ provisioned: false,
58
+ };
59
+ }
60
+ }
61
+ throw err;
62
+ }
63
+ }
64
+ finally {
65
+ eoaPriv.fill(0);
66
+ }
67
+ }
68
+ /** Sync profile metadata with live /crypto/self when token is available. */
69
+ async function refreshIdentityFromCryptoSelf(config) {
70
+ const self = await (0, crypto_self_1.fetchCryptoSelf)(config);
71
+ if (!self.ok) {
72
+ return { identityKeyId: null, walletId: null };
73
+ }
74
+ return {
75
+ identityKeyId: self.data.identityKeyId,
76
+ walletId: self.data.walletId,
77
+ };
78
+ }
79
+ //# sourceMappingURL=identity-provision.js.map
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.buildIdentityKeyInitMessage = buildIdentityKeyInitMessage;
37
+ exports.wrapNewX25519Keypair = wrapNewX25519Keypair;
38
+ const crypto = __importStar(require("node:crypto"));
39
+ const wallet_identity_1 = require("../constants/wallet-identity");
40
+ function buildCanonicalAad(params) {
41
+ const lowerAddr = params.evmAddress.toLowerCase();
42
+ const addrBytes = Buffer.from(lowerAddr, "ascii");
43
+ return Buffer.concat([
44
+ addrBytes,
45
+ Buffer.from([wallet_identity_1.WRAP_FORMAT_VERSION]),
46
+ params.x25519SpkiDer,
47
+ ]);
48
+ }
49
+ function deriveKek(rawEoaPrivBytes, saltB64) {
50
+ const salt = Buffer.from(saltB64, "base64");
51
+ if (salt.length !== wallet_identity_1.WRAP_KDF_SALT_LEN) {
52
+ throw new Error(`Wrap KDF salt must be ${wallet_identity_1.WRAP_KDF_SALT_LEN} bytes`);
53
+ }
54
+ return Buffer.from(crypto.hkdfSync("sha256", rawEoaPrivBytes, salt, wallet_identity_1.WRAP_KDF_INFO, 32));
55
+ }
56
+ function sha256Hex(buf) {
57
+ return crypto.createHash("sha256").update(buf).digest("hex");
58
+ }
59
+ function buildIdentityKeyInitMessage(params) {
60
+ return [
61
+ "NeoZip Token Service identity key init",
62
+ `walletId:${params.walletId}`,
63
+ `challenge:${params.challengeId}`,
64
+ `address:${params.evmAddress.toLowerCase()}`,
65
+ `formatVersion:${params.formatVersion}`,
66
+ `x25519:${params.x25519PublicKeyB64}`,
67
+ `wrappedSha256:${params.wrappedSha256Hex}`,
68
+ ].join("\n");
69
+ }
70
+ function wrapNewX25519Keypair(params) {
71
+ const { publicKey, privateKey } = crypto.generateKeyPairSync("x25519");
72
+ const spkiDer = publicKey.export({ type: "spki", format: "der" });
73
+ const pkcs8Der = privateKey.export({ type: "pkcs8", format: "der" });
74
+ const rawPriv = pkcs8Der.subarray(pkcs8Der.length - 32);
75
+ const saltB64 = crypto.randomBytes(wallet_identity_1.WRAP_KDF_SALT_LEN).toString("base64");
76
+ const ivB64 = crypto.randomBytes(wallet_identity_1.WRAP_IV_LEN).toString("base64");
77
+ const aad = buildCanonicalAad({
78
+ evmAddress: params.evmAddress,
79
+ x25519SpkiDer: spkiDer,
80
+ });
81
+ const kek = deriveKek(params.rawEoaPrivBytes, saltB64);
82
+ const iv = Buffer.from(ivB64, "base64");
83
+ const cipher = crypto.createCipheriv("aes-256-gcm", kek, iv);
84
+ cipher.setAAD(aad);
85
+ const ct = Buffer.concat([cipher.update(rawPriv), cipher.final()]);
86
+ const tag = cipher.getAuthTag();
87
+ const wrapped = Buffer.concat([ct, tag]);
88
+ kek.fill(0);
89
+ rawPriv.fill(0);
90
+ pkcs8Der.fill(0);
91
+ const bundle = {
92
+ wrappedPrivateKeyB64: wrapped.toString("base64"),
93
+ wrapFormatVersion: wallet_identity_1.WRAP_FORMAT_VERSION,
94
+ wrapKdfInfo: wallet_identity_1.WRAP_KDF_INFO,
95
+ wrapKdfSaltB64: saltB64,
96
+ wrapAead: wallet_identity_1.WRAP_AEAD,
97
+ wrapIvB64: ivB64,
98
+ wrapAadB64: aad.toString("base64"),
99
+ };
100
+ return {
101
+ bundle,
102
+ x25519PublicKeyB64: spkiDer.toString("base64"),
103
+ wrappedSha256Hex: sha256Hex(wrapped),
104
+ };
105
+ }
106
+ //# sourceMappingURL=identity-wrap.js.map