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