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,74 @@
|
|
|
1
|
+
import * as os from "os";
|
|
2
|
+
import { applyActiveProfileToEnv } from "../account/profile-store.js";
|
|
3
|
+
import { readConnectionCredentials } from "../connection/credentials.js";
|
|
4
|
+
import { promoteReadyActiveConnection } from "../connection/promote-active.js";
|
|
5
|
+
import { isAutoCapabilitiesMode, resolveCapabilities, } from "./capabilities.js";
|
|
6
|
+
function parseSandboxPaths(raw) {
|
|
7
|
+
const defaults = [process.cwd(), os.homedir()];
|
|
8
|
+
if (!raw)
|
|
9
|
+
return defaults;
|
|
10
|
+
const custom = raw
|
|
11
|
+
.split(",")
|
|
12
|
+
.map((s) => s.trim())
|
|
13
|
+
.filter(Boolean);
|
|
14
|
+
return custom.length > 0 ? custom : defaults;
|
|
15
|
+
}
|
|
16
|
+
export function loadConfig() {
|
|
17
|
+
// Merge into process.env for subprocess compatibility; config reads connection store directly.
|
|
18
|
+
applyActiveProfileToEnv();
|
|
19
|
+
const rawCaps = process.env.NEOZIP_MCP_CAPABILITIES;
|
|
20
|
+
let creds = readConnectionCredentials();
|
|
21
|
+
if (isAutoCapabilitiesMode(rawCaps)) {
|
|
22
|
+
promoteReadyActiveConnection({
|
|
23
|
+
preferredTokenServiceOrigin: creds.tokenServiceUrl ?? undefined,
|
|
24
|
+
});
|
|
25
|
+
creds = readConnectionCredentials();
|
|
26
|
+
}
|
|
27
|
+
const { capabilities, mode: capabilitiesMode } = resolveCapabilities(rawCaps);
|
|
28
|
+
let walletKeySource = null;
|
|
29
|
+
let walletKey = null;
|
|
30
|
+
if (creds.walletKey) {
|
|
31
|
+
walletKey = creds.walletKey;
|
|
32
|
+
walletKeySource = "mcp-profile";
|
|
33
|
+
}
|
|
34
|
+
let networkSource = "default";
|
|
35
|
+
let network = "base-sepolia";
|
|
36
|
+
if (process.env.NEOZIP_NETWORK) {
|
|
37
|
+
networkSource = "env";
|
|
38
|
+
network = process.env.NEOZIP_NETWORK;
|
|
39
|
+
}
|
|
40
|
+
let recipientEmailSource = null;
|
|
41
|
+
let recipientEmail = null;
|
|
42
|
+
if (creds.recipientEmail) {
|
|
43
|
+
recipientEmail = creds.recipientEmail;
|
|
44
|
+
recipientEmailSource = "mcp-profile";
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
capabilities,
|
|
48
|
+
capabilitiesMode,
|
|
49
|
+
sandboxPaths: parseSandboxPaths(process.env.NEOZIP_MCP_SANDBOX_PATHS),
|
|
50
|
+
rateLimit: parseInt(process.env.NEOZIP_MCP_RATE_LIMIT || "60", 10),
|
|
51
|
+
blockchainRateLimit: parseInt(process.env.NEOZIP_MCP_BLOCKCHAIN_RATE_LIMIT || "10", 10),
|
|
52
|
+
maxFileSize: parseInt(process.env.NEOZIP_MCP_MAX_FILE_SIZE || "524288000", 10),
|
|
53
|
+
maxEntries: parseInt(process.env.NEOZIP_MCP_MAX_ENTRIES || "10000", 10),
|
|
54
|
+
timeoutSeconds: parseInt(process.env.NEOZIP_MCP_TIMEOUT || "300", 10),
|
|
55
|
+
defaultMaxChars: parseInt(process.env.NEOZIP_MCP_DEFAULT_MAX_CHARS || "10000", 10),
|
|
56
|
+
maxReadChars: parseInt(process.env.NEOZIP_MCP_MAX_READ_CHARS || "100000", 10),
|
|
57
|
+
grepMaxMatches: parseInt(process.env.NEOZIP_MCP_GREP_MAX_MATCHES || "100", 10),
|
|
58
|
+
grepMaxSnippetChars: parseInt(process.env.NEOZIP_MCP_GREP_MAX_SNIPPET_CHARS || "200", 10),
|
|
59
|
+
grepMaxFileBytes: parseInt(process.env.NEOZIP_MCP_GREP_MAX_FILE_BYTES || "1048576", 10),
|
|
60
|
+
apiKey: process.env.NEOZIP_MCP_API_KEY || null,
|
|
61
|
+
walletKey,
|
|
62
|
+
network,
|
|
63
|
+
tokenServiceUrl: creds.tokenServiceUrl,
|
|
64
|
+
recipientEmail,
|
|
65
|
+
identityPrivateKeyHex: process.env.NEOZIP_IDENTITY_PRIVATE_KEY_HEX?.trim() || null,
|
|
66
|
+
tokenServiceAccessToken: creds.tokenServiceAccessToken,
|
|
67
|
+
debug: process.env.NEOZIP_MCP_DEBUG === "true",
|
|
68
|
+
walletKeySource,
|
|
69
|
+
networkSource,
|
|
70
|
+
recipientEmailSource,
|
|
71
|
+
mcpProfileActiveId: creds.connectionId,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./load-env.js";
|
|
3
|
+
import { connectFinish, connectDelete, connectLogout, connectMigrate, connectPhoneRequest, connectPhoneVerify, connectRegister, connectRename, connectSelect, connectSetup, connectStatus, connectVerify, connectWalletCreate, connectWalletImportKey, connectWalletImportMnemonic, } from "./connection/onboarding.js";
|
|
4
|
+
import { buildConnectionDump, formatConnectionDump } from "./connection/dump.js";
|
|
5
|
+
import { resetConnectionStore } from "./connection/reset.js";
|
|
6
|
+
import { runInteractiveConnect } from "./connection/interactive.js";
|
|
7
|
+
import { formatVerboseStatusText } from "./connection/status-report.js";
|
|
8
|
+
function parseArgs(argv) {
|
|
9
|
+
const args = argv.slice(2);
|
|
10
|
+
const flags = { json: false };
|
|
11
|
+
const positional = [];
|
|
12
|
+
let command = null;
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
if (arg === "--json") {
|
|
16
|
+
flags.json = true;
|
|
17
|
+
}
|
|
18
|
+
else if (arg === "--token-service-url" && args[i + 1]) {
|
|
19
|
+
flags.tokenServiceUrl = args[++i];
|
|
20
|
+
}
|
|
21
|
+
else if (arg === "--label" && args[i + 1]) {
|
|
22
|
+
flags.label = args[++i];
|
|
23
|
+
}
|
|
24
|
+
else if (arg === "--email" && args[i + 1]) {
|
|
25
|
+
flags.email = args[++i];
|
|
26
|
+
}
|
|
27
|
+
else if (arg === "--code" && args[i + 1]) {
|
|
28
|
+
flags.code = args[++i];
|
|
29
|
+
}
|
|
30
|
+
else if (arg === "--phone" && args[i + 1]) {
|
|
31
|
+
flags.phone = args[++i];
|
|
32
|
+
}
|
|
33
|
+
else if (arg === "--mnemonic" && args[i + 1]) {
|
|
34
|
+
flags.mnemonic = args[++i];
|
|
35
|
+
}
|
|
36
|
+
else if (arg === "--private-key" && args[i + 1]) {
|
|
37
|
+
flags.privateKey = args[++i];
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--ack-backup") {
|
|
40
|
+
flags.ackBackup = true;
|
|
41
|
+
}
|
|
42
|
+
else if (arg === "--create-wallet") {
|
|
43
|
+
flags.createWallet = true;
|
|
44
|
+
}
|
|
45
|
+
else if (arg === "--force") {
|
|
46
|
+
flags.force = true;
|
|
47
|
+
}
|
|
48
|
+
else if (arg === "--verbose" || arg === "-v") {
|
|
49
|
+
flags.verbose = true;
|
|
50
|
+
}
|
|
51
|
+
else if (arg === "--resend") {
|
|
52
|
+
flags.resend = true;
|
|
53
|
+
}
|
|
54
|
+
else if (arg === "--magic-token" && args[i + 1]) {
|
|
55
|
+
flags.magicToken = args[++i];
|
|
56
|
+
}
|
|
57
|
+
else if (arg === "--connection-id" && args[i + 1]) {
|
|
58
|
+
flags.connectionId = args[++i];
|
|
59
|
+
}
|
|
60
|
+
else if (arg === "--help" || arg === "-h") {
|
|
61
|
+
command = "help";
|
|
62
|
+
}
|
|
63
|
+
else if (arg.startsWith("-")) {
|
|
64
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
65
|
+
}
|
|
66
|
+
else if (command === null) {
|
|
67
|
+
command = arg;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
positional.push(arg);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { command: command ?? "interactive", flags, positional };
|
|
74
|
+
}
|
|
75
|
+
function printResult(res, json) {
|
|
76
|
+
if (json) {
|
|
77
|
+
console.log(JSON.stringify(res, null, 2));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const lines = [`Phase: ${res.phase}`];
|
|
81
|
+
if (res.tokenServiceOrigin)
|
|
82
|
+
lines.push(`Token Service: ${res.tokenServiceOrigin}`);
|
|
83
|
+
if (res.connectionId)
|
|
84
|
+
lines.push(`Connection: ${res.connectionId}`);
|
|
85
|
+
if (res.message)
|
|
86
|
+
lines.push(res.message);
|
|
87
|
+
if (res.nextCommand)
|
|
88
|
+
lines.push(`Next: ${res.nextCommand}`);
|
|
89
|
+
if (res.accounts?.length) {
|
|
90
|
+
lines.push("");
|
|
91
|
+
lines.push(formatVerboseStatusText(res.accounts));
|
|
92
|
+
}
|
|
93
|
+
console.log(lines.join("\n"));
|
|
94
|
+
}
|
|
95
|
+
function usage() {
|
|
96
|
+
console.error(`NeoZip Connect — Token Service account setup
|
|
97
|
+
|
|
98
|
+
Usage:
|
|
99
|
+
neozip-connect Interactive wizard (default)
|
|
100
|
+
neozip-connect wizard Same as running with no subcommand
|
|
101
|
+
neozip-connect status [--verbose] [--json] [--token-service-url URL]
|
|
102
|
+
neozip-connect select --connection-id ID [--json]
|
|
103
|
+
neozip-connect rename --label NAME [--connection-id ID] [--json]
|
|
104
|
+
neozip-connect dump [--connection-id ID] Print manifest + public.json (no secrets)
|
|
105
|
+
neozip-connect register --email EMAIL [--resend] [--json] [--token-service-url URL] [--label NAME]
|
|
106
|
+
neozip-connect verify --email EMAIL --code CODE [--json] [--token-service-url URL]
|
|
107
|
+
neozip-connect verify --magic-token LINK_OR_TOKEN [--email EMAIL] [--json]
|
|
108
|
+
neozip-connect phone request --phone +14155551234 [--json] [--token-service-url URL]
|
|
109
|
+
neozip-connect phone verify --phone +14155551234 --code CODE [--json] [--token-service-url URL]
|
|
110
|
+
neozip-connect wallet create --ack-backup [--json] [--token-service-url URL]
|
|
111
|
+
neozip-connect wallet import-mnemonic --mnemonic "word1 word2 ..." [--force] [--json]
|
|
112
|
+
neozip-connect wallet import-key --private-key 0x... [--force] [--json]
|
|
113
|
+
neozip-connect finish [--create-wallet] [--json] [--token-service-url URL]
|
|
114
|
+
neozip-connect setup --email EMAIL --code CODE [--json] [--token-service-url URL]
|
|
115
|
+
neozip-connect logout [--json]
|
|
116
|
+
neozip-connect delete [--json] [--connection-id ID]
|
|
117
|
+
neozip-connect migrate [--json]
|
|
118
|
+
neozip-connect reset [--force] Remove connection data (re-run setup)
|
|
119
|
+
|
|
120
|
+
Environment:
|
|
121
|
+
NEOZIP_UNLOCK_PASSPHRASE — optional custom passphrase (default: machine-local key)
|
|
122
|
+
TOKEN_SERVICE_URL — default Token Service base URL
|
|
123
|
+
`);
|
|
124
|
+
}
|
|
125
|
+
async function main() {
|
|
126
|
+
const { command, flags, positional } = parseArgs(process.argv);
|
|
127
|
+
const json = Boolean(flags.json);
|
|
128
|
+
const tokenServiceUrl = typeof flags.tokenServiceUrl === "string" ? flags.tokenServiceUrl : undefined;
|
|
129
|
+
const label = typeof flags.label === "string" ? flags.label : undefined;
|
|
130
|
+
let res;
|
|
131
|
+
switch (command) {
|
|
132
|
+
case "interactive":
|
|
133
|
+
case "wizard":
|
|
134
|
+
if (json) {
|
|
135
|
+
throw new Error("Interactive setup requires a TTY; omit --json or use subcommands (register, verify, finish).");
|
|
136
|
+
}
|
|
137
|
+
if (!process.stdin.isTTY) {
|
|
138
|
+
throw new Error("Interactive setup requires a TTY. Use subcommands with --json for scripted setup.");
|
|
139
|
+
}
|
|
140
|
+
res = await runInteractiveConnect({ tokenServiceUrl, label });
|
|
141
|
+
if (!res.success) {
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
case "status":
|
|
146
|
+
res = await connectStatus({
|
|
147
|
+
tokenServiceUrl,
|
|
148
|
+
autoMigrate: true,
|
|
149
|
+
verbose: Boolean(flags.verbose),
|
|
150
|
+
});
|
|
151
|
+
break;
|
|
152
|
+
case "select": {
|
|
153
|
+
const connectionId = String(flags.connectionId || "");
|
|
154
|
+
if (!connectionId)
|
|
155
|
+
throw new Error("--connection-id is required");
|
|
156
|
+
res = connectSelect({ connectionId });
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case "rename": {
|
|
160
|
+
const renameLabel = String(flags.label || "");
|
|
161
|
+
if (!renameLabel)
|
|
162
|
+
throw new Error("--label is required");
|
|
163
|
+
const connectionId = typeof flags.connectionId === "string" ? flags.connectionId : undefined;
|
|
164
|
+
res = connectRename({ label: renameLabel, connectionId });
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case "dump": {
|
|
168
|
+
const connectionId = typeof flags.connectionId === "string" ? flags.connectionId : undefined;
|
|
169
|
+
const snapshot = buildConnectionDump({ connectionId });
|
|
170
|
+
console.log(formatConnectionDump(snapshot));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
case "register": {
|
|
174
|
+
const email = String(flags.email || "");
|
|
175
|
+
if (!email)
|
|
176
|
+
throw new Error("--email is required");
|
|
177
|
+
res = await connectRegister({
|
|
178
|
+
email,
|
|
179
|
+
tokenServiceUrl,
|
|
180
|
+
label,
|
|
181
|
+
resend: Boolean(flags.resend),
|
|
182
|
+
});
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
case "verify": {
|
|
186
|
+
const email = typeof flags.email === "string" ? flags.email : undefined;
|
|
187
|
+
const code = typeof flags.code === "string" ? flags.code : undefined;
|
|
188
|
+
const magicToken = typeof flags.magicToken === "string" ? flags.magicToken : undefined;
|
|
189
|
+
if (!magicToken && (!email || !code)) {
|
|
190
|
+
throw new Error("Provide --email and --code, or --magic-token (optionally with --email)");
|
|
191
|
+
}
|
|
192
|
+
res = await connectVerify({ email, code, magicToken, tokenServiceUrl });
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case "phone": {
|
|
196
|
+
const action = positional[0];
|
|
197
|
+
const phone = String(flags.phone || "");
|
|
198
|
+
if (action === "request") {
|
|
199
|
+
if (!phone)
|
|
200
|
+
throw new Error("--phone is required");
|
|
201
|
+
res = await connectPhoneRequest({ phone, tokenServiceUrl });
|
|
202
|
+
}
|
|
203
|
+
else if (action === "verify") {
|
|
204
|
+
const code = String(flags.code || "");
|
|
205
|
+
if (!phone || !code)
|
|
206
|
+
throw new Error("--phone and --code are required");
|
|
207
|
+
res = await connectPhoneVerify({ phone, code, tokenServiceUrl });
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
throw new Error("Usage: neozip-connect phone request|verify ...");
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
case "wallet": {
|
|
215
|
+
const action = positional[0];
|
|
216
|
+
if (action === "create") {
|
|
217
|
+
res = await connectWalletCreate({
|
|
218
|
+
ackBackup: Boolean(flags.ackBackup),
|
|
219
|
+
tokenServiceUrl,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
else if (action === "import-mnemonic") {
|
|
223
|
+
const mnemonic = String(flags.mnemonic || "");
|
|
224
|
+
if (!mnemonic)
|
|
225
|
+
throw new Error("--mnemonic is required");
|
|
226
|
+
res = await connectWalletImportMnemonic({
|
|
227
|
+
mnemonic,
|
|
228
|
+
force: Boolean(flags.force),
|
|
229
|
+
tokenServiceUrl,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else if (action === "import-key") {
|
|
233
|
+
const privateKey = String(flags.privateKey || "");
|
|
234
|
+
if (!privateKey)
|
|
235
|
+
throw new Error("--private-key is required");
|
|
236
|
+
res = await connectWalletImportKey({
|
|
237
|
+
privateKey,
|
|
238
|
+
force: Boolean(flags.force),
|
|
239
|
+
tokenServiceUrl,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
throw new Error("Usage: neozip-connect wallet create|import-mnemonic|import-key ...");
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case "finish":
|
|
248
|
+
res = await connectFinish({
|
|
249
|
+
tokenServiceUrl,
|
|
250
|
+
createWallet: Boolean(flags.createWallet),
|
|
251
|
+
});
|
|
252
|
+
break;
|
|
253
|
+
case "setup": {
|
|
254
|
+
const email = String(flags.email || "");
|
|
255
|
+
const code = String(flags.code || "");
|
|
256
|
+
if (!email || !code)
|
|
257
|
+
throw new Error("--email and --code are required");
|
|
258
|
+
res = await connectSetup({ email, code, tokenServiceUrl, label });
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
case "logout":
|
|
262
|
+
res = connectLogout();
|
|
263
|
+
break;
|
|
264
|
+
case "delete": {
|
|
265
|
+
const connectionId = typeof flags.connectionId === "string" ? flags.connectionId : undefined;
|
|
266
|
+
res = connectDelete({ connectionId });
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
case "migrate":
|
|
270
|
+
res = connectMigrate();
|
|
271
|
+
break;
|
|
272
|
+
case "reset": {
|
|
273
|
+
const force = process.argv.includes("--force") || process.argv.includes("-f");
|
|
274
|
+
if (!force && process.stdin.isTTY) {
|
|
275
|
+
console.error("This removes all NeoZip connection data.", "Run neozip-connect (interactive wizard offers reset), or: neozip-connect reset --force");
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
const out = resetConnectionStore();
|
|
279
|
+
res = {
|
|
280
|
+
success: out.removedConnection || out.removedLegacy,
|
|
281
|
+
phase: "no_profile",
|
|
282
|
+
message: [
|
|
283
|
+
out.removedConnection ? `Removed ${out.connectionDir}` : null,
|
|
284
|
+
out.removedLegacy ? `Removed ${out.legacyMcpDir}` : null,
|
|
285
|
+
"Run neozip-connect to set up again.",
|
|
286
|
+
]
|
|
287
|
+
.filter(Boolean)
|
|
288
|
+
.join(" "),
|
|
289
|
+
};
|
|
290
|
+
if (!json) {
|
|
291
|
+
console.error(res.message);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case "help":
|
|
297
|
+
usage();
|
|
298
|
+
return;
|
|
299
|
+
default:
|
|
300
|
+
usage();
|
|
301
|
+
throw new Error(`Unknown command: ${command}`);
|
|
302
|
+
}
|
|
303
|
+
printResult(res, json);
|
|
304
|
+
if (!res.success && res.phase !== "awaiting_code") {
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
main().catch((err) => {
|
|
309
|
+
console.error(`neozip-connect: ${err instanceof Error ? err.message : String(err)}`);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
});
|
|
312
|
+
//# sourceMappingURL=connect-cli.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { WRAP_AEAD, WRAP_FORMAT_VERSION, WRAP_IV_LEN, WRAP_KDF_INFO, WRAP_KDF_SALT_LEN, } from "../constants/wallet-identity.js";
|
|
2
|
+
import { normalizeTokenServiceOrigin } from "./origin.js";
|
|
3
|
+
function normalizeUrl(baseUrl) {
|
|
4
|
+
return baseUrl.trim().replace(/\/+$/, "");
|
|
5
|
+
}
|
|
6
|
+
export async function fetchCoordinatorConfigParsed(baseUrl) {
|
|
7
|
+
const url = normalizeUrl(baseUrl);
|
|
8
|
+
let res;
|
|
9
|
+
try {
|
|
10
|
+
res = await fetch(`${url}/crypto/coordinator-config`, { method: "GET" });
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (!res.ok)
|
|
16
|
+
return null;
|
|
17
|
+
const ct = res.headers.get("content-type") || "";
|
|
18
|
+
if (!ct.includes("application/json"))
|
|
19
|
+
return null;
|
|
20
|
+
try {
|
|
21
|
+
const data = (await res.json());
|
|
22
|
+
const wrap = data.walletEncryptedIdentityKey;
|
|
23
|
+
return {
|
|
24
|
+
cryptoRequirePhoneVerified: Boolean(data.cryptoRequirePhoneVerified),
|
|
25
|
+
walletGatedIdentityKey: Boolean(data.walletGatedIdentityKey),
|
|
26
|
+
walletEncryptedIdentityKey: wrap
|
|
27
|
+
? {
|
|
28
|
+
wrapFormatVersion: Number(wrap.wrapFormatVersion),
|
|
29
|
+
wrapKdfInfo: String(wrap.wrapKdfInfo ?? ""),
|
|
30
|
+
wrapAead: String(wrap.wrapAead ?? ""),
|
|
31
|
+
wrapKdfSaltLen: Number(wrap.wrapKdfSaltLen),
|
|
32
|
+
wrapIvLen: Number(wrap.wrapIvLen),
|
|
33
|
+
}
|
|
34
|
+
: null,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function assertCoordinatorWrapSupported(config) {
|
|
42
|
+
if (!config?.walletEncryptedIdentityKey) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const w = config.walletEncryptedIdentityKey;
|
|
46
|
+
if (w.wrapFormatVersion !== WRAP_FORMAT_VERSION) {
|
|
47
|
+
throw new Error(`Unsupported wrapFormatVersion ${w.wrapFormatVersion} (client supports ${WRAP_FORMAT_VERSION}).`);
|
|
48
|
+
}
|
|
49
|
+
if (w.wrapAead !== WRAP_AEAD) {
|
|
50
|
+
throw new Error(`Unsupported wrapAead ${w.wrapAead} (client supports ${WRAP_AEAD}).`);
|
|
51
|
+
}
|
|
52
|
+
if (w.wrapKdfInfo !== WRAP_KDF_INFO) {
|
|
53
|
+
throw new Error(`Unsupported wrapKdfInfo (client expects ${WRAP_KDF_INFO}).`);
|
|
54
|
+
}
|
|
55
|
+
if (w.wrapKdfSaltLen !== WRAP_KDF_SALT_LEN) {
|
|
56
|
+
throw new Error(`Unsupported wrapKdfSaltLen (client expects ${WRAP_KDF_SALT_LEN}).`);
|
|
57
|
+
}
|
|
58
|
+
if (w.wrapIvLen !== WRAP_IV_LEN) {
|
|
59
|
+
throw new Error(`Unsupported wrapIvLen (client expects ${WRAP_IV_LEN}).`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function probeTokenServiceReachable(baseUrl) {
|
|
63
|
+
try {
|
|
64
|
+
const parsed = await fetchCoordinatorConfigParsed(baseUrl);
|
|
65
|
+
return parsed !== null;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function coordinatorBaseUrl(serverUrl) {
|
|
72
|
+
return normalizeTokenServiceOrigin(serverUrl || process.env.TOKEN_SERVICE_URL || "");
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=coordinator.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getActiveConnection, getActiveConnectionId, getConnectionDir, readActiveConnectionSecrets, } from "./store.js";
|
|
2
|
+
/** Token Service and wallet credentials from neozip-connect only (not MCP env or wallet.json). */
|
|
3
|
+
export function readConnectionCredentials() {
|
|
4
|
+
const connectionId = getActiveConnectionId();
|
|
5
|
+
const meta = getActiveConnection();
|
|
6
|
+
const secrets = readActiveConnectionSecrets();
|
|
7
|
+
if (!meta || !secrets) {
|
|
8
|
+
return {
|
|
9
|
+
connectionId: null,
|
|
10
|
+
label: null,
|
|
11
|
+
tokenServiceUrl: null,
|
|
12
|
+
recipientEmail: null,
|
|
13
|
+
tokenServiceAccessToken: null,
|
|
14
|
+
walletKey: null,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
connectionId,
|
|
19
|
+
label: meta.label,
|
|
20
|
+
tokenServiceUrl: meta.tokenServiceOrigin ?? null,
|
|
21
|
+
recipientEmail: meta.emailVerified && meta.email ? meta.email : null,
|
|
22
|
+
tokenServiceAccessToken: secrets.accessToken?.trim() || null,
|
|
23
|
+
walletKey: secrets.evmPrivateKeyHex?.trim() || null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function getConnectionStorePath() {
|
|
27
|
+
return getConnectionDir();
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
const CONNECTION_KDF_INFO = "NeoZip/ConnectionSecrets/v1";
|
|
4
|
+
const CONNECTION_KDF_SALT = Buffer.from("NeoZipConnectionStoreSalt-v1", "utf8");
|
|
5
|
+
/** Machine-local default when NEOZIP_UNLOCK_PASSPHRASE is unset. */
|
|
6
|
+
export function machineUnlockPassphrase() {
|
|
7
|
+
return `${os.hostname()}:${os.userInfo().username}:neozip-connection`;
|
|
8
|
+
}
|
|
9
|
+
export function usesCustomUnlockPassphrase() {
|
|
10
|
+
return Boolean(process.env.NEOZIP_UNLOCK_PASSPHRASE?.trim());
|
|
11
|
+
}
|
|
12
|
+
/** Passphrase for encrypting connection secrets at rest. */
|
|
13
|
+
export function resolveUnlockPassphrase() {
|
|
14
|
+
return process.env.NEOZIP_UNLOCK_PASSPHRASE?.trim() || machineUnlockPassphrase();
|
|
15
|
+
}
|
|
16
|
+
function deriveKey() {
|
|
17
|
+
const passphrase = resolveUnlockPassphrase();
|
|
18
|
+
return Buffer.from(crypto.hkdfSync("sha256", Buffer.from(passphrase, "utf8"), CONNECTION_KDF_SALT, CONNECTION_KDF_INFO, 32));
|
|
19
|
+
}
|
|
20
|
+
export function encryptSecrets(payload) {
|
|
21
|
+
const key = deriveKey();
|
|
22
|
+
const iv = crypto.randomBytes(12);
|
|
23
|
+
const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
|
|
24
|
+
try {
|
|
25
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
26
|
+
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
27
|
+
const tag = cipher.getAuthTag();
|
|
28
|
+
return JSON.stringify({
|
|
29
|
+
v: 1,
|
|
30
|
+
iv: iv.toString("base64"),
|
|
31
|
+
tag: tag.toString("base64"),
|
|
32
|
+
data: ct.toString("base64"),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
key.fill(0);
|
|
37
|
+
plaintext.fill(0);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function decryptSecrets(encrypted) {
|
|
41
|
+
const key = deriveKey();
|
|
42
|
+
const parsed = JSON.parse(encrypted);
|
|
43
|
+
const iv = Buffer.from(parsed.iv, "base64");
|
|
44
|
+
const tag = Buffer.from(parsed.tag, "base64");
|
|
45
|
+
const ct = Buffer.from(parsed.data, "base64");
|
|
46
|
+
try {
|
|
47
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
48
|
+
decipher.setAuthTag(tag);
|
|
49
|
+
const pt = Buffer.concat([decipher.update(ct), decipher.final()]);
|
|
50
|
+
return JSON.parse(pt.toString("utf8"));
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
key.fill(0);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { computeConnectionPhase, computeConnectionPhaseForId } from "./phase.js";
|
|
3
|
+
import { connectionPathFor, getActiveConnectionId, getConnectionDir, listConnectionIds, listConnections, readConnectionPublic, } from "./store.js";
|
|
4
|
+
function readManifestForDump() {
|
|
5
|
+
const manifestPath = `${getConnectionDir()}/manifest.json`;
|
|
6
|
+
if (!fs.existsSync(manifestPath)) {
|
|
7
|
+
return { version: 1, activeConnectionId: null, workspaces: {} };
|
|
8
|
+
}
|
|
9
|
+
return JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
10
|
+
}
|
|
11
|
+
/** Safe snapshot of manifest + public.json files (never includes secrets.enc). */
|
|
12
|
+
export function buildConnectionDump(options) {
|
|
13
|
+
const manifest = readManifestForDump();
|
|
14
|
+
const activeId = getActiveConnectionId();
|
|
15
|
+
const requestedId = options?.connectionId?.trim();
|
|
16
|
+
let ids = listConnectionIds();
|
|
17
|
+
if (requestedId) {
|
|
18
|
+
if (!readConnectionPublic(requestedId)) {
|
|
19
|
+
throw new Error(`Connection not found: ${requestedId}`);
|
|
20
|
+
}
|
|
21
|
+
ids = [requestedId];
|
|
22
|
+
}
|
|
23
|
+
const connections = [];
|
|
24
|
+
for (const id of ids) {
|
|
25
|
+
const meta = readConnectionPublic(id);
|
|
26
|
+
if (!meta)
|
|
27
|
+
continue;
|
|
28
|
+
connections.push({
|
|
29
|
+
id,
|
|
30
|
+
path: connectionPathFor(id),
|
|
31
|
+
active: id === activeId,
|
|
32
|
+
hasSecretsEnc: fs.existsSync(`${connectionPathFor(id)}/secrets.enc`),
|
|
33
|
+
public: meta,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
connectionDir: getConnectionDir(),
|
|
38
|
+
phase: computeConnectionPhase(),
|
|
39
|
+
manifest,
|
|
40
|
+
connections,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function formatConnectionDump(snapshot) {
|
|
44
|
+
return JSON.stringify(snapshot, null, 2);
|
|
45
|
+
}
|
|
46
|
+
function formatAccountLine(meta, activeId) {
|
|
47
|
+
const phase = computeConnectionPhaseForId(meta.id);
|
|
48
|
+
const active = meta.id === activeId ? " [active]" : "";
|
|
49
|
+
const email = meta.email ?? "no email";
|
|
50
|
+
const wallet = meta.evmAddress ?? "no wallet";
|
|
51
|
+
const phone = meta.phoneVerifiedAt
|
|
52
|
+
? "phone verified"
|
|
53
|
+
: meta.phoneE164
|
|
54
|
+
? "phone pending"
|
|
55
|
+
: "no phone";
|
|
56
|
+
const ready = meta.onboardingCompleteAt ? "onboarding complete" : "onboarding incomplete";
|
|
57
|
+
return [
|
|
58
|
+
` • ${meta.label}${active}`,
|
|
59
|
+
` Email: ${email}`,
|
|
60
|
+
` Token Service: ${meta.tokenServiceOrigin}`,
|
|
61
|
+
` Phase: ${phase} · Wallet: ${wallet}`,
|
|
62
|
+
` ${phone} · ${ready}`,
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
/** Human-readable summary of all saved accounts (no secrets). */
|
|
66
|
+
export function formatConnectionsSummaryText() {
|
|
67
|
+
const activeId = getActiveConnectionId();
|
|
68
|
+
const connections = listConnections();
|
|
69
|
+
if (connections.length === 0) {
|
|
70
|
+
return `Connection store: ${getConnectionDir()}\nNo saved accounts.`;
|
|
71
|
+
}
|
|
72
|
+
const lines = [
|
|
73
|
+
`Connection store: ${getConnectionDir()}`,
|
|
74
|
+
`Saved accounts (${connections.length}):`,
|
|
75
|
+
...connections.flatMap((conn) => formatAccountLine(conn, activeId)),
|
|
76
|
+
];
|
|
77
|
+
return lines.join("\n");
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=dump.js.map
|