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
@@ -0,0 +1,871 @@
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.promptTokenServiceUrl = promptTokenServiceUrl;
37
+ exports.parseVerificationCodeInput = parseVerificationCodeInput;
38
+ exports.promptVerificationCode = promptVerificationCode;
39
+ exports.promptRecoveryPhraseBackup = promptRecoveryPhraseBackup;
40
+ exports.promptWalletOverwriteConfirm = promptWalletOverwriteConfirm;
41
+ exports.promptWalletChoice = promptWalletChoice;
42
+ exports.promptBrokenStoreReset = promptBrokenStoreReset;
43
+ exports.promptIncompleteAccountAction = promptIncompleteAccountAction;
44
+ exports.promptReadyAccountAction = promptReadyAccountAction;
45
+ exports.promptSwitchConnection = promptSwitchConnection;
46
+ exports.promptDeleteConnection = promptDeleteConnection;
47
+ exports.runInteractiveConnect = runInteractiveConnect;
48
+ const readline = __importStar(require("node:readline/promises"));
49
+ const node_process_1 = require("node:process");
50
+ const account_state_1 = require("../account/account-state");
51
+ const format_account_status_1 = require("../account/format-account-status");
52
+ const wallet_evm_1 = require("../account/wallet-evm");
53
+ const onboarding_1 = require("./onboarding");
54
+ const phone_1 = require("./phone");
55
+ const promote_active_1 = require("./promote-active");
56
+ const phase_1 = require("./phase");
57
+ const origin_1 = require("./origin");
58
+ const store_1 = require("./store");
59
+ const migrate_1 = require("./migrate");
60
+ const wallet_json_migration_1 = require("./wallet-json-migration");
61
+ const coordinator_1 = require("./coordinator");
62
+ const dump_1 = require("./dump");
63
+ const cli_settings_1 = require("./cli-settings");
64
+ const incomplete_setup_1 = require("./incomplete-setup");
65
+ const reset_1 = require("./reset");
66
+ const wallet_setup_1 = require("./wallet-setup");
67
+ function defaultPrompter(rl) {
68
+ return {
69
+ async question(prompt, defaultValue) {
70
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
71
+ const answer = (await rl.question(`${prompt}${suffix}: `)).trim();
72
+ return answer || defaultValue || "";
73
+ },
74
+ };
75
+ }
76
+ function promptWithDefault(prompter, prompt, defaultValue) {
77
+ return prompter.question(prompt, defaultValue);
78
+ }
79
+ async function promptTokenServiceUrl(prompter, options) {
80
+ if (options?.tokenServiceUrl?.trim()) {
81
+ return (0, origin_1.resolveTokenServiceOrigin)(options.tokenServiceUrl);
82
+ }
83
+ console.error(`
84
+ Token Service — choose deployment:
85
+ 1) NeoZip Testnet — ${origin_1.NEOZIP_TOKEN_SERVICE_DEFAULT_URL} (default)
86
+ 2) NeoZip Production — ${origin_1.NEOZIP_TOKEN_SERVICE_PRODUCTION_URL}
87
+ 3) Local Development — ${origin_1.NEOZIP_TOKEN_SERVICE_LOCALHOST}
88
+ 4) Custom URL
89
+ `);
90
+ const choice = await promptWithDefault(prompter, "Choice", (0, origin_1.defaultTokenServiceMenuChoice)());
91
+ switch (choice) {
92
+ case "1":
93
+ case "testnet":
94
+ return (0, origin_1.normalizeTokenServiceOrigin)(origin_1.KNOWN_TOKEN_SERVICE_PRESETS[0].url);
95
+ case "2":
96
+ case "production":
97
+ case "prod":
98
+ return (0, origin_1.normalizeTokenServiceOrigin)(origin_1.KNOWN_TOKEN_SERVICE_PRESETS[1].url);
99
+ case "3":
100
+ case "local":
101
+ return (0, origin_1.normalizeTokenServiceOrigin)(origin_1.KNOWN_TOKEN_SERVICE_PRESETS[2].url);
102
+ case "4":
103
+ case "custom": {
104
+ const url = await promptWithDefault(prompter, "Custom Token Service URL", undefined);
105
+ if (!url)
106
+ throw new Error("Token Service URL is required");
107
+ return (0, origin_1.resolveTokenServiceOrigin)(url);
108
+ }
109
+ default:
110
+ throw new Error(`Invalid choice: ${choice}. Enter 1, 2, 3, or 4.`);
111
+ }
112
+ }
113
+ async function ensureConnectionShell(prompter, options) {
114
+ const phase = (0, phase_1.computeConnectionPhase)();
115
+ if (phase !== "no_profile")
116
+ return;
117
+ const existing = (0, store_1.listConnections)();
118
+ if (existing.length > 0) {
119
+ if (existing.length === 1) {
120
+ (0, store_1.selectConnection)(existing[0].id);
121
+ console.error(`Using connection: ${existing[0].label} (${existing[0].id})`);
122
+ return;
123
+ }
124
+ console.error("Existing connections:");
125
+ existing.forEach((c, i) => {
126
+ console.error(` ${i + 1}) ${c.label} — ${c.email ?? "no email"} (${c.id})`);
127
+ });
128
+ console.error(` ${existing.length + 1}) Create new connection`);
129
+ const pick = await promptWithDefault(prompter, `Select connection (1-${existing.length + 1})`, "1");
130
+ const idx = parseInt(pick, 10);
131
+ if (idx >= 1 && idx <= existing.length) {
132
+ (0, store_1.selectConnection)(existing[idx - 1].id);
133
+ return;
134
+ }
135
+ }
136
+ const tokenServiceUrl = await promptTokenServiceUrl(prompter, options);
137
+ const label = options?.label?.trim() || (0, origin_1.connectionLabelForOrigin)(tokenServiceUrl);
138
+ (0, store_1.createConnection)({ label, tokenServiceUrl });
139
+ console.error(`Connection "${label}" stored under ${(0, store_1.getConnectionDir)()}`);
140
+ }
141
+ async function sendVerificationCode(origin, email, label) {
142
+ console.error(`Sending verification code to ${email}…`);
143
+ const reg = await (0, onboarding_1.connectRegister)({
144
+ email,
145
+ tokenServiceUrl: origin,
146
+ label,
147
+ });
148
+ if (!reg.success) {
149
+ throw new Error(reg.message ?? "Failed to send verification email.");
150
+ }
151
+ console.error(`Verification code sent to ${email}.`);
152
+ }
153
+ function parseVerificationCodeInput(input) {
154
+ const trimmed = input.trim();
155
+ if (!trimmed)
156
+ return null;
157
+ const key = trimmed.toLowerCase();
158
+ if (key === "r" || key === "resend")
159
+ return { kind: "resend" };
160
+ if (key === "c" || key === "change")
161
+ return { kind: "change_email" };
162
+ if (key === "a" || key === "abort")
163
+ return { kind: "abort" };
164
+ return { kind: "code", code: trimmed };
165
+ }
166
+ async function promptVerificationCode(prompter) {
167
+ const answer = await prompter.question("6-digit verification code from email [r=resend, c=change email, a=abort]");
168
+ return parseVerificationCodeInput(answer);
169
+ }
170
+ async function runEmailStep(prompter) {
171
+ if ((0, phase_1.computeConnectionPhase)() !== "email_unverified")
172
+ return;
173
+ const conn = (0, store_1.getActiveConnection)();
174
+ if (!conn)
175
+ throw new Error("No active connection");
176
+ const origin = conn.tokenServiceOrigin;
177
+ const reachable = await (0, coordinator_1.probeTokenServiceReachable)(origin);
178
+ if (!reachable) {
179
+ throw new Error(`Token Service unreachable at ${origin}. Start the server (e.g. local development on port 14789) or choose another deployment.`);
180
+ }
181
+ const defaultEmail = conn.email?.trim() ||
182
+ process.env.NEOZIP_RECIPIENT_EMAIL?.trim() ||
183
+ undefined;
184
+ let email = await promptWithDefault(prompter, "Email for Token Service", defaultEmail);
185
+ if (!email)
186
+ throw new Error("Email is required");
187
+ await sendVerificationCode(origin, email, conn.label);
188
+ while (true) {
189
+ const input = await promptVerificationCode(prompter);
190
+ if (input === null) {
191
+ console.error("Enter the verification code, r to resend, c to change email, or a to abort.");
192
+ continue;
193
+ }
194
+ if (input.kind === "abort") {
195
+ throw new Error("Email verification aborted.");
196
+ }
197
+ if (input.kind === "change_email") {
198
+ email = await promptWithDefault(prompter, "Email for Token Service");
199
+ if (!email) {
200
+ console.error("Email is required.");
201
+ continue;
202
+ }
203
+ await sendVerificationCode(origin, email, conn.label);
204
+ continue;
205
+ }
206
+ if (input.kind === "resend") {
207
+ await sendVerificationCode(origin, email, conn.label);
208
+ continue;
209
+ }
210
+ console.error("Verifying email…");
211
+ try {
212
+ const ver = await (0, onboarding_1.connectVerify)({
213
+ email,
214
+ code: input.code,
215
+ tokenServiceUrl: origin,
216
+ });
217
+ if (!ver.success && ver.phase === "phone_required" && !ver.message?.includes("Email verified")) {
218
+ throw new Error(ver.message ?? "Phone verification required");
219
+ }
220
+ console.error("Email verified.");
221
+ return;
222
+ }
223
+ catch (err) {
224
+ const message = err instanceof Error ? err.message : String(err);
225
+ console.error(message);
226
+ console.error("Try again, enter r to resend, c to change email, or a to abort.");
227
+ }
228
+ }
229
+ }
230
+ async function runPhoneStep(prompter) {
231
+ const conn = (0, store_1.getActiveConnection)();
232
+ if (!conn)
233
+ throw new Error("No active connection");
234
+ if ((0, phone_1.isPhoneVerified)(conn))
235
+ return;
236
+ const coordinator = await (0, coordinator_1.fetchCoordinatorConfigParsed)(conn.tokenServiceOrigin);
237
+ if (!coordinator?.cryptoRequirePhoneVerified)
238
+ return;
239
+ console.error(`
240
+ Phone verification — required by this Token Service deployment.
241
+ Enter your phone in E.164 format (e.g. +14155551234). We will send a 6-digit SMS code.
242
+ `);
243
+ let phone = conn.phoneE164?.trim() || "";
244
+ if (!phone) {
245
+ phone = await promptWithDefault(prompter, "Phone number (E.164)", undefined);
246
+ }
247
+ if (!phone)
248
+ throw new Error("Phone number is required");
249
+ let smsSent = false;
250
+ while (true) {
251
+ if (!smsSent) {
252
+ console.error(`Sending SMS code to ${phone}…`);
253
+ const req = await (0, onboarding_1.connectPhoneRequest)({
254
+ phone,
255
+ tokenServiceUrl: conn.tokenServiceOrigin,
256
+ });
257
+ if (!req.success) {
258
+ throw new Error(req.message ?? "Failed to send SMS code");
259
+ }
260
+ smsSent = true;
261
+ console.error(req.message ?? "SMS code sent.");
262
+ }
263
+ const answer = await prompter.question("6-digit SMS code [r=resend, c=change phone, a=abort]");
264
+ const trimmed = answer.trim().toLowerCase();
265
+ if (trimmed === "a" || trimmed === "abort") {
266
+ throw new Error("Phone verification aborted.");
267
+ }
268
+ if (trimmed === "r" || trimmed === "resend") {
269
+ smsSent = false;
270
+ continue;
271
+ }
272
+ if (trimmed === "c" || trimmed === "change") {
273
+ phone = await promptWithDefault(prompter, "Phone number (E.164)");
274
+ if (!phone) {
275
+ console.error("Phone number is required.");
276
+ continue;
277
+ }
278
+ smsSent = false;
279
+ continue;
280
+ }
281
+ if (!trimmed) {
282
+ console.error("Enter the SMS code, r to resend, c to change phone, or a to abort.");
283
+ continue;
284
+ }
285
+ console.error("Verifying phone…");
286
+ try {
287
+ const ver = await (0, onboarding_1.connectPhoneVerify)({
288
+ phone,
289
+ code: trimmed,
290
+ tokenServiceUrl: conn.tokenServiceOrigin,
291
+ });
292
+ if (!ver.success) {
293
+ console.error(ver.message ?? "Verification failed");
294
+ continue;
295
+ }
296
+ console.error("Phone verified.");
297
+ return;
298
+ }
299
+ catch (err) {
300
+ console.error(err instanceof Error ? err.message : String(err));
301
+ }
302
+ }
303
+ }
304
+ async function promptRecoveryPhraseBackup(prompter, input) {
305
+ console.error(`
306
+ Back up your Data Wallet (same as NeoZip Desktop)
307
+ This is the one and only time we will show this recovery phrase. Write it down and store it
308
+ somewhere safe. NeoZip does not store this phrase on the server.
309
+
310
+ Note: This is your wallet recovery phrase. Connection file encryption uses this machine by
311
+ default unless you set NEOZIP_UNLOCK_PASSPHRASE.
312
+
313
+ Address: ${input.address}
314
+
315
+ Recovery phrase:
316
+ ${input.mnemonic}
317
+ `);
318
+ while (true) {
319
+ const ack = (await prompter.question("Have you written down and stored your recovery phrase? (yes/no)", "no"))
320
+ .trim()
321
+ .toLowerCase();
322
+ if (ack === "yes" || ack === "y") {
323
+ return;
324
+ }
325
+ if (ack === "no" || ack === "n") {
326
+ console.error("You must save your recovery phrase before continuing. You cannot recover this wallet without it.");
327
+ continue;
328
+ }
329
+ console.error('Answer "yes" or "no".');
330
+ }
331
+ }
332
+ async function promptWalletOverwriteConfirm(prompter, serverAddress, newAddress) {
333
+ console.error(`
334
+ WARNING: Token Service wallet on file: ${serverAddress}
335
+ Your choice would use a different wallet: ${newAddress}
336
+ Continuing will replace the server-linked wallet when you finish setup.
337
+ `);
338
+ const answer = (await prompter.question('Type "yes" to continue', "no"))
339
+ .trim()
340
+ .toLowerCase();
341
+ return answer === "yes" || answer === "y";
342
+ }
343
+ async function promptWalletChoice(prompter, options) {
344
+ const serverLinkedAddress = options?.serverLinkedAddress?.trim() || null;
345
+ if (serverLinkedAddress) {
346
+ console.error(`
347
+ This account already has a Data Wallet on Token Service: ${serverLinkedAddress}
348
+ Restore it with your recovery phrase or private key, or create a new wallet (requires confirmation).
349
+ `);
350
+ }
351
+ console.error(`
352
+ Data Wallet — choose one (same options as NeoZip Desktop):
353
+ 1) Create new wallet (generated locally; secrets stay on this device)
354
+ 2) Import recovery phrase (12 or 24 words)
355
+ 3) Import private key (hex, with or without 0x prefix)
356
+ `);
357
+ const defaultChoice = serverLinkedAddress ? "2" : "1";
358
+ const choice = await promptWithDefault(prompter, "Choice", defaultChoice);
359
+ switch (choice) {
360
+ case "1":
361
+ case "create":
362
+ case "new": {
363
+ const evm = (0, wallet_evm_1.generateEvmIdentity)();
364
+ if (!evm.mnemonic) {
365
+ throw new Error("Failed to generate recovery phrase for new Data Wallet");
366
+ }
367
+ if (serverLinkedAddress &&
368
+ evm.address.toLowerCase() !== serverLinkedAddress.toLowerCase()) {
369
+ const ok = await promptWalletOverwriteConfirm(prompter, serverLinkedAddress, evm.address);
370
+ if (!ok)
371
+ throw new Error("Wallet creation cancelled.");
372
+ }
373
+ await promptRecoveryPhraseBackup(prompter, {
374
+ mnemonic: evm.mnemonic,
375
+ address: evm.address,
376
+ });
377
+ console.error(`New Data Wallet: ${evm.address}`);
378
+ return evm;
379
+ }
380
+ case "2":
381
+ case "mnemonic":
382
+ case "phrase": {
383
+ const mnemonic = await promptWithDefault(prompter, "Recovery phrase (space-separated words)");
384
+ if (!mnemonic)
385
+ throw new Error("Recovery phrase is required");
386
+ const evm = (0, wallet_evm_1.evmIdentityFromMnemonic)(mnemonic);
387
+ if (serverLinkedAddress &&
388
+ evm.address.toLowerCase() !== serverLinkedAddress.toLowerCase()) {
389
+ const ok = await promptWalletOverwriteConfirm(prompter, serverLinkedAddress, evm.address);
390
+ if (!ok)
391
+ throw new Error("Wallet import cancelled.");
392
+ }
393
+ console.error(`Imported wallet: ${evm.address}`);
394
+ return evm;
395
+ }
396
+ case "3":
397
+ case "key":
398
+ case "private": {
399
+ const privateKey = await promptWithDefault(prompter, "EVM private key (hex)");
400
+ if (!privateKey)
401
+ throw new Error("Private key is required");
402
+ const evm = (0, wallet_evm_1.evmIdentityFromPrivateKey)(privateKey);
403
+ if (serverLinkedAddress &&
404
+ evm.address.toLowerCase() !== serverLinkedAddress.toLowerCase()) {
405
+ const ok = await promptWalletOverwriteConfirm(prompter, serverLinkedAddress, evm.address);
406
+ if (!ok)
407
+ throw new Error("Wallet import cancelled.");
408
+ }
409
+ console.error(`Imported wallet: ${evm.address}`);
410
+ return evm;
411
+ }
412
+ default:
413
+ throw new Error(`Invalid choice: ${choice}. Enter 1, 2, or 3.`);
414
+ }
415
+ }
416
+ async function runWalletStep(prompter) {
417
+ if ((0, phase_1.computeConnectionPhase)() !== "no_wallet")
418
+ return;
419
+ const conn = (0, store_1.getActiveConnection)();
420
+ if (!conn)
421
+ throw new Error("No active connection");
422
+ let serverLinkedAddress = null;
423
+ const secrets = (0, store_1.readActiveConnectionSecrets)();
424
+ if (secrets?.accessToken) {
425
+ console.error("Checking Token Service for an existing linked wallet…");
426
+ serverLinkedAddress = await (0, wallet_setup_1.fetchServerLinkedWalletAddress)(conn.tokenServiceOrigin, secrets.accessToken);
427
+ }
428
+ const evm = await promptWalletChoice(prompter, { serverLinkedAddress });
429
+ (0, store_1.saveEvmWallet)(conn.id, evm.privateKey, evm.address);
430
+ console.error("Data Wallet saved to encrypted connection file.");
431
+ }
432
+ async function promptBrokenStoreReset(prompter, message) {
433
+ console.error(`\n${message}\n`);
434
+ console.error(` 1) Reset all connection data and start fresh (recommended)
435
+ 2) Exit without changes
436
+ 3) Show account summary
437
+ 4) Show connection JSON (no secrets)
438
+ `);
439
+ while (true) {
440
+ const choice = await promptWithDefault(prompter, "Choice", "1");
441
+ switch (choice) {
442
+ case "1":
443
+ case "reset":
444
+ return "reset";
445
+ case "2":
446
+ case "exit":
447
+ case "quit":
448
+ return "exit";
449
+ case "3":
450
+ case "summary":
451
+ case "accounts":
452
+ return "summary";
453
+ case "4":
454
+ case "dump":
455
+ case "json":
456
+ return "dump";
457
+ default:
458
+ console.error("Invalid choice. Enter 1, 2, 3, or 4.");
459
+ }
460
+ }
461
+ }
462
+ function rollbackFailedNewConnection(previousActiveId) {
463
+ const currentId = (0, store_1.getActiveConnectionId)();
464
+ if (currentId &&
465
+ currentId !== previousActiveId &&
466
+ (0, phase_1.computeConnectionPhaseForId)(currentId) === "email_unverified") {
467
+ const removed = (0, store_1.deleteConnection)(currentId);
468
+ console.error(`Removed incomplete account "${removed.label}".`);
469
+ }
470
+ if (previousActiveId && (0, store_1.readConnectionPublic)(previousActiveId)) {
471
+ const restored = (0, store_1.selectConnection)(previousActiveId);
472
+ console.error(`Restored active account: ${formatConnectionSummary(restored, restored.id)}`);
473
+ return;
474
+ }
475
+ const promoted = (0, promote_active_1.promoteReadyActiveConnection)();
476
+ if (promoted.connectionId && promoted.label) {
477
+ console.error(`Restored active account: ${promoted.label}`);
478
+ }
479
+ }
480
+ async function promptIncompleteAccountAction(prompter, active) {
481
+ const email = active.email ?? "no email yet";
482
+ console.error(`
483
+ Incomplete account setup (${active.label} — ${email} — ${active.tokenServiceOrigin}).
484
+ 1) Continue setup
485
+ 2) Delete this incomplete account
486
+ 3) Switch to a ready account
487
+ 4) Show account summary
488
+ 5) Show connection JSON (no secrets)
489
+ `);
490
+ while (true) {
491
+ const choice = await promptWithDefault(prompter, "Choice", "3");
492
+ switch (choice) {
493
+ case "1":
494
+ case "continue":
495
+ return "continue_setup";
496
+ case "2":
497
+ case "delete":
498
+ return "delete";
499
+ case "3":
500
+ case "switch":
501
+ case "ready":
502
+ return "switch_ready";
503
+ case "4":
504
+ case "summary":
505
+ case "accounts":
506
+ return "summary";
507
+ case "5":
508
+ case "dump":
509
+ case "json":
510
+ return "dump";
511
+ default:
512
+ console.error("Invalid choice. Enter 1, 2, 3, 4, or 5.");
513
+ }
514
+ }
515
+ }
516
+ function formatConnectionSummary(meta, activeId) {
517
+ const active = activeId === meta.id ? " (active)" : "";
518
+ const email = meta.email ?? "no email";
519
+ return `${meta.label} — ${email} — ${meta.tokenServiceOrigin}${active}`;
520
+ }
521
+ async function promptReadyAccountAction(prompter, active) {
522
+ const all = (0, store_1.listConnections)();
523
+ const switchLine = all.length > 1 ? `\n 5) Switch active account (${all.length} saved)` : "";
524
+ const summaryChoice = all.length > 1 ? "6" : "5";
525
+ const dumpChoice = all.length > 1 ? "7" : "6";
526
+ console.error(`
527
+ Account is ready. What would you like to do?
528
+ 1) Done — use current account (default)
529
+ 2) Add another account on ${active.tokenServiceOrigin}
530
+ 3) Add another account on a different Token Service
531
+ 4) Delete an account${switchLine}
532
+ ${summaryChoice}) Show account summary
533
+ ${dumpChoice}) Show connection JSON (no secrets)
534
+ `);
535
+ while (true) {
536
+ const choice = await promptWithDefault(prompter, "Choice", "1");
537
+ switch (choice) {
538
+ case "1":
539
+ case "done":
540
+ return "done";
541
+ case "2":
542
+ case "add":
543
+ case "same":
544
+ return "add_same";
545
+ case "3":
546
+ case "other":
547
+ return "add_other";
548
+ case "4":
549
+ case "delete":
550
+ return "delete";
551
+ case "5":
552
+ if (all.length > 1)
553
+ return "switch";
554
+ return "summary";
555
+ case "6":
556
+ if (all.length > 1)
557
+ return "summary";
558
+ return "dump";
559
+ case "7":
560
+ case "dump":
561
+ case "json":
562
+ case "show":
563
+ if (all.length > 1)
564
+ return "dump";
565
+ break;
566
+ case "summary":
567
+ case "accounts":
568
+ return "summary";
569
+ default:
570
+ break;
571
+ }
572
+ const valid = all.length > 1 ? "1, 2, 3, 4, 5, 6, or 7" : "1, 2, 3, 4, 5, or 6";
573
+ console.error(`Invalid choice. Enter ${valid}.`);
574
+ }
575
+ }
576
+ async function promptSwitchConnection(prompter) {
577
+ const connections = (0, store_1.listConnections)();
578
+ if (connections.length <= 1) {
579
+ console.error("Only one account is saved.");
580
+ return;
581
+ }
582
+ console.error("\nSaved accounts:");
583
+ connections.forEach((conn, index) => {
584
+ console.error(` ${index + 1}) ${formatConnectionSummary(conn, (0, store_1.getActiveConnectionId)())}`);
585
+ });
586
+ const pick = await promptWithDefault(prompter, `Switch active account (1-${connections.length})`, "1");
587
+ const idx = parseInt(pick, 10);
588
+ if (idx < 1 || idx > connections.length) {
589
+ throw new Error(`Invalid selection: ${pick}`);
590
+ }
591
+ const selected = (0, store_1.selectConnection)(connections[idx - 1].id);
592
+ console.error(`Active account: ${formatConnectionSummary(selected, selected.id)}`);
593
+ }
594
+ async function promptDeleteConnection(prompter) {
595
+ const connections = (0, store_1.listConnections)();
596
+ if (connections.length === 0) {
597
+ console.error("No accounts to delete.");
598
+ return;
599
+ }
600
+ console.error("\nSaved accounts:");
601
+ connections.forEach((conn, index) => {
602
+ console.error(` ${index + 1}) ${formatConnectionSummary(conn, (0, store_1.getActiveConnectionId)())}`);
603
+ });
604
+ const pick = await promptWithDefault(prompter, `Delete which account (1-${connections.length}, or cancel)`, "cancel");
605
+ if (pick === "cancel" || pick === "") {
606
+ console.error("Delete cancelled.");
607
+ return;
608
+ }
609
+ const idx = parseInt(pick, 10);
610
+ if (idx < 1 || idx > connections.length) {
611
+ throw new Error(`Invalid selection: ${pick}`);
612
+ }
613
+ const target = connections[idx - 1];
614
+ const confirm = (await promptWithDefault(prompter, `Delete "${target.label}" (${target.email ?? target.id})? Type yes to confirm`, "no"))
615
+ .trim()
616
+ .toLowerCase();
617
+ if (confirm !== "yes") {
618
+ console.error("Delete cancelled.");
619
+ return;
620
+ }
621
+ const deleted = (0, store_1.deleteConnection)(target.id);
622
+ console.error(`Deleted account "${deleted.label}".`);
623
+ if (deleted.newActiveConnectionId) {
624
+ const next = (0, store_1.readConnectionPublic)(deleted.newActiveConnectionId);
625
+ if (next) {
626
+ console.error(`Active account is now: ${formatConnectionSummary(next, next.id)}`);
627
+ }
628
+ }
629
+ else {
630
+ console.error("No accounts remain.");
631
+ }
632
+ }
633
+ async function runNewConnectionSetup(prompter, options) {
634
+ await ensureConnectionShell(prompter, options);
635
+ await runEmailStep(prompter);
636
+ await runPhoneStep(prompter);
637
+ await runWalletStep(prompter);
638
+ return runFinishStep(options?.tokenServiceUrl ?? (0, store_1.getActiveConnection)()?.tokenServiceOrigin, prompter);
639
+ }
640
+ async function runAddConnectionOnService(prompter, tokenServiceUrl) {
641
+ const label = (0, origin_1.connectionLabelForOrigin)(tokenServiceUrl);
642
+ (0, store_1.createConnection)({ label, tokenServiceUrl });
643
+ console.error(`New account on ${tokenServiceUrl}`);
644
+ await runEmailStep(prompter);
645
+ await runPhoneStep(prompter);
646
+ await runWalletStep(prompter);
647
+ return runFinishStep(tokenServiceUrl, prompter);
648
+ }
649
+ async function logFinishMeta() {
650
+ const meta = (0, store_1.getActiveConnection)();
651
+ if (meta?.walletId != null && meta.linkId != null) {
652
+ console.error(`Wallet linked (walletId=${meta.walletId})`);
653
+ }
654
+ if (meta?.identityKeyId != null) {
655
+ console.error(`Identity ready (identityKeyId=${meta.identityKeyId})`);
656
+ }
657
+ }
658
+ function readyResult(origin, message) {
659
+ return {
660
+ success: true,
661
+ phase: "ready",
662
+ connectionId: (0, store_1.getActiveConnection)()?.id ?? null,
663
+ tokenServiceOrigin: (0, store_1.getActiveConnection)()?.tokenServiceOrigin ?? origin,
664
+ message,
665
+ };
666
+ }
667
+ async function runFinishStep(tokenServiceUrl, prompter) {
668
+ const phase = (0, phase_1.computeConnectionPhase)();
669
+ if (phase === "wallet_unlinked") {
670
+ console.error("Linking wallet on Token Service (signed challenges)…");
671
+ }
672
+ if (phase === "identity_missing") {
673
+ console.error("Provisioning X25519 identity key (wallet-encrypted)…");
674
+ }
675
+ const result = phase === "ready"
676
+ ? await (0, onboarding_1.connectFinish)({ tokenServiceUrl })
677
+ : await (0, onboarding_1.connectFinish)({ tokenServiceUrl });
678
+ if (result.phase === "ready" && prompter) {
679
+ await (0, cli_settings_1.promptDefaultNetworkAfterReady)(prompter);
680
+ }
681
+ return result;
682
+ }
683
+ /** Interactive wizard — desktop-equivalent onboarding (email → wallet → link → identity). */
684
+ async function runInteractiveConnect(options) {
685
+ if ((0, migrate_1.hasLegacyMcpProfiles)()) {
686
+ const migrated = (0, migrate_1.migrateFromMcpProfileStore)();
687
+ if (migrated.migrated > 0) {
688
+ console.error(`Migrated ${migrated.migrated} legacy profile(s) to ${migrated.connectionDir}.`);
689
+ }
690
+ }
691
+ if ((0, wallet_json_migration_1.walletJsonExists)()) {
692
+ const wallet = (0, wallet_json_migration_1.migrateFromWalletJson)();
693
+ for (const msg of wallet.messages) {
694
+ console.error(msg);
695
+ }
696
+ }
697
+ const origin = (0, origin_1.resolveTokenServiceOrigin)(options?.tokenServiceUrl);
698
+ const ownsRl = !options?.prompter;
699
+ const rl = ownsRl ? readline.createInterface({ input: node_process_1.stdin, output: node_process_1.stdout }) : null;
700
+ const prompter = options?.prompter ?? defaultPrompter(rl);
701
+ try {
702
+ const autoRecover = (0, incomplete_setup_1.tryAutoRecoverConnectionStore)();
703
+ if (autoRecover.recovered && autoRecover.message) {
704
+ console.error(autoRecover.message);
705
+ }
706
+ let unrecoverable = (0, incomplete_setup_1.diagnoseUnrecoverableStore)();
707
+ while (unrecoverable) {
708
+ const action = await promptBrokenStoreReset(prompter, (0, incomplete_setup_1.formatUnrecoverableStoreMessage)(unrecoverable));
709
+ if (action === "exit") {
710
+ return {
711
+ success: false,
712
+ phase: (0, phase_1.computeConnectionPhase)(),
713
+ connectionId: (0, store_1.getActiveConnectionId)(),
714
+ tokenServiceOrigin: origin,
715
+ message: "Exited without resetting connection data.",
716
+ };
717
+ }
718
+ if (action === "dump") {
719
+ console.log((0, dump_1.formatConnectionDump)((0, dump_1.buildConnectionDump)()));
720
+ unrecoverable = (0, incomplete_setup_1.diagnoseUnrecoverableStore)();
721
+ continue;
722
+ }
723
+ if (action === "summary") {
724
+ console.error(`\n${(0, dump_1.formatConnectionsSummaryText)()}\n`);
725
+ unrecoverable = (0, incomplete_setup_1.diagnoseUnrecoverableStore)();
726
+ continue;
727
+ }
728
+ const reset = (0, reset_1.resetConnectionStore)();
729
+ console.error(reset.removedConnection
730
+ ? "Connection store reset. Starting fresh setup.\n"
731
+ : "Nothing to reset.\n");
732
+ break;
733
+ }
734
+ while (true) {
735
+ const phase = (0, phase_1.computeConnectionPhase)();
736
+ if (phase === "ready") {
737
+ const active = (0, store_1.getActiveConnection)();
738
+ if (!active) {
739
+ throw new Error("Connection phase is ready but no active connection was found.");
740
+ }
741
+ const report = await (0, account_state_1.buildAccountStatusReport)({ probeTokenService: true });
742
+ console.error((0, format_account_status_1.formatAccountStatusText)(report));
743
+ const action = await promptReadyAccountAction(prompter, active);
744
+ if (action === "done") {
745
+ console.error("\nReload MCP in Cursor Settings if you changed the active account.");
746
+ return readyResult(origin, "Connection ready.");
747
+ }
748
+ if (action === "add_same") {
749
+ const previousActiveId = active.id;
750
+ try {
751
+ const finish = await runAddConnectionOnService(prompter, active.tokenServiceOrigin);
752
+ if (!finish.success) {
753
+ rollbackFailedNewConnection(previousActiveId);
754
+ console.error(finish.message ?? "Setup failed.");
755
+ continue;
756
+ }
757
+ await logFinishMeta();
758
+ }
759
+ catch (err) {
760
+ rollbackFailedNewConnection(previousActiveId);
761
+ console.error(err instanceof Error ? err.message : String(err));
762
+ console.error("Setup paused. Start the Token Service and try again, or delete the incomplete account from the menu.");
763
+ }
764
+ continue;
765
+ }
766
+ if (action === "add_other") {
767
+ const previousActiveId = active.id;
768
+ try {
769
+ const tokenServiceUrl = await promptTokenServiceUrl(prompter, options);
770
+ const finish = await runAddConnectionOnService(prompter, tokenServiceUrl);
771
+ if (!finish.success) {
772
+ rollbackFailedNewConnection(previousActiveId);
773
+ console.error(finish.message ?? "Setup failed.");
774
+ continue;
775
+ }
776
+ await logFinishMeta();
777
+ }
778
+ catch (err) {
779
+ rollbackFailedNewConnection(previousActiveId);
780
+ console.error(err instanceof Error ? err.message : String(err));
781
+ console.error("Setup paused. Start the Token Service and try again, or delete the incomplete account from the menu.");
782
+ }
783
+ continue;
784
+ }
785
+ if (action === "delete") {
786
+ await promptDeleteConnection(prompter);
787
+ continue;
788
+ }
789
+ if (action === "switch") {
790
+ await promptSwitchConnection(prompter);
791
+ continue;
792
+ }
793
+ if (action === "summary") {
794
+ console.error(`\n${(0, dump_1.formatConnectionsSummaryText)()}\n`);
795
+ continue;
796
+ }
797
+ if (action === "dump") {
798
+ console.log((0, dump_1.formatConnectionDump)((0, dump_1.buildConnectionDump)()));
799
+ continue;
800
+ }
801
+ }
802
+ if ((0, phase_1.listReadyConnectionIds)().length > 0) {
803
+ const incomplete = (0, store_1.getActiveConnection)();
804
+ if (!incomplete) {
805
+ (0, promote_active_1.promoteReadyActiveConnection)();
806
+ continue;
807
+ }
808
+ const incompleteAction = await promptIncompleteAccountAction(prompter, incomplete);
809
+ if (incompleteAction === "continue_setup") {
810
+ try {
811
+ await runEmailStep(prompter);
812
+ await runPhoneStep(prompter);
813
+ await runWalletStep(prompter);
814
+ const finish = await runFinishStep(incomplete.tokenServiceOrigin, prompter);
815
+ if (!finish.success) {
816
+ console.error(finish.message ?? "Setup failed.");
817
+ }
818
+ else {
819
+ await logFinishMeta();
820
+ }
821
+ }
822
+ catch (err) {
823
+ console.error(err instanceof Error ? err.message : String(err));
824
+ }
825
+ continue;
826
+ }
827
+ if (incompleteAction === "delete") {
828
+ await promptDeleteConnection(prompter);
829
+ (0, promote_active_1.promoteReadyActiveConnection)();
830
+ continue;
831
+ }
832
+ if (incompleteAction === "switch_ready") {
833
+ const promoted = (0, promote_active_1.promoteReadyActiveConnection)();
834
+ if (promoted.label) {
835
+ console.error(`Active account: ${promoted.label}`);
836
+ }
837
+ continue;
838
+ }
839
+ if (incompleteAction === "summary") {
840
+ console.error(`\n${(0, dump_1.formatConnectionsSummaryText)()}\n`);
841
+ continue;
842
+ }
843
+ if (incompleteAction === "dump") {
844
+ console.log((0, dump_1.formatConnectionDump)((0, dump_1.buildConnectionDump)()));
845
+ continue;
846
+ }
847
+ }
848
+ console.error("NeoZip Connect — interactive setup");
849
+ console.error("Complete this once in a terminal before starting the MCP server.\n");
850
+ try {
851
+ const finish = await runNewConnectionSetup(prompter, options);
852
+ if (!finish.success) {
853
+ return finish;
854
+ }
855
+ await logFinishMeta();
856
+ }
857
+ catch (err) {
858
+ console.error(err instanceof Error ? err.message : String(err));
859
+ if ((0, phase_1.listReadyConnectionIds)().length > 0) {
860
+ (0, promote_active_1.promoteReadyActiveConnection)();
861
+ continue;
862
+ }
863
+ throw err;
864
+ }
865
+ }
866
+ }
867
+ finally {
868
+ rl?.close();
869
+ }
870
+ }
871
+ //# sourceMappingURL=interactive.js.map