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,303 @@
|
|
|
1
|
+
import neozipkit from "neozipkit";
|
|
2
|
+
const { HashCalculator } = neozipkit;
|
|
3
|
+
import { TOKENIZED_METADATA, ZipkitVerifier } from "neozip-blockchain";
|
|
4
|
+
import { findMetadataEntry, verifyTimestamp, verifyTimestampedZip, } from "neozip-blockchain/token-service";
|
|
5
|
+
import { extractEntryToBuffer } from "./extract-entry-buffer.js";
|
|
6
|
+
import { isMetadataEntryPath } from "./metadata-paths.js";
|
|
7
|
+
import { hasRecipientAccessMetadata } from "./recipient-access.js";
|
|
8
|
+
import { computeMerkleRoot } from "./merkle.js";
|
|
9
|
+
import { enrichTimestampMetadata } from "./timestamp-network.js";
|
|
10
|
+
import { requireTokenServiceConfigured } from "../token-service/require-configured.js";
|
|
11
|
+
export { isMetadataEntryPath } from "./metadata-paths.js";
|
|
12
|
+
/** Pending TS-SUBMIT archives require live Token Service checks; upgrade for standalone verification. */
|
|
13
|
+
export function timestampUpgradeWarningLines() {
|
|
14
|
+
return [
|
|
15
|
+
"WARNING: Archive has not been upgraded to META-INF/TIMESTAMP.NZIP.",
|
|
16
|
+
"Blockchain verification depends on the NeoZip Token Service and cannot be verified independently.",
|
|
17
|
+
"Run upgrade_timestamped once the batch is confirmed on-chain.",
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
/** Shown when the digest is queued at Token Service and not yet on-chain. */
|
|
21
|
+
export function timestampQueuedWarningLines() {
|
|
22
|
+
return [
|
|
23
|
+
"The digest has been submitted to NeoZip Token Service and is queued.",
|
|
24
|
+
"The service will submit the batch to the blockchain; until then the timestamp stays pending.",
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
function isTimestampArchiveKind(kind) {
|
|
28
|
+
return kind === "timestamped-pending" || kind === "timestamped-confirmed";
|
|
29
|
+
}
|
|
30
|
+
export function formatBlockchainVerificationSummary(verification) {
|
|
31
|
+
if (verification.kind === "plain")
|
|
32
|
+
return null;
|
|
33
|
+
if (verification.status === "failed" || verification.status === "error") {
|
|
34
|
+
return "Blockchain verification check: FAILED";
|
|
35
|
+
}
|
|
36
|
+
if (verification.verifiedOnChain) {
|
|
37
|
+
if (verification.kind === "timestamped-pending") {
|
|
38
|
+
return "Blockchain verification check: VERIFIED (via Token Service only — not independently verifiable)";
|
|
39
|
+
}
|
|
40
|
+
return "Blockchain verification check: VERIFIED";
|
|
41
|
+
}
|
|
42
|
+
return "Blockchain verification check: PENDING";
|
|
43
|
+
}
|
|
44
|
+
export function detectArchiveKind(entries) {
|
|
45
|
+
const hasToken = entries.some((e) => (e.fileName || e.filename) === TOKENIZED_METADATA);
|
|
46
|
+
if (hasToken)
|
|
47
|
+
return "tokenized";
|
|
48
|
+
const metadata = findMetadataEntry(entries);
|
|
49
|
+
if (metadata?.type === "confirmed")
|
|
50
|
+
return "timestamped-confirmed";
|
|
51
|
+
if (metadata?.type === "pending")
|
|
52
|
+
return "timestamped-pending";
|
|
53
|
+
if (hasRecipientAccessMetadata(entries))
|
|
54
|
+
return "recipient-encrypted";
|
|
55
|
+
return "plain";
|
|
56
|
+
}
|
|
57
|
+
function buildServiceOptions(config) {
|
|
58
|
+
return config.tokenServiceUrl
|
|
59
|
+
? { serverUrl: config.tokenServiceUrl, debug: config.debug }
|
|
60
|
+
: { debug: config.debug };
|
|
61
|
+
}
|
|
62
|
+
export async function verifyArchiveBlockchain(zip, entries, config) {
|
|
63
|
+
const kind = detectArchiveKind(entries);
|
|
64
|
+
const merkleRoot = computeMerkleRoot(entries) ?? undefined;
|
|
65
|
+
if (kind === "plain" || kind === "recipient-encrypted") {
|
|
66
|
+
return {
|
|
67
|
+
kind,
|
|
68
|
+
status: "not_applicable",
|
|
69
|
+
verifiedOnChain: false,
|
|
70
|
+
message: kind === "recipient-encrypted"
|
|
71
|
+
? "Recipient-encrypted archive (META-INF/ACCESS.NZIP). Blockchain metadata not applicable."
|
|
72
|
+
: "No timestamp or token metadata in archive.",
|
|
73
|
+
merkleRoot,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (isTimestampArchiveKind(kind)) {
|
|
77
|
+
const tsErr = requireTokenServiceConfigured("Blockchain verification for timestamped archives", config);
|
|
78
|
+
if (tsErr) {
|
|
79
|
+
return {
|
|
80
|
+
kind,
|
|
81
|
+
status: "error",
|
|
82
|
+
verifiedOnChain: false,
|
|
83
|
+
message: tsErr,
|
|
84
|
+
merkleRoot,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (kind === "tokenized") {
|
|
89
|
+
const tokenEntry = entries.find((e) => (e.fileName || e.filename) === TOKENIZED_METADATA);
|
|
90
|
+
if (!tokenEntry) {
|
|
91
|
+
return {
|
|
92
|
+
kind,
|
|
93
|
+
status: "error",
|
|
94
|
+
verifiedOnChain: false,
|
|
95
|
+
message: "Token metadata entry missing.",
|
|
96
|
+
merkleRoot,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const tokenBuffer = await extractEntryToBuffer(zip, tokenEntry);
|
|
100
|
+
const verifier = new ZipkitVerifier({ debug: config.debug });
|
|
101
|
+
const metadataResult = await verifier.extractTokenMetadata(tokenBuffer);
|
|
102
|
+
if (!metadataResult?.success || !metadataResult.metadata) {
|
|
103
|
+
return {
|
|
104
|
+
kind,
|
|
105
|
+
metadataEntry: TOKENIZED_METADATA,
|
|
106
|
+
status: "error",
|
|
107
|
+
verifiedOnChain: false,
|
|
108
|
+
message: "Failed to parse token metadata.",
|
|
109
|
+
merkleRoot,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const hashCalc = new HashCalculator({ enableAccumulation: true });
|
|
113
|
+
let hasHashes = false;
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
const name = entry.fileName || entry.filename;
|
|
116
|
+
if (name === TOKENIZED_METADATA || isMetadataEntryPath(name))
|
|
117
|
+
continue;
|
|
118
|
+
if (entry.sha256) {
|
|
119
|
+
hashCalc.addHash(Buffer.from(entry.sha256, "hex"));
|
|
120
|
+
hasHashes = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const calculatedMerkleRoot = hasHashes
|
|
124
|
+
? hashCalc.merkleRoot()
|
|
125
|
+
: null;
|
|
126
|
+
const verificationResult = await verifier.verifyToken(metadataResult.metadata, calculatedMerkleRoot, { debug: config.debug });
|
|
127
|
+
const details = verificationResult.verificationDetails;
|
|
128
|
+
const verified = Boolean(verificationResult.success);
|
|
129
|
+
return {
|
|
130
|
+
kind,
|
|
131
|
+
metadataEntry: TOKENIZED_METADATA,
|
|
132
|
+
status: verified ? "verified" : "failed",
|
|
133
|
+
verifiedOnChain: verified,
|
|
134
|
+
message: verificationResult.message || (verified ? "Token verified on-chain." : "Token verification failed."),
|
|
135
|
+
merkleRoot: calculatedMerkleRoot ?? merkleRoot,
|
|
136
|
+
tokenId: details?.tokenId,
|
|
137
|
+
transactionHash: details?.transactionHash,
|
|
138
|
+
network: details?.network,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const metadata = findMetadataEntry(entries);
|
|
142
|
+
const metadataPath = metadata.entry.filename || metadata.entry.fileName;
|
|
143
|
+
let result = await verifyTimestampedZip(zip, buildServiceOptions(config));
|
|
144
|
+
const verifyFromEmbeddedMetadata = async () => {
|
|
145
|
+
const metadataBuffer = await extractEntryToBuffer(zip, metadata.entry);
|
|
146
|
+
const timestampData = JSON.parse(metadataBuffer.toString("utf-8"));
|
|
147
|
+
const enriched = enrichTimestampMetadata(timestampData, config.network);
|
|
148
|
+
const digest = enriched.digest ||
|
|
149
|
+
enriched.merkleRoot ||
|
|
150
|
+
merkleRoot;
|
|
151
|
+
if (digest &&
|
|
152
|
+
merkleRoot &&
|
|
153
|
+
digest.toLowerCase() !== merkleRoot.toLowerCase()) {
|
|
154
|
+
return {
|
|
155
|
+
kind,
|
|
156
|
+
metadataEntry: metadataPath,
|
|
157
|
+
status: "error",
|
|
158
|
+
verifiedOnChain: false,
|
|
159
|
+
message: "Timestamp digest does not match archive merkle root.",
|
|
160
|
+
merkleRoot,
|
|
161
|
+
digest,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return verifyTimestamp((merkleRoot || digest), enriched, buildServiceOptions(config));
|
|
165
|
+
};
|
|
166
|
+
const needsMetadataFallback = result.status === "error" &&
|
|
167
|
+
(result.message?.includes("extract") ||
|
|
168
|
+
result.message?.includes("Validation error") ||
|
|
169
|
+
result.message?.includes("Digest not found"));
|
|
170
|
+
if (needsMetadataFallback) {
|
|
171
|
+
try {
|
|
172
|
+
const fallbackResult = await verifyFromEmbeddedMetadata();
|
|
173
|
+
if ("verifiedOnChain" in fallbackResult) {
|
|
174
|
+
return fallbackResult;
|
|
175
|
+
}
|
|
176
|
+
result = fallbackResult;
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
return {
|
|
180
|
+
kind,
|
|
181
|
+
metadataEntry: metadataPath,
|
|
182
|
+
status: "error",
|
|
183
|
+
verifiedOnChain: false,
|
|
184
|
+
message: err instanceof Error
|
|
185
|
+
? err.message
|
|
186
|
+
: "Failed to extract timestamp metadata.",
|
|
187
|
+
merkleRoot,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (result.status === "none") {
|
|
192
|
+
return {
|
|
193
|
+
kind,
|
|
194
|
+
metadataEntry: metadataPath,
|
|
195
|
+
status: "error",
|
|
196
|
+
verifiedOnChain: false,
|
|
197
|
+
message: result.message || "Timestamp metadata not readable.",
|
|
198
|
+
merkleRoot,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (result.status === "error") {
|
|
202
|
+
return {
|
|
203
|
+
kind,
|
|
204
|
+
metadataEntry: metadataPath,
|
|
205
|
+
status: "error",
|
|
206
|
+
verifiedOnChain: false,
|
|
207
|
+
message: result.message || "Timestamp verification error.",
|
|
208
|
+
merkleRoot,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (result.status === "valid" && result.verified) {
|
|
212
|
+
return {
|
|
213
|
+
kind,
|
|
214
|
+
metadataEntry: metadataPath,
|
|
215
|
+
status: "verified",
|
|
216
|
+
verifiedOnChain: true,
|
|
217
|
+
message: "Timestamp verified on blockchain.",
|
|
218
|
+
merkleRoot: result.digest || merkleRoot,
|
|
219
|
+
digest: result.digest,
|
|
220
|
+
batchId: result.batchId,
|
|
221
|
+
tokenId: result.tokenId,
|
|
222
|
+
transactionHash: result.transactionHash,
|
|
223
|
+
network: result.network,
|
|
224
|
+
blockNumber: result.blockNumber,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
kind,
|
|
229
|
+
metadataEntry: metadataPath,
|
|
230
|
+
status: "pending",
|
|
231
|
+
verifiedOnChain: false,
|
|
232
|
+
message: result.message ||
|
|
233
|
+
"The digest has been submitted to NeoZip Token Service and is queued.",
|
|
234
|
+
merkleRoot: result.digest || merkleRoot,
|
|
235
|
+
digest: result.digest,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
export function formatBlockchainVerification(verification) {
|
|
239
|
+
const lines = [];
|
|
240
|
+
lines.push("");
|
|
241
|
+
lines.push("Blockchain verification:");
|
|
242
|
+
if (verification.kind === "plain") {
|
|
243
|
+
lines.push(" Archive type: plain (no timestamp/token metadata)");
|
|
244
|
+
lines.push(` Status: ${verification.status}`);
|
|
245
|
+
return lines;
|
|
246
|
+
}
|
|
247
|
+
const kindLabel = verification.kind === "tokenized"
|
|
248
|
+
? "tokenized"
|
|
249
|
+
: verification.kind === "timestamped-confirmed"
|
|
250
|
+
? "timestamped (confirmed)"
|
|
251
|
+
: "timestamped (pending submission metadata)";
|
|
252
|
+
lines.push(` Archive type: ${kindLabel}`);
|
|
253
|
+
if (verification.metadataEntry) {
|
|
254
|
+
lines.push(` Metadata entry: ${verification.metadataEntry}`);
|
|
255
|
+
}
|
|
256
|
+
if (verification.merkleRoot) {
|
|
257
|
+
lines.push(` Merkle root: ${verification.merkleRoot}`);
|
|
258
|
+
}
|
|
259
|
+
switch (verification.status) {
|
|
260
|
+
case "verified":
|
|
261
|
+
lines.push(" On-chain status: VERIFIED");
|
|
262
|
+
break;
|
|
263
|
+
case "pending":
|
|
264
|
+
lines.push(" On-chain status: PENDING");
|
|
265
|
+
break;
|
|
266
|
+
case "failed":
|
|
267
|
+
lines.push(" On-chain status: FAILED");
|
|
268
|
+
break;
|
|
269
|
+
case "error":
|
|
270
|
+
lines.push(" On-chain status: ERROR");
|
|
271
|
+
break;
|
|
272
|
+
default:
|
|
273
|
+
lines.push(` On-chain status: ${verification.status}`);
|
|
274
|
+
}
|
|
275
|
+
lines.push(` ${verification.message}`);
|
|
276
|
+
if (verification.tokenId)
|
|
277
|
+
lines.push(` Token ID: ${verification.tokenId}`);
|
|
278
|
+
if (verification.transactionHash) {
|
|
279
|
+
lines.push(` Transaction: ${verification.transactionHash}`);
|
|
280
|
+
}
|
|
281
|
+
if (verification.network)
|
|
282
|
+
lines.push(` Network: ${verification.network}`);
|
|
283
|
+
if (verification.batchId)
|
|
284
|
+
lines.push(` Batch ID: ${verification.batchId}`);
|
|
285
|
+
if (verification.blockNumber != null) {
|
|
286
|
+
lines.push(` Block: ${verification.blockNumber}`);
|
|
287
|
+
}
|
|
288
|
+
if (verification.kind === "timestamped-pending" &&
|
|
289
|
+
verification.status !== "pending") {
|
|
290
|
+
lines.push("");
|
|
291
|
+
for (const warningLine of timestampUpgradeWarningLines()) {
|
|
292
|
+
lines.push(` ${warningLine}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (verification.status === "pending" && isTimestampArchiveKind(verification.kind)) {
|
|
296
|
+
lines.push("");
|
|
297
|
+
for (const warningLine of timestampQueuedWarningLines()) {
|
|
298
|
+
lines.push(` ${warningLine}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return lines;
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=blockchain-status.js.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { resolveTokenServiceBaseUrl } from "./recipient-lookup.js";
|
|
2
|
+
import { tokenServiceAuthErrorMessage } from "../connection/token-auth.js";
|
|
3
|
+
import { getActiveConnection } from "../connection/store.js";
|
|
4
|
+
function pickString(obj, key) {
|
|
5
|
+
const v = obj[key];
|
|
6
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
7
|
+
}
|
|
8
|
+
function parseCryptoSelfBody(parsed, status) {
|
|
9
|
+
if (parsed.success !== true) {
|
|
10
|
+
const err = typeof parsed.error === "string"
|
|
11
|
+
? parsed.error
|
|
12
|
+
: "Unexpected /crypto/self response";
|
|
13
|
+
const hint = typeof parsed.hint === "string" ? parsed.hint : undefined;
|
|
14
|
+
return { ok: false, status, error: err, hint };
|
|
15
|
+
}
|
|
16
|
+
const cryptoRaw = parsed.crypto;
|
|
17
|
+
if (!cryptoRaw || typeof cryptoRaw !== "object") {
|
|
18
|
+
return { ok: false, status, error: "crypto/self missing crypto object" };
|
|
19
|
+
}
|
|
20
|
+
const c = cryptoRaw;
|
|
21
|
+
const walletId = typeof c.walletId === "number" ? c.walletId : Number(c.walletId);
|
|
22
|
+
const identityKeyIdRaw = c.identityKeyId;
|
|
23
|
+
const identityKeyId = typeof identityKeyIdRaw === "number"
|
|
24
|
+
? identityKeyIdRaw
|
|
25
|
+
: identityKeyIdRaw == null
|
|
26
|
+
? null
|
|
27
|
+
: Number.isFinite(Number(identityKeyIdRaw))
|
|
28
|
+
? Number(identityKeyIdRaw)
|
|
29
|
+
: null;
|
|
30
|
+
const evmAddress = pickString(c, "evmAddress");
|
|
31
|
+
const x25519PublicKey = pickString(c, "x25519PublicKey");
|
|
32
|
+
if (!Number.isFinite(walletId) || !evmAddress || !x25519PublicKey) {
|
|
33
|
+
return { ok: false, status, error: "crypto/self crypto object incomplete" };
|
|
34
|
+
}
|
|
35
|
+
const recipientSuites = Array.isArray(c.recipientSuites)
|
|
36
|
+
? c.recipientSuites.filter((s) => typeof s === "string")
|
|
37
|
+
: [];
|
|
38
|
+
return {
|
|
39
|
+
ok: true,
|
|
40
|
+
data: {
|
|
41
|
+
userId: typeof parsed.userId === "number"
|
|
42
|
+
? parsed.userId
|
|
43
|
+
: Number(parsed.userId),
|
|
44
|
+
email: typeof parsed.email === "string" ? parsed.email : "",
|
|
45
|
+
phoneE164: typeof parsed.phoneE164 === "string" ? parsed.phoneE164 : null,
|
|
46
|
+
phoneVerified: parsed.phoneVerified === true,
|
|
47
|
+
walletId,
|
|
48
|
+
identityKeyId,
|
|
49
|
+
evmAddress,
|
|
50
|
+
x25519PublicKey,
|
|
51
|
+
cryptoProvisionedAt: typeof c.cryptoProvisionedAt === "string"
|
|
52
|
+
? c.cryptoProvisionedAt
|
|
53
|
+
: null,
|
|
54
|
+
mlkem768PublicKeyB64: typeof c.mlkem768PublicKeyB64 === "string"
|
|
55
|
+
? c.mlkem768PublicKeyB64
|
|
56
|
+
: null,
|
|
57
|
+
recipientSuites,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function fetchCryptoSelf(config, options) {
|
|
62
|
+
const accessToken = config.tokenServiceAccessToken?.trim();
|
|
63
|
+
if (!accessToken) {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
status: 0,
|
|
67
|
+
error: "NEOZIP_TOKEN_SERVICE_ACCESS_TOKEN not configured. Complete token_service_verify and set the token in MCP env.",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const baseUrl = resolveTokenServiceBaseUrl(config, options?.serverUrl);
|
|
71
|
+
const response = await fetch(`${baseUrl}/crypto/self`, {
|
|
72
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
73
|
+
});
|
|
74
|
+
const ct = response.headers.get("content-type") || "";
|
|
75
|
+
if (!ct.includes("json")) {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
status: response.status,
|
|
79
|
+
error: `Token Service returned non-JSON (HTTP ${response.status})`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
let parsed;
|
|
83
|
+
try {
|
|
84
|
+
parsed = (await response.json());
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
status: response.status,
|
|
90
|
+
error: `Invalid JSON from Token Service (HTTP ${response.status})`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (response.status === 404) {
|
|
94
|
+
const err = typeof parsed.error === "string"
|
|
95
|
+
? parsed.error
|
|
96
|
+
: "Identity key not found";
|
|
97
|
+
const hint = typeof parsed.hint === "string" ? parsed.hint : undefined;
|
|
98
|
+
return { ok: false, status: 404, error: err, hint };
|
|
99
|
+
}
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const err = typeof parsed.error === "string"
|
|
102
|
+
? parsed.error
|
|
103
|
+
: `HTTP ${response.status}`;
|
|
104
|
+
const authHint = tokenServiceAuthErrorMessage(response.status, getActiveConnection()?.email);
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
status: response.status,
|
|
108
|
+
error: authHint ?? err,
|
|
109
|
+
hint: authHint ? "Re-run neozip-connect verify, then reload MCP." : undefined,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return parseCryptoSelfBody(parsed, response.status);
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=crypto-self.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const TEXT_EXTENSIONS = new Set([
|
|
2
|
+
".txt", ".md", ".markdown", ".json", ".csv", ".xml", ".yaml", ".yml",
|
|
3
|
+
".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs",
|
|
4
|
+
".py", ".rb", ".go", ".rs", ".java", ".kt", ".swift",
|
|
5
|
+
".c", ".h", ".cpp", ".hpp", ".cs",
|
|
6
|
+
".html", ".htm", ".css", ".scss", ".less",
|
|
7
|
+
".sh", ".bash", ".zsh", ".fish",
|
|
8
|
+
".sql", ".toml", ".ini", ".cfg", ".conf", ".env",
|
|
9
|
+
".log", ".svg",
|
|
10
|
+
]);
|
|
11
|
+
/** Extensionless benchmark text files (e.g. Calgary corpus). */
|
|
12
|
+
const EXTENSIONLESS_TEXT_NAMES = new Set([
|
|
13
|
+
"bib", "book1", "book2", "news", "trans",
|
|
14
|
+
"paper1", "paper2", "paper3", "paper4", "paper5", "paper6",
|
|
15
|
+
"progc", "progl", "progp",
|
|
16
|
+
]);
|
|
17
|
+
export function detectTextSafety(entryPath, buffer) {
|
|
18
|
+
const ext = entryPath.includes(".")
|
|
19
|
+
? entryPath.slice(entryPath.lastIndexOf(".")).toLowerCase()
|
|
20
|
+
: "";
|
|
21
|
+
const basename = entryPath.replace(/\\/g, "/").split("/").pop() ?? entryPath;
|
|
22
|
+
if (EXTENSIONLESS_TEXT_NAMES.has(basename)) {
|
|
23
|
+
return { isTextSafe: true, contentType: "text/plain", recommendedTool: "read_entry" };
|
|
24
|
+
}
|
|
25
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
26
|
+
const contentType = ext === ".json"
|
|
27
|
+
? "application/json"
|
|
28
|
+
: ext === ".csv"
|
|
29
|
+
? "text/csv"
|
|
30
|
+
: ext === ".md" || ext === ".markdown"
|
|
31
|
+
? "text/markdown"
|
|
32
|
+
: "text/plain";
|
|
33
|
+
return { isTextSafe: true, contentType, recommendedTool: "read_entry" };
|
|
34
|
+
}
|
|
35
|
+
if (buffer) {
|
|
36
|
+
const sample = buffer.subarray(0, Math.min(buffer.length, 8192));
|
|
37
|
+
const nullIndex = sample.indexOf(0);
|
|
38
|
+
if (nullIndex === -1) {
|
|
39
|
+
return {
|
|
40
|
+
isTextSafe: true,
|
|
41
|
+
contentType: "text/plain",
|
|
42
|
+
recommendedTool: "read_entry",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
isTextSafe: false,
|
|
48
|
+
contentType: "application/octet-stream",
|
|
49
|
+
recommendedTool: "extract",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function isBufferTextSafe(buffer) {
|
|
53
|
+
const sample = buffer.subarray(0, Math.min(buffer.length, 8192));
|
|
54
|
+
return sample.indexOf(0) === -1;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=detect-text.js.map
|