neozip-cli 0.80.0 → 0.95.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +93 -0
- package/CHANGELOG.md +117 -12
- package/DOCUMENTATION.md +33 -38
- package/README.md +137 -63
- package/dist/src/account/account-state.js +95 -0
- package/dist/src/account/format-account-status.js +62 -0
- package/dist/src/account/identity-provision.js +79 -0
- package/dist/src/account/identity-wrap.js +106 -0
- package/dist/src/account/profile-crypto.js +85 -0
- package/dist/src/account/profile-store.js +129 -0
- package/dist/src/account/require-account.js +32 -0
- package/dist/src/account/types.js +3 -0
- package/dist/src/account/wallet-evm.js +46 -0
- package/dist/src/account/wallet-setup.js +33 -0
- package/dist/src/archive/crypto-self.js +117 -0
- package/dist/src/archive/identity-key.js +217 -0
- package/dist/src/archive/recipient-lookup.js +61 -0
- package/dist/src/cli/output.js +100 -0
- package/dist/src/cli/params.js +122 -0
- package/dist/src/cli/schema.js +186 -0
- package/dist/src/cli/validate.js +119 -0
- package/dist/src/commands/mintTimestampProof.js +26 -14
- package/dist/src/config/ConfigSetup.js +82 -423
- package/dist/src/connect/command.js +364 -0
- package/dist/src/connection/bootstrap.js +50 -0
- package/dist/src/connection/cli-guidance.js +101 -0
- package/dist/src/connection/cli-prefs.js +180 -0
- package/dist/src/connection/cli-settings.js +140 -0
- package/dist/src/connection/cli-types.js +14 -0
- package/dist/src/connection/coordinator.js +83 -0
- package/dist/src/connection/credentials.js +33 -0
- package/dist/src/connection/crypto.js +96 -0
- package/dist/src/connection/dump.js +117 -0
- package/dist/src/connection/funding.js +187 -0
- package/dist/src/connection/incomplete-setup.js +89 -0
- package/dist/src/connection/interactive.js +871 -0
- package/dist/src/connection/legacy-profile-reader.js +87 -0
- package/dist/src/connection/magic-link.js +142 -0
- package/dist/src/connection/migrate.js +115 -0
- package/dist/src/connection/onboarding.js +616 -0
- package/dist/src/connection/origin.js +69 -0
- package/dist/src/connection/phase.js +101 -0
- package/dist/src/connection/phone.js +26 -0
- package/dist/src/connection/promote-active.js +56 -0
- package/dist/src/connection/reset.js +56 -0
- package/dist/src/connection/status-report.js +52 -0
- package/dist/src/connection/store.js +406 -0
- package/dist/src/connection/token-auth.js +44 -0
- package/dist/src/connection/types.js +3 -0
- package/dist/src/connection/wallet-json-migration.js +155 -0
- package/dist/src/connection/wallet-login.js +65 -0
- package/dist/src/connection/wallet-setup.js +83 -0
- package/dist/src/constants/wallet-identity.js +14 -0
- package/dist/src/exit-codes.js +54 -10
- package/dist/src/neolist.js +100 -10
- package/dist/src/neounzip.js +236 -65
- package/dist/src/neozip/blockchain.js +18 -18
- package/dist/src/neozip/createZip.js +118 -94
- package/dist/src/neozip/upgradeZip.js +14 -11
- package/dist/src/neozip.js +269 -91
- package/dist/src/skills/command.js +256 -0
- package/dist/src/skills/locate.js +99 -0
- package/dist/src/util/mask.js +34 -0
- package/dist/src/util/token-service-fetch.js +26 -0
- package/env.example +18 -85
- package/package.json +89 -73
- package/skills/neozip-connect/SKILL.md +95 -0
- package/skills/neozip-create/SKILL.md +57 -0
- package/skills/neozip-extract/SKILL.md +58 -0
- package/skills/neozip-legacy/SKILL.md +48 -0
- package/skills/neozip-list/SKILL.md +56 -0
- package/skills/neozip-shared/SKILL.md +60 -0
- package/dist/src/commands/verifyEmail.js +0 -146
- package/dist/src/config/ConfigStore.js +0 -406
|
@@ -0,0 +1,85 @@
|
|
|
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.deriveProfileKey = deriveProfileKey;
|
|
37
|
+
exports.encryptJson = encryptJson;
|
|
38
|
+
exports.decryptJson = decryptJson;
|
|
39
|
+
const crypto = __importStar(require("node:crypto"));
|
|
40
|
+
const os = __importStar(require("node:os"));
|
|
41
|
+
const PROFILE_KDF_INFO = "NeoZip/McpProfileSecrets/v1";
|
|
42
|
+
const PROFILE_KDF_SALT = Buffer.from("NeoZipMcpProfileStoreSalt-v1", "utf8");
|
|
43
|
+
function deriveProfileKey() {
|
|
44
|
+
const passphrase = process.env.NEOZIP_UNLOCK_PASSPHRASE?.trim() ||
|
|
45
|
+
`${os.hostname()}:${os.userInfo().username}:neozip-mcp-profile`;
|
|
46
|
+
const okm = crypto.hkdfSync("sha256", Buffer.from(passphrase, "utf8"), PROFILE_KDF_SALT, PROFILE_KDF_INFO, 32);
|
|
47
|
+
return Buffer.from(okm);
|
|
48
|
+
}
|
|
49
|
+
function encryptJson(payload) {
|
|
50
|
+
const key = deriveProfileKey();
|
|
51
|
+
const iv = crypto.randomBytes(12);
|
|
52
|
+
const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
|
|
53
|
+
try {
|
|
54
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
55
|
+
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
56
|
+
const tag = cipher.getAuthTag();
|
|
57
|
+
return JSON.stringify({
|
|
58
|
+
v: 1,
|
|
59
|
+
iv: iv.toString("base64"),
|
|
60
|
+
tag: tag.toString("base64"),
|
|
61
|
+
data: ct.toString("base64"),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
key.fill(0);
|
|
66
|
+
plaintext.fill(0);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function decryptJson(encrypted) {
|
|
70
|
+
const key = deriveProfileKey();
|
|
71
|
+
const parsed = JSON.parse(encrypted);
|
|
72
|
+
const iv = Buffer.from(parsed.iv, "base64");
|
|
73
|
+
const tag = Buffer.from(parsed.tag, "base64");
|
|
74
|
+
const ct = Buffer.from(parsed.data, "base64");
|
|
75
|
+
try {
|
|
76
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
77
|
+
decipher.setAuthTag(tag);
|
|
78
|
+
const pt = Buffer.concat([decipher.update(ct), decipher.final()]);
|
|
79
|
+
return JSON.parse(pt.toString("utf8"));
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
key.fill(0);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=profile-crypto.js.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.savePhoneVerified = exports.saveIdentityProvision = exports.saveWalletLink = exports.saveEvmWallet = exports.saveAccessToken = void 0;
|
|
4
|
+
exports.getMcpProfileDir = getMcpProfileDir;
|
|
5
|
+
exports.listProfiles = listProfiles;
|
|
6
|
+
exports.getActiveProfileId = getActiveProfileId;
|
|
7
|
+
exports.getActiveProfile = getActiveProfile;
|
|
8
|
+
exports.selectProfile = selectProfile;
|
|
9
|
+
exports.createProfile = createProfile;
|
|
10
|
+
exports.updateProfile = updateProfile;
|
|
11
|
+
exports.removeProfile = removeProfile;
|
|
12
|
+
exports.readSecrets = readSecrets;
|
|
13
|
+
exports.readActiveSecrets = readActiveSecrets;
|
|
14
|
+
exports.logoutProfile = logoutProfile;
|
|
15
|
+
exports.clearProfileCredentialsFromEnv = clearProfileCredentialsFromEnv;
|
|
16
|
+
exports.applyActiveProfileToEnv = applyActiveProfileToEnv;
|
|
17
|
+
const store_1 = require("../connection/store");
|
|
18
|
+
Object.defineProperty(exports, "saveAccessToken", { enumerable: true, get: function () { return store_1.saveAccessToken; } });
|
|
19
|
+
Object.defineProperty(exports, "saveEvmWallet", { enumerable: true, get: function () { return store_1.saveEvmWallet; } });
|
|
20
|
+
Object.defineProperty(exports, "saveIdentityProvision", { enumerable: true, get: function () { return store_1.saveIdentityProvision; } });
|
|
21
|
+
Object.defineProperty(exports, "savePhoneVerified", { enumerable: true, get: function () { return store_1.savePhoneVerified; } });
|
|
22
|
+
Object.defineProperty(exports, "saveWalletLink", { enumerable: true, get: function () { return store_1.saveWalletLink; } });
|
|
23
|
+
const migrate_1 = require("../connection/migrate");
|
|
24
|
+
function getMcpProfileDir() {
|
|
25
|
+
return (0, store_1.getConnectionDir)();
|
|
26
|
+
}
|
|
27
|
+
function ensureMigrated() {
|
|
28
|
+
if ((0, migrate_1.hasLegacyMcpProfiles)()) {
|
|
29
|
+
(0, migrate_1.migrateFromMcpProfileStore)();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function toProfile(meta) {
|
|
33
|
+
if (!meta)
|
|
34
|
+
return null;
|
|
35
|
+
return {
|
|
36
|
+
id: meta.id,
|
|
37
|
+
label: meta.label,
|
|
38
|
+
tokenServiceUrl: meta.tokenServiceOrigin,
|
|
39
|
+
email: meta.email,
|
|
40
|
+
emailVerified: meta.emailVerified,
|
|
41
|
+
evmAddress: meta.evmAddress,
|
|
42
|
+
walletId: meta.walletId,
|
|
43
|
+
linkId: meta.linkId,
|
|
44
|
+
identityKeyId: meta.identityKeyId,
|
|
45
|
+
x25519Fingerprint: meta.x25519Fingerprint,
|
|
46
|
+
createdAt: meta.createdAt,
|
|
47
|
+
updatedAt: meta.updatedAt,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function toSecrets(secrets) {
|
|
51
|
+
if (!secrets)
|
|
52
|
+
return null;
|
|
53
|
+
return {
|
|
54
|
+
accessToken: secrets.accessToken,
|
|
55
|
+
evmPrivateKey: secrets.evmPrivateKeyHex,
|
|
56
|
+
accessTokenObtainedAt: secrets.accessTokenObtainedAt,
|
|
57
|
+
accessTokenExpiresAt: secrets.accessTokenExpiresAt,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function listProfiles() {
|
|
61
|
+
ensureMigrated();
|
|
62
|
+
return (0, store_1.listConnections)().map((c) => ({
|
|
63
|
+
id: c.id,
|
|
64
|
+
label: c.label,
|
|
65
|
+
tokenServiceUrl: c.tokenServiceOrigin,
|
|
66
|
+
email: c.email,
|
|
67
|
+
emailVerified: c.emailVerified,
|
|
68
|
+
evmAddress: c.evmAddress,
|
|
69
|
+
walletId: c.walletId,
|
|
70
|
+
linkId: c.linkId,
|
|
71
|
+
identityKeyId: c.identityKeyId,
|
|
72
|
+
x25519Fingerprint: c.x25519Fingerprint,
|
|
73
|
+
createdAt: c.createdAt,
|
|
74
|
+
updatedAt: c.updatedAt,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
function getActiveProfileId() {
|
|
78
|
+
ensureMigrated();
|
|
79
|
+
return (0, store_1.getActiveConnectionId)();
|
|
80
|
+
}
|
|
81
|
+
function getActiveProfile() {
|
|
82
|
+
ensureMigrated();
|
|
83
|
+
return toProfile((0, store_1.getActiveConnection)());
|
|
84
|
+
}
|
|
85
|
+
function selectProfile(profileId) {
|
|
86
|
+
ensureMigrated();
|
|
87
|
+
return toProfile((0, store_1.selectConnection)(profileId));
|
|
88
|
+
}
|
|
89
|
+
function createProfile(input) {
|
|
90
|
+
ensureMigrated();
|
|
91
|
+
return toProfile((0, store_1.createConnection)({ label: input.label, tokenServiceUrl: input.tokenServiceUrl }));
|
|
92
|
+
}
|
|
93
|
+
function updateProfile(profileId, patch) {
|
|
94
|
+
ensureMigrated();
|
|
95
|
+
const mapped = { ...patch };
|
|
96
|
+
if (patch.tokenServiceUrl) {
|
|
97
|
+
mapped.tokenServiceOrigin = patch.tokenServiceUrl;
|
|
98
|
+
delete mapped.tokenServiceUrl;
|
|
99
|
+
}
|
|
100
|
+
return toProfile((0, store_1.updateConnection)(profileId, mapped));
|
|
101
|
+
}
|
|
102
|
+
function removeProfile(_profileId, confirm) {
|
|
103
|
+
if (!confirm) {
|
|
104
|
+
throw new Error("Set confirm: true to delete the profile and its secrets.");
|
|
105
|
+
}
|
|
106
|
+
throw new Error("Profile removal via account_remove is deprecated. Use neozip connect logout.");
|
|
107
|
+
}
|
|
108
|
+
function readSecrets(profileId) {
|
|
109
|
+
ensureMigrated();
|
|
110
|
+
return toSecrets((0, store_1.readConnectionSecrets)(profileId));
|
|
111
|
+
}
|
|
112
|
+
function readActiveSecrets() {
|
|
113
|
+
ensureMigrated();
|
|
114
|
+
return toSecrets((0, store_1.readActiveConnectionSecrets)());
|
|
115
|
+
}
|
|
116
|
+
function logoutProfile(profileId) {
|
|
117
|
+
ensureMigrated();
|
|
118
|
+
const out = (0, store_1.logoutConnection)(profileId);
|
|
119
|
+
return { ...out, profileId: out.connectionId };
|
|
120
|
+
}
|
|
121
|
+
function clearProfileCredentialsFromEnv() {
|
|
122
|
+
(0, store_1.clearConnectionCredentialsFromEnv)();
|
|
123
|
+
}
|
|
124
|
+
function applyActiveProfileToEnv() {
|
|
125
|
+
ensureMigrated();
|
|
126
|
+
const out = (0, store_1.applyActiveConnectionToEnv)();
|
|
127
|
+
return { applied: out.applied, profileId: out.connectionId };
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=profile-store.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireAccountReady = requireAccountReady;
|
|
4
|
+
const phase_1 = require("../connection/phase");
|
|
5
|
+
const store_1 = require("../connection/store");
|
|
6
|
+
const token_auth_1 = require("../connection/token-auth");
|
|
7
|
+
async function requireAccountReady(context) {
|
|
8
|
+
const phase = (0, phase_1.computeConnectionPhase)();
|
|
9
|
+
const meta = (0, store_1.getActiveConnection)();
|
|
10
|
+
const secrets = (0, store_1.readActiveConnectionSecrets)();
|
|
11
|
+
if (secrets?.accessToken?.trim() && (0, token_auth_1.isAccessTokenExpired)(secrets)) {
|
|
12
|
+
return [
|
|
13
|
+
`${context} requires a valid Token Service access token.`,
|
|
14
|
+
(0, token_auth_1.formatTokenReauthGuidance)(meta?.email),
|
|
15
|
+
].join("\n\n");
|
|
16
|
+
}
|
|
17
|
+
if (phase !== "ready") {
|
|
18
|
+
const nextCmd = (0, phase_1.nextConnectCommand)(phase, {
|
|
19
|
+
email: meta?.email,
|
|
20
|
+
phoneE164: meta?.phoneE164,
|
|
21
|
+
});
|
|
22
|
+
const parts = [
|
|
23
|
+
`${context} requires a fully initialized NeoZip account (phase: ${phase}).`,
|
|
24
|
+
];
|
|
25
|
+
if (nextCmd)
|
|
26
|
+
parts.push(`Run: ${nextCmd}`);
|
|
27
|
+
parts.push("Reload MCP after setup so credentials load into this session.");
|
|
28
|
+
return parts.join(" ");
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=require-account.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateEvmIdentity = generateEvmIdentity;
|
|
4
|
+
exports.evmIdentityFromMnemonic = evmIdentityFromMnemonic;
|
|
5
|
+
exports.evmIdentityFromPrivateKey = evmIdentityFromPrivateKey;
|
|
6
|
+
exports.signEvmMessage = signEvmMessage;
|
|
7
|
+
exports.rawEoaBytes = rawEoaBytes;
|
|
8
|
+
const ethers_1 = require("ethers");
|
|
9
|
+
function generateEvmIdentity() {
|
|
10
|
+
const wallet = ethers_1.Wallet.createRandom();
|
|
11
|
+
return {
|
|
12
|
+
address: wallet.address,
|
|
13
|
+
privateKey: wallet.privateKey,
|
|
14
|
+
mnemonic: wallet.mnemonic?.phrase ?? null,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function evmIdentityFromMnemonic(mnemonic) {
|
|
18
|
+
const phrase = mnemonic.trim().split(/\s+/).join(" ");
|
|
19
|
+
const wallet = ethers_1.Wallet.fromPhrase(phrase);
|
|
20
|
+
return {
|
|
21
|
+
address: wallet.address,
|
|
22
|
+
privateKey: wallet.privateKey,
|
|
23
|
+
mnemonic: wallet.mnemonic?.phrase ?? phrase,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function evmIdentityFromPrivateKey(privateKey) {
|
|
27
|
+
const raw = privateKey.trim();
|
|
28
|
+
const normalized = raw.startsWith("0x") ? raw : `0x${raw}`;
|
|
29
|
+
const wallet = new ethers_1.Wallet(normalized);
|
|
30
|
+
return {
|
|
31
|
+
address: wallet.address,
|
|
32
|
+
privateKey: wallet.privateKey,
|
|
33
|
+
mnemonic: null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function signEvmMessage(privateKey, message) {
|
|
37
|
+
const wallet = new ethers_1.Wallet(privateKey);
|
|
38
|
+
return wallet.signMessage(message);
|
|
39
|
+
}
|
|
40
|
+
function rawEoaBytes(privateKey) {
|
|
41
|
+
const stripped = privateKey.startsWith("0x")
|
|
42
|
+
? privateKey.slice(2)
|
|
43
|
+
: privateKey;
|
|
44
|
+
return Buffer.from(stripped, "hex");
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=wallet-evm.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runWalletEnsureAndAttach = runWalletEnsureAndAttach;
|
|
4
|
+
/**
|
|
5
|
+
* Local orchestration for linking the Data Wallet to a Token Service account.
|
|
6
|
+
*
|
|
7
|
+
* The individual HTTP calls live in `neozip-blockchain/token-service`; this
|
|
8
|
+
* module composes the challenge/complete pairs around a local EVM signer, since
|
|
9
|
+
* signing requires the wallet's private key, which never leaves the CLI host.
|
|
10
|
+
*/
|
|
11
|
+
const token_service_1 = require("neozip-blockchain/token-service");
|
|
12
|
+
const wallet_evm_1 = require("./wallet-evm");
|
|
13
|
+
/**
|
|
14
|
+
* Ensure a wallet record exists for `evmAddress` and attach it to the
|
|
15
|
+
* authenticated account as the primary wallet, signing both challenges locally.
|
|
16
|
+
*/
|
|
17
|
+
async function runWalletEnsureAndAttach(baseUrl, accessToken, evmPrivateKey, evmAddress) {
|
|
18
|
+
const ensureCh = await (0, token_service_1.requestWalletEnsureChallenge)(baseUrl, evmAddress);
|
|
19
|
+
const ensureSig = await (0, wallet_evm_1.signEvmMessage)(evmPrivateKey, ensureCh.message);
|
|
20
|
+
const ensured = await (0, token_service_1.completeWalletEnsure)(baseUrl, {
|
|
21
|
+
evmAddress,
|
|
22
|
+
challengeId: ensureCh.challengeId,
|
|
23
|
+
evmSignature: ensureSig,
|
|
24
|
+
});
|
|
25
|
+
const attachCh = await (0, token_service_1.requestWalletAttachChallenge)(baseUrl, accessToken, ensured.walletId);
|
|
26
|
+
const attachSig = await (0, wallet_evm_1.signEvmMessage)(evmPrivateKey, attachCh.message);
|
|
27
|
+
const { linkId } = await (0, token_service_1.completeWalletAttach)(baseUrl, accessToken, ensured.walletId, {
|
|
28
|
+
challengeId: attachCh.challengeId,
|
|
29
|
+
evmSignature: attachSig,
|
|
30
|
+
});
|
|
31
|
+
return { walletId: ensured.walletId, evmAddress: ensured.evmAddress, linkId };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=wallet-setup.js.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchCryptoSelf = fetchCryptoSelf;
|
|
4
|
+
const recipient_lookup_1 = require("./recipient-lookup");
|
|
5
|
+
const token_auth_1 = require("../connection/token-auth");
|
|
6
|
+
const store_1 = require("../connection/store");
|
|
7
|
+
function pickString(obj, key) {
|
|
8
|
+
const v = obj[key];
|
|
9
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
10
|
+
}
|
|
11
|
+
function parseCryptoSelfBody(parsed, status) {
|
|
12
|
+
if (parsed.success !== true) {
|
|
13
|
+
const err = typeof parsed.error === "string"
|
|
14
|
+
? parsed.error
|
|
15
|
+
: "Unexpected /crypto/self response";
|
|
16
|
+
const hint = typeof parsed.hint === "string" ? parsed.hint : undefined;
|
|
17
|
+
return { ok: false, status, error: err, hint };
|
|
18
|
+
}
|
|
19
|
+
const cryptoRaw = parsed.crypto;
|
|
20
|
+
if (!cryptoRaw || typeof cryptoRaw !== "object") {
|
|
21
|
+
return { ok: false, status, error: "crypto/self missing crypto object" };
|
|
22
|
+
}
|
|
23
|
+
const c = cryptoRaw;
|
|
24
|
+
const walletId = typeof c.walletId === "number" ? c.walletId : Number(c.walletId);
|
|
25
|
+
const identityKeyIdRaw = c.identityKeyId;
|
|
26
|
+
const identityKeyId = typeof identityKeyIdRaw === "number"
|
|
27
|
+
? identityKeyIdRaw
|
|
28
|
+
: identityKeyIdRaw == null
|
|
29
|
+
? null
|
|
30
|
+
: Number.isFinite(Number(identityKeyIdRaw))
|
|
31
|
+
? Number(identityKeyIdRaw)
|
|
32
|
+
: null;
|
|
33
|
+
const evmAddress = pickString(c, "evmAddress");
|
|
34
|
+
const x25519PublicKey = pickString(c, "x25519PublicKey");
|
|
35
|
+
if (!Number.isFinite(walletId) || !evmAddress || !x25519PublicKey) {
|
|
36
|
+
return { ok: false, status, error: "crypto/self crypto object incomplete" };
|
|
37
|
+
}
|
|
38
|
+
const recipientSuites = Array.isArray(c.recipientSuites)
|
|
39
|
+
? c.recipientSuites.filter((s) => typeof s === "string")
|
|
40
|
+
: [];
|
|
41
|
+
return {
|
|
42
|
+
ok: true,
|
|
43
|
+
data: {
|
|
44
|
+
userId: typeof parsed.userId === "number"
|
|
45
|
+
? parsed.userId
|
|
46
|
+
: Number(parsed.userId),
|
|
47
|
+
email: typeof parsed.email === "string" ? parsed.email : "",
|
|
48
|
+
phoneE164: typeof parsed.phoneE164 === "string" ? parsed.phoneE164 : null,
|
|
49
|
+
phoneVerified: parsed.phoneVerified === true,
|
|
50
|
+
walletId,
|
|
51
|
+
identityKeyId,
|
|
52
|
+
evmAddress,
|
|
53
|
+
x25519PublicKey,
|
|
54
|
+
cryptoProvisionedAt: typeof c.cryptoProvisionedAt === "string"
|
|
55
|
+
? c.cryptoProvisionedAt
|
|
56
|
+
: null,
|
|
57
|
+
mlkem768PublicKeyB64: typeof c.mlkem768PublicKeyB64 === "string"
|
|
58
|
+
? c.mlkem768PublicKeyB64
|
|
59
|
+
: null,
|
|
60
|
+
recipientSuites,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function fetchCryptoSelf(config, options) {
|
|
65
|
+
const accessToken = config.tokenServiceAccessToken?.trim();
|
|
66
|
+
if (!accessToken) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
status: 0,
|
|
70
|
+
error: "NEOZIP_TOKEN_SERVICE_ACCESS_TOKEN not configured. Complete token_service_verify and set the token in MCP env.",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const baseUrl = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)(config, options?.serverUrl);
|
|
74
|
+
const response = await fetch(`${baseUrl}/crypto/self`, {
|
|
75
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
76
|
+
});
|
|
77
|
+
const ct = response.headers.get("content-type") || "";
|
|
78
|
+
if (!ct.includes("json")) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
status: response.status,
|
|
82
|
+
error: `Token Service returned non-JSON (HTTP ${response.status})`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = (await response.json());
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
status: response.status,
|
|
93
|
+
error: `Invalid JSON from Token Service (HTTP ${response.status})`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (response.status === 404) {
|
|
97
|
+
const err = typeof parsed.error === "string"
|
|
98
|
+
? parsed.error
|
|
99
|
+
: "Identity key not found";
|
|
100
|
+
const hint = typeof parsed.hint === "string" ? parsed.hint : undefined;
|
|
101
|
+
return { ok: false, status: 404, error: err, hint };
|
|
102
|
+
}
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const err = typeof parsed.error === "string"
|
|
105
|
+
? parsed.error
|
|
106
|
+
: `HTTP ${response.status}`;
|
|
107
|
+
const authHint = (0, token_auth_1.tokenServiceAuthErrorMessage)(response.status, (0, store_1.getActiveConnection)()?.email);
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
status: response.status,
|
|
111
|
+
error: authHint ?? err,
|
|
112
|
+
hint: authHint ? 'Re-run neozip connect verify.' : undefined,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return parseCryptoSelfBody(parsed, response.status);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=crypto-self.js.map
|
|
@@ -0,0 +1,217 @@
|
|
|
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.IdentityKeyVersionError = exports.IdentityKeyTamperError = void 0;
|
|
37
|
+
exports.unwrapX25519PrivateKey = unwrapX25519PrivateKey;
|
|
38
|
+
exports.hasConfiguredIdentityKey = hasConfiguredIdentityKey;
|
|
39
|
+
exports.resolveIdentityPrivateKeyHex = resolveIdentityPrivateKeyHex;
|
|
40
|
+
const crypto = __importStar(require("node:crypto"));
|
|
41
|
+
const ethers_1 = require("ethers");
|
|
42
|
+
const wallet_identity_1 = require("../constants/wallet-identity");
|
|
43
|
+
const crypto_self_1 = require("./crypto-self");
|
|
44
|
+
const recipient_lookup_1 = require("./recipient-lookup");
|
|
45
|
+
class IdentityKeyTamperError extends Error {
|
|
46
|
+
constructor(message) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "IdentityKeyTamperError";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.IdentityKeyTamperError = IdentityKeyTamperError;
|
|
52
|
+
class IdentityKeyVersionError extends Error {
|
|
53
|
+
constructor(message) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "IdentityKeyVersionError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.IdentityKeyVersionError = IdentityKeyVersionError;
|
|
59
|
+
function buildCanonicalAad(params) {
|
|
60
|
+
const lowerAddr = params.evmAddress.toLowerCase();
|
|
61
|
+
const addrBytes = Buffer.from(lowerAddr, "ascii");
|
|
62
|
+
if (params.x25519SpkiDer.length !== 44) {
|
|
63
|
+
throw new Error(`Unexpected SPKI DER length ${params.x25519SpkiDer.length}, expected 44 bytes`);
|
|
64
|
+
}
|
|
65
|
+
return Buffer.concat([
|
|
66
|
+
addrBytes,
|
|
67
|
+
Buffer.from([wallet_identity_1.WRAP_FORMAT_VERSION]),
|
|
68
|
+
params.x25519SpkiDer,
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
function deriveKek(rawEoaPrivBytes, saltB64) {
|
|
72
|
+
if (rawEoaPrivBytes.length !== 32) {
|
|
73
|
+
throw new Error("EOA private key must be 32 bytes");
|
|
74
|
+
}
|
|
75
|
+
const salt = Buffer.from(saltB64, "base64");
|
|
76
|
+
if (salt.length !== wallet_identity_1.WRAP_KDF_SALT_LEN) {
|
|
77
|
+
throw new Error(`Wrap KDF salt must be ${wallet_identity_1.WRAP_KDF_SALT_LEN} bytes`);
|
|
78
|
+
}
|
|
79
|
+
const okm = crypto.hkdfSync("sha256", rawEoaPrivBytes, salt, wallet_identity_1.WRAP_KDF_INFO, 32);
|
|
80
|
+
return Buffer.from(okm);
|
|
81
|
+
}
|
|
82
|
+
function pkcs8FromRaw(rawPriv) {
|
|
83
|
+
const prefix = Buffer.from("302e020100300506032b656e04200420", "hex");
|
|
84
|
+
return Buffer.concat([prefix, rawPriv]);
|
|
85
|
+
}
|
|
86
|
+
function unwrapX25519PrivateKey(params) {
|
|
87
|
+
const { bundle } = params;
|
|
88
|
+
if (bundle.wrapFormatVersion !== wallet_identity_1.WRAP_FORMAT_VERSION) {
|
|
89
|
+
throw new IdentityKeyVersionError(`Unsupported wrap format version ${bundle.wrapFormatVersion}`);
|
|
90
|
+
}
|
|
91
|
+
if (bundle.wrapAead !== wallet_identity_1.WRAP_AEAD || bundle.wrapKdfInfo !== wallet_identity_1.WRAP_KDF_INFO) {
|
|
92
|
+
throw new IdentityKeyVersionError("Unsupported wrap format");
|
|
93
|
+
}
|
|
94
|
+
const expectedSpki = Buffer.from(params.expectedSpkiDerB64, "base64");
|
|
95
|
+
if (expectedSpki.length !== 44 ||
|
|
96
|
+
!expectedSpki.subarray(0, wallet_identity_1.X25519_SPKI_PREFIX.length).equals(wallet_identity_1.X25519_SPKI_PREFIX)) {
|
|
97
|
+
throw new IdentityKeyTamperError("Invalid server X25519 SPKI");
|
|
98
|
+
}
|
|
99
|
+
const recomputedAad = buildCanonicalAad({
|
|
100
|
+
evmAddress: params.expectedEvmAddress,
|
|
101
|
+
x25519SpkiDer: expectedSpki,
|
|
102
|
+
});
|
|
103
|
+
const advertisedAad = Buffer.from(bundle.wrapAadB64, "base64");
|
|
104
|
+
if (advertisedAad.length !== recomputedAad.length ||
|
|
105
|
+
!crypto.timingSafeEqual(advertisedAad, recomputedAad)) {
|
|
106
|
+
throw new IdentityKeyTamperError("Wrap AAD does not match local recomputation");
|
|
107
|
+
}
|
|
108
|
+
const kek = deriveKek(params.rawEoaPrivBytes, bundle.wrapKdfSaltB64);
|
|
109
|
+
const iv = Buffer.from(bundle.wrapIvB64, "base64");
|
|
110
|
+
if (iv.length !== wallet_identity_1.WRAP_IV_LEN) {
|
|
111
|
+
kek.fill(0);
|
|
112
|
+
throw new IdentityKeyVersionError(`Invalid wrap IV length ${iv.length}`);
|
|
113
|
+
}
|
|
114
|
+
const wrapped = Buffer.from(bundle.wrappedPrivateKeyB64, "base64");
|
|
115
|
+
const ct = wrapped.subarray(0, wrapped.length - 16);
|
|
116
|
+
const tag = wrapped.subarray(wrapped.length - 16);
|
|
117
|
+
try {
|
|
118
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", kek, iv);
|
|
119
|
+
decipher.setAAD(recomputedAad);
|
|
120
|
+
decipher.setAuthTag(tag);
|
|
121
|
+
const pt = Buffer.concat([decipher.update(ct), decipher.final()]);
|
|
122
|
+
if (pt.length !== 32) {
|
|
123
|
+
pt.fill(0);
|
|
124
|
+
throw new IdentityKeyTamperError("Unwrapped key length is not 32 bytes");
|
|
125
|
+
}
|
|
126
|
+
return pt;
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
kek.fill(0);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function fetchJson(url, init) {
|
|
133
|
+
const response = await fetch(url, init);
|
|
134
|
+
const ct = response.headers.get("content-type") || "";
|
|
135
|
+
if (!ct.includes("json")) {
|
|
136
|
+
return { ok: response.ok, status: response.status, data: null };
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
return {
|
|
140
|
+
ok: response.ok,
|
|
141
|
+
status: response.status,
|
|
142
|
+
data: (await response.json()),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return { ok: response.ok, status: response.status, data: null };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function loadIdentityKeyFromWallet(config) {
|
|
150
|
+
const accessToken = config.tokenServiceAccessToken?.trim();
|
|
151
|
+
const walletKey = config.walletKey?.trim();
|
|
152
|
+
if (!accessToken || !walletKey) {
|
|
153
|
+
throw new Error("Wallet identity key requires NEOZIP_TOKEN_SERVICE_ACCESS_TOKEN and NEOZIP_WALLET_PASSKEY in MCP env.");
|
|
154
|
+
}
|
|
155
|
+
const self = await (0, crypto_self_1.fetchCryptoSelf)(config);
|
|
156
|
+
if (!self.ok) {
|
|
157
|
+
throw new Error(self.hint
|
|
158
|
+
? `${self.error} (${self.hint})`
|
|
159
|
+
: self.error);
|
|
160
|
+
}
|
|
161
|
+
const walletId = self.data.walletId;
|
|
162
|
+
const expectedSpkiB64 = self.data.x25519PublicKey;
|
|
163
|
+
const baseUrl = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)(config);
|
|
164
|
+
const auth = { Authorization: `Bearer ${accessToken}` };
|
|
165
|
+
const keyRes = await fetchJson(`${baseUrl}/identity/wallets/${walletId}/identity-key`, {
|
|
166
|
+
headers: auth,
|
|
167
|
+
});
|
|
168
|
+
if (!keyRes.ok || !keyRes.data?.success || !keyRes.data.identityKey) {
|
|
169
|
+
throw new Error(keyRes.data?.error ||
|
|
170
|
+
`Failed to fetch identity key (HTTP ${keyRes.status})`);
|
|
171
|
+
}
|
|
172
|
+
const bundle = keyRes.data.identityKey;
|
|
173
|
+
const wallet = new ethers_1.Wallet(walletKey);
|
|
174
|
+
const rawEoa = Buffer.from(wallet.privateKey.startsWith("0x")
|
|
175
|
+
? wallet.privateKey.slice(2)
|
|
176
|
+
: wallet.privateKey, "hex");
|
|
177
|
+
const expectedAddr = bundle.evmAddress || wallet.address;
|
|
178
|
+
let x25519Private = null;
|
|
179
|
+
try {
|
|
180
|
+
x25519Private = unwrapX25519PrivateKey({
|
|
181
|
+
rawEoaPrivBytes: rawEoa,
|
|
182
|
+
bundle,
|
|
183
|
+
expectedEvmAddress: expectedAddr,
|
|
184
|
+
expectedSpkiDerB64: expectedSpkiB64,
|
|
185
|
+
});
|
|
186
|
+
const hex = x25519Private.toString("hex");
|
|
187
|
+
return hex;
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
rawEoa.fill(0);
|
|
191
|
+
if (x25519Private)
|
|
192
|
+
x25519Private.fill(0);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function hasConfiguredIdentityKey(config) {
|
|
196
|
+
return Boolean(config.identityPrivateKeyHex?.trim() ||
|
|
197
|
+
(config.tokenServiceAccessToken?.trim() && config.walletKey?.trim()));
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Resolve X25519 private key hex for recipient-encrypted archives.
|
|
201
|
+
* Never pass through MCP tool arguments — env/config only.
|
|
202
|
+
*/
|
|
203
|
+
async function resolveIdentityPrivateKeyHex(config) {
|
|
204
|
+
const envHex = config.identityPrivateKeyHex?.trim();
|
|
205
|
+
if (envHex) {
|
|
206
|
+
const clean = envHex.startsWith("0x") ? envHex.slice(2) : envHex;
|
|
207
|
+
if (!/^[0-9a-fA-F]{64}$/.test(clean)) {
|
|
208
|
+
throw new Error("NEOZIP_IDENTITY_PRIVATE_KEY_HEX must be 64 hex characters (32-byte X25519 key)");
|
|
209
|
+
}
|
|
210
|
+
return clean.toLowerCase();
|
|
211
|
+
}
|
|
212
|
+
if (config.tokenServiceAccessToken && config.walletKey) {
|
|
213
|
+
return loadIdentityKeyFromWallet(config);
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=identity-key.js.map
|