@mysten-incubation/memwal-mcp 0.0.1-dev.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/dist/auth-required.d.ts +5 -0
- package/dist/auth-required.d.ts.map +1 -0
- package/dist/auth-required.js +175 -0
- package/dist/auth-required.js.map +1 -0
- package/dist/auth.d.ts +30 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +75 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin/memwal-mcp.d.ts +3 -0
- package/dist/bin/memwal-mcp.d.ts.map +1 -0
- package/dist/bin/memwal-mcp.js +10 -0
- package/dist/bin/memwal-mcp.js.map +1 -0
- package/dist/bridge.d.ts +28 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +330 -0
- package/dist/bridge.js.map +1 -0
- package/dist/crypto.d.ts +16 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +41 -0
- package/dist/crypto.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +240 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +13 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +28 -0
- package/dist/logger.js.map +1 -0
- package/dist/login.d.ts +19 -0
- package/dist/login.d.ts.map +1 -0
- package/dist/login.js +272 -0
- package/dist/login.js.map +1 -0
- package/package.json +56 -0
package/dist/bridge.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { credsPath } from "./auth.js";
|
|
2
|
+
import { log, note } from "./logger.js";
|
|
3
|
+
async function openSseStream(relayerUrl, creds) {
|
|
4
|
+
const url = `${relayerUrl.replace(/\/+$/, "")}/api/mcp/sse`;
|
|
5
|
+
const controller = new AbortController();
|
|
6
|
+
const resp = await fetch(url, {
|
|
7
|
+
method: "GET",
|
|
8
|
+
headers: {
|
|
9
|
+
authorization: `Bearer ${creds.delegatePrivateKey}`,
|
|
10
|
+
"x-memwal-account-id": creds.accountId,
|
|
11
|
+
accept: "text/event-stream",
|
|
12
|
+
"cache-control": "no-cache",
|
|
13
|
+
},
|
|
14
|
+
signal: controller.signal,
|
|
15
|
+
});
|
|
16
|
+
if (resp.status === 401) {
|
|
17
|
+
controller.abort();
|
|
18
|
+
log.warn("bridge.unauthorized", { url });
|
|
19
|
+
// DO NOT wipe creds here. A 401 from the relayer is *evidence* of
|
|
20
|
+
// a problem but not *proof* the saved seed is the cause. Possible
|
|
21
|
+
// sources: revoked delegate key (genuine), transient WAF / rate
|
|
22
|
+
// limit (false positive), http_proxy interposed somewhere on the
|
|
23
|
+
// path, or — on `--local` — local malware racing the relayer port.
|
|
24
|
+
// Auto-wiping the seed turns any one of those into a permanent
|
|
25
|
+
// outage that forces re-login. Force-fail loud instead; the user
|
|
26
|
+
// runs `memwal-mcp login` if they want to actually rotate.
|
|
27
|
+
throw new Error("MemWal relayer rejected credentials (HTTP 401). " +
|
|
28
|
+
"Delegate key may have been revoked, the relayer may be " +
|
|
29
|
+
"rate-limiting, or a proxy may be interposed. Saved " +
|
|
30
|
+
`credentials at ${credsPath()} were NOT modified. ` +
|
|
31
|
+
"Run `memwal-mcp login` if you need to rotate the key.");
|
|
32
|
+
}
|
|
33
|
+
if (!resp.ok || !resp.body) {
|
|
34
|
+
const body = resp.body ? await resp.text() : "";
|
|
35
|
+
controller.abort();
|
|
36
|
+
throw new Error(`MemWal relayer SSE handshake failed: HTTP ${resp.status} ${body.slice(0, 200)}`);
|
|
37
|
+
}
|
|
38
|
+
const ct = resp.headers.get("content-type") ?? "";
|
|
39
|
+
if (!ct.includes("event-stream")) {
|
|
40
|
+
controller.abort();
|
|
41
|
+
throw new Error(`MemWal relayer returned unexpected content-type "${ct}" for SSE endpoint`);
|
|
42
|
+
}
|
|
43
|
+
const reader = resp.body.getReader();
|
|
44
|
+
const decoder = new TextDecoder();
|
|
45
|
+
let buf = "";
|
|
46
|
+
let endpointResolved = false;
|
|
47
|
+
let endpointPath = "";
|
|
48
|
+
let streamEnded = false;
|
|
49
|
+
let streamError = null;
|
|
50
|
+
const events = [];
|
|
51
|
+
let queueResolver = null;
|
|
52
|
+
function wake() {
|
|
53
|
+
const r = queueResolver;
|
|
54
|
+
if (r) {
|
|
55
|
+
queueResolver = null;
|
|
56
|
+
r();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function pushEvent(ev) {
|
|
60
|
+
events.push(ev);
|
|
61
|
+
wake();
|
|
62
|
+
}
|
|
63
|
+
// Pump the SSE stream in the background.
|
|
64
|
+
const pump = (async () => {
|
|
65
|
+
try {
|
|
66
|
+
while (true) {
|
|
67
|
+
const { done, value } = await reader.read();
|
|
68
|
+
if (done)
|
|
69
|
+
break;
|
|
70
|
+
buf += decoder.decode(value, { stream: true });
|
|
71
|
+
let sep;
|
|
72
|
+
while ((sep = buf.indexOf("\n\n")) >= 0) {
|
|
73
|
+
const chunk = buf.slice(0, sep);
|
|
74
|
+
buf = buf.slice(sep + 2);
|
|
75
|
+
const lines = chunk.split("\n");
|
|
76
|
+
const event = lines
|
|
77
|
+
.find((l) => l.startsWith("event:"))
|
|
78
|
+
?.slice("event:".length)
|
|
79
|
+
.trim();
|
|
80
|
+
const data = lines
|
|
81
|
+
.filter((l) => l.startsWith("data:"))
|
|
82
|
+
.map((l) => l.slice("data:".length).replace(/^\s/, ""))
|
|
83
|
+
.join("\n");
|
|
84
|
+
if (event === "endpoint" && !endpointResolved) {
|
|
85
|
+
endpointPath = data.trim();
|
|
86
|
+
endpointResolved = true;
|
|
87
|
+
wake();
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (event === "message" || (!event && data)) {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(data);
|
|
93
|
+
pushEvent(parsed);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
log.warn("bridge.sse_parse_failed", { data: data.slice(0, 120) });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (!controller.signal.aborted) {
|
|
104
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
105
|
+
streamError = msg;
|
|
106
|
+
// `terminated` is undici's keep-alive idle drop — happens on
|
|
107
|
+
// long-idle SSE in manual tests. The MCP client wrapping us
|
|
108
|
+
// (Cursor / Claude Desktop) will re-spawn the process if it
|
|
109
|
+
// needs the bridge again, so a clean exit is fine.
|
|
110
|
+
if (msg === "terminated" || msg.includes("ECONNRESET")) {
|
|
111
|
+
log.warn("bridge.sse_idle_closed", { reason: msg });
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
log.error("bridge.sse_pump_error", { err: msg });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
streamEnded = true;
|
|
120
|
+
// Wake any waiter so they see EOF.
|
|
121
|
+
wake();
|
|
122
|
+
}
|
|
123
|
+
})();
|
|
124
|
+
// Wait for the `endpoint` event (or first message) before returning.
|
|
125
|
+
while (!endpointResolved) {
|
|
126
|
+
if (streamEnded) {
|
|
127
|
+
controller.abort();
|
|
128
|
+
throw new Error(`MemWal relayer SSE handshake ended before endpoint event${streamError ? `: ${streamError}` : ""}`);
|
|
129
|
+
}
|
|
130
|
+
await new Promise((r) => (queueResolver = r));
|
|
131
|
+
}
|
|
132
|
+
const iter = {
|
|
133
|
+
async next() {
|
|
134
|
+
while (events.length === 0) {
|
|
135
|
+
if (controller.signal.aborted)
|
|
136
|
+
return { value: undefined, done: true };
|
|
137
|
+
await new Promise((r) => (queueResolver = r));
|
|
138
|
+
}
|
|
139
|
+
return { value: events.shift(), done: false };
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
// `endpointPath` may be relative (`/api/mcp/messages?sessionId=...`) or
|
|
143
|
+
// absolute. Make it absolute for `fetch()`.
|
|
144
|
+
const postUrl = endpointPath.startsWith("http")
|
|
145
|
+
? endpointPath
|
|
146
|
+
: `${relayerUrl.replace(/\/+$/, "")}${endpointPath}`;
|
|
147
|
+
return {
|
|
148
|
+
postUrl,
|
|
149
|
+
iter,
|
|
150
|
+
abort: () => {
|
|
151
|
+
controller.abort();
|
|
152
|
+
void pump; // suppress unused warning
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async function postMessage(postUrl, msg) {
|
|
157
|
+
const resp = await fetch(postUrl, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: { "content-type": "application/json" },
|
|
160
|
+
body: JSON.stringify(msg),
|
|
161
|
+
});
|
|
162
|
+
if (!resp.ok && resp.status !== 202) {
|
|
163
|
+
const body = await resp.text();
|
|
164
|
+
log.warn("bridge.post_non_ok", { status: resp.status, body: body.slice(0, 200) });
|
|
165
|
+
}
|
|
166
|
+
return resp.status;
|
|
167
|
+
}
|
|
168
|
+
function readStdinLines(onLine) {
|
|
169
|
+
return new Promise((resolve) => {
|
|
170
|
+
let buf = "";
|
|
171
|
+
process.stdin.setEncoding("utf8");
|
|
172
|
+
process.stdin.on("data", (chunk) => {
|
|
173
|
+
buf += chunk;
|
|
174
|
+
let nl;
|
|
175
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
176
|
+
const line = buf.slice(0, nl).replace(/\r$/, "");
|
|
177
|
+
buf = buf.slice(nl + 1);
|
|
178
|
+
if (line.length > 0)
|
|
179
|
+
onLine(line);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
process.stdin.on("end", () => resolve());
|
|
183
|
+
process.stdin.on("close", () => resolve());
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function writeStdoutMessage(msg) {
|
|
187
|
+
process.stdout.write(JSON.stringify(msg) + "\n");
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Open the SSE bridge and forward stdio ↔ relayer until stdin closes.
|
|
191
|
+
*
|
|
192
|
+
* On SSE drop (idle timeout in the Rust proxy / undici keep-alive / network
|
|
193
|
+
* blip), we transparently reopen the stream — the relayer issues a fresh
|
|
194
|
+
* sessionId, we route subsequent POSTs there. stdin stays open the whole
|
|
195
|
+
* time, so the MCP client (Cursor / Claude Desktop / etc.) never sees the
|
|
196
|
+
* reconnection.
|
|
197
|
+
*/
|
|
198
|
+
export async function runBridge(creds) {
|
|
199
|
+
note(`Connecting to ${creds.relayerUrl}...`);
|
|
200
|
+
log.info("bridge.connecting", {
|
|
201
|
+
relayer: creds.relayerUrl,
|
|
202
|
+
accountId: creds.accountId,
|
|
203
|
+
delegate: creds.delegateAddress,
|
|
204
|
+
});
|
|
205
|
+
// Live handle to the current SSE stream — replaced whenever we reconnect.
|
|
206
|
+
let sse = await openSseStream(creds.relayerUrl, creds);
|
|
207
|
+
note(`Connected. Bridging stdio MCP ↔ ${creds.relayerUrl}`);
|
|
208
|
+
log.info("bridge.connected", { relayer: creds.relayerUrl });
|
|
209
|
+
let stdinClosed = false;
|
|
210
|
+
let reconnecting = false;
|
|
211
|
+
let reconnectAttempt = 0;
|
|
212
|
+
// In-flight requests pending a response. We replay them after a forced
|
|
213
|
+
// reconnect so a server-side session swap doesn't strand a tool call
|
|
214
|
+
// forever waiting for a reply that will never come. Notifications
|
|
215
|
+
// (no id) and responses (no method) are not tracked.
|
|
216
|
+
const inFlight = new Map();
|
|
217
|
+
async function reconnect(reason) {
|
|
218
|
+
if (stdinClosed || reconnecting)
|
|
219
|
+
return;
|
|
220
|
+
reconnecting = true;
|
|
221
|
+
try {
|
|
222
|
+
sse.abort();
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
/* already dead */
|
|
226
|
+
}
|
|
227
|
+
const backoff = Math.min(15_000, 500 * Math.pow(2, reconnectAttempt));
|
|
228
|
+
reconnectAttempt += 1;
|
|
229
|
+
log.warn("bridge.reconnecting", { reason, backoffMs: backoff, attempt: reconnectAttempt });
|
|
230
|
+
await new Promise((r) => setTimeout(r, backoff));
|
|
231
|
+
try {
|
|
232
|
+
sse = await openSseStream(creds.relayerUrl, creds);
|
|
233
|
+
reconnectAttempt = 0;
|
|
234
|
+
log.info("bridge.reconnected", {
|
|
235
|
+
relayer: creds.relayerUrl,
|
|
236
|
+
replayCount: inFlight.size,
|
|
237
|
+
});
|
|
238
|
+
// Replay any requests that haven't been answered yet against the
|
|
239
|
+
// fresh session. Iterate over a snapshot — postMessage is async
|
|
240
|
+
// and the SSE pump may delete entries concurrently as replies
|
|
241
|
+
// start arriving on the new session.
|
|
242
|
+
for (const [id, msg] of Array.from(inFlight.entries())) {
|
|
243
|
+
try {
|
|
244
|
+
const status = await postMessage(sse.postUrl, msg);
|
|
245
|
+
log.info("bridge.replayed", { id, status });
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
log.error("bridge.replay_failed", {
|
|
249
|
+
id,
|
|
250
|
+
err: err instanceof Error ? err.message : String(err),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
log.error("bridge.reconnect_failed", {
|
|
257
|
+
err: err instanceof Error ? err.message : String(err),
|
|
258
|
+
});
|
|
259
|
+
// Try again on the next stdin message rather than spinning.
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
reconnecting = false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Server → client: stream SSE messages to stdout. Loop forever, restart
|
|
266
|
+
// pump on stream end (which means SSE got cut → we already reconnected).
|
|
267
|
+
const serverPump = (async () => {
|
|
268
|
+
while (!stdinClosed) {
|
|
269
|
+
try {
|
|
270
|
+
while (true) {
|
|
271
|
+
const { value, done } = await sse.iter.next();
|
|
272
|
+
if (done)
|
|
273
|
+
break;
|
|
274
|
+
// Clear in-flight tracking once the response lands.
|
|
275
|
+
if (value &&
|
|
276
|
+
(value.result !== undefined || value.error !== undefined) &&
|
|
277
|
+
value.id !== undefined &&
|
|
278
|
+
value.id !== null) {
|
|
279
|
+
inFlight.delete(value.id);
|
|
280
|
+
}
|
|
281
|
+
writeStdoutMessage(value);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
log.error("bridge.server_pump_error", {
|
|
286
|
+
err: err instanceof Error ? err.message : String(err),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (stdinClosed)
|
|
290
|
+
break;
|
|
291
|
+
// Stream ended unexpectedly — reconnect and resume.
|
|
292
|
+
await reconnect("server-pump-eof");
|
|
293
|
+
}
|
|
294
|
+
})();
|
|
295
|
+
// Client → server: forward stdin lines as POST messages. On 404 (the
|
|
296
|
+
// relayer doesn't know our sessionId — happens right after a reconnect
|
|
297
|
+
// if the message races the new handshake), trigger another reconnect.
|
|
298
|
+
const clientPump = readStdinLines((line) => {
|
|
299
|
+
void (async () => {
|
|
300
|
+
try {
|
|
301
|
+
const msg = JSON.parse(line);
|
|
302
|
+
// Track requests (have both method and id) so we can replay
|
|
303
|
+
// them on reconnect. Notifications and responses are not
|
|
304
|
+
// tracked.
|
|
305
|
+
if (msg.method !== undefined &&
|
|
306
|
+
msg.id !== undefined &&
|
|
307
|
+
msg.id !== null) {
|
|
308
|
+
inFlight.set(msg.id, msg);
|
|
309
|
+
}
|
|
310
|
+
const status = await postMessage(sse.postUrl, msg);
|
|
311
|
+
if (status === 404) {
|
|
312
|
+
log.warn("bridge.session_stale", { sessionUrl: sse.postUrl });
|
|
313
|
+
// reconnect() itself replays in-flight against the fresh
|
|
314
|
+
// session, so no explicit per-message retry is needed.
|
|
315
|
+
await reconnect("post-404");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
log.warn("bridge.stdin_parse_failed", { line: line.slice(0, 120) });
|
|
320
|
+
}
|
|
321
|
+
})();
|
|
322
|
+
}).then(() => {
|
|
323
|
+
stdinClosed = true;
|
|
324
|
+
sse.abort();
|
|
325
|
+
});
|
|
326
|
+
await Promise.race([serverPump, clientPump]);
|
|
327
|
+
sse.abort();
|
|
328
|
+
log.info("bridge.closed", {});
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAoBxC,KAAK,UAAU,aAAa,CACxB,UAAkB,EAClB,KAAwB;IAExB,MAAM,GAAG,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC;IAC5D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IAEzC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC1B,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACL,aAAa,EAAE,UAAU,KAAK,CAAC,kBAAkB,EAAE;YACnD,qBAAqB,EAAE,KAAK,CAAC,SAAS;YACtC,MAAM,EAAE,mBAAmB;YAC3B,eAAe,EAAE,UAAU;SAC9B;QACD,MAAM,EAAE,UAAU,CAAC,MAAM;KAC5B,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACtB,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACzC,kEAAkE;QAClE,kEAAkE;QAClE,gEAAgE;QAChE,iEAAiE;QACjE,mEAAmE;QACnE,+DAA+D;QAC/D,iEAAiE;QACjE,2DAA2D;QAC3D,MAAM,IAAI,KAAK,CACX,kDAAkD;YAC9C,yDAAyD;YACzD,qDAAqD;YACrD,kBAAkB,SAAS,EAAE,sBAAsB;YACnD,uDAAuD,CAC9D,CAAC;IACN,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACX,6CAA6C,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACnF,CAAC;IACN,CAAC;IAED,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAClD,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/B,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACX,oDAAoD,EAAE,oBAAoB,CAC7E,CAAC;IACN,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,MAAM,MAAM,GAAiB,EAAE,CAAC;IAEhC,IAAI,aAAa,GAAiB,IAAI,CAAC;IACvC,SAAS,IAAI;QACT,MAAM,CAAC,GAAG,aAAa,CAAC;QACxB,IAAI,CAAC,EAAE,CAAC;YACJ,aAAa,GAAG,IAAI,CAAC;YACrB,CAAC,EAAE,CAAC;QACR,CAAC;IACL,CAAC;IAED,SAAS,SAAS,CAAC,EAAc;QAC7B,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,IAAI,EAAE,CAAC;IACX,CAAC;IAED,yCAAyC;IACzC,MAAM,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;QACrB,IAAI,CAAC;YACD,OAAO,IAAI,EAAE,CAAC;gBACV,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI;oBAAE,MAAM;gBAChB,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,IAAI,GAAW,CAAC;gBAChB,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;oBAChC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;oBACzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAChC,MAAM,KAAK,GAAG,KAAK;yBACd,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;wBACpC,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;yBACvB,IAAI,EAAE,CAAC;oBACZ,MAAM,IAAI,GAAG,KAAK;yBACb,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;yBACpC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;yBACtD,IAAI,CAAC,IAAI,CAAC,CAAC;oBAChB,IAAI,KAAK,KAAK,UAAU,IAAI,CAAC,gBAAgB,EAAE,CAAC;wBAC5C,YAAY,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;wBAC3B,gBAAgB,GAAG,IAAI,CAAC;wBACxB,IAAI,EAAE,CAAC;wBACP,SAAS;oBACb,CAAC;oBACD,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,EAAE,CAAC;wBAC1C,IAAI,CAAC;4BACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;4BAC9C,SAAS,CAAC,MAAM,CAAC,CAAC;wBACtB,CAAC;wBAAC,MAAM,CAAC;4BACL,GAAG,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;wBACtE,CAAC;oBACL,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC7B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC7D,WAAW,GAAG,GAAG,CAAC;gBAClB,6DAA6D;gBAC7D,4DAA4D;gBAC5D,4DAA4D;gBAC5D,mDAAmD;gBACnD,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;oBACrD,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;gBACxD,CAAC;qBAAM,CAAC;oBACJ,GAAG,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;gBACrD,CAAC;YACL,CAAC;QACL,CAAC;gBAAS,CAAC;YACP,WAAW,GAAG,IAAI,CAAC;YACnB,mCAAmC;YACnC,IAAI,EAAE,CAAC;QACX,CAAC;IACL,CAAC,CAAC,EAAE,CAAC;IAEL,qEAAqE;IACrE,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACvB,IAAI,WAAW,EAAE,CAAC;YACd,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACX,2DAA2D,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACrG,CAAC;QACN,CAAC;QACD,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,IAAI,GAA8B;QACpC,KAAK,CAAC,IAAI;YACN,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAO,EAAE,KAAK,EAAE,SAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBAChF,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC;YACxD,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QACnD,CAAC;KACJ,CAAC;IAEF,wEAAwE;IACxE,4CAA4C;IAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC;QAC3C,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC;IAEzD,OAAO;QACH,OAAO;QACP,IAAI;QACJ,KAAK,EAAE,GAAG,EAAE;YACR,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,IAAI,CAAC,CAAC,0BAA0B;QACzC,CAAC;KACJ,CAAC;AACN,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,GAAe;IACvD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE;QAC9B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;KAC5B,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,SAAS,cAAc,CAAC,MAA8B;IAClD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACvC,GAAG,IAAI,KAAK,CAAC;YACb,IAAI,EAAU,CAAC;YACf,OAAO,CAAC,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACjD,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;gBACxB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;oBAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACtC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAe;IACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAwB;IACpD,IAAI,CAAC,iBAAiB,KAAK,CAAC,UAAU,KAAK,CAAC,CAAC;IAC7C,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE;QAC1B,OAAO,EAAE,KAAK,CAAC,UAAU;QACzB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,KAAK,CAAC,eAAe;KAClC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,IAAI,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACvD,IAAI,CAAC,mCAAmC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAC5D,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAE5D,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IAEzB,uEAAuE;IACvE,qEAAqE;IACrE,kEAAkE;IAClE,qDAAqD;IACrD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;IAExD,KAAK,UAAU,SAAS,CAAC,MAAc;QACnC,IAAI,WAAW,IAAI,YAAY;YAAE,OAAO;QACxC,YAAY,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACD,GAAG,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACL,kBAAkB;QACtB,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;QACtE,gBAAgB,IAAI,CAAC,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC3F,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC;YACD,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YACnD,gBAAgB,GAAG,CAAC,CAAC;YACrB,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE;gBAC3B,OAAO,EAAE,KAAK,CAAC,UAAU;gBACzB,WAAW,EAAE,QAAQ,CAAC,IAAI;aAC7B,CAAC,CAAC;YACH,iEAAiE;YACjE,gEAAgE;YAChE,8DAA8D;YAC9D,qCAAqC;YACrC,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;gBACrD,IAAI,CAAC;oBACD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;oBACnD,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;gBAChD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACX,GAAG,CAAC,KAAK,CAAC,sBAAsB,EAAE;wBAC9B,EAAE;wBACF,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;qBACxD,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,GAAG,CAAC,KAAK,CAAC,yBAAyB,EAAE;gBACjC,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACxD,CAAC,CAAC;YACH,4DAA4D;QAChE,CAAC;gBAAS,CAAC;YACP,YAAY,GAAG,KAAK,CAAC;QACzB,CAAC;IACL,CAAC;IAED,wEAAwE;IACxE,yEAAyE;IACzE,MAAM,UAAU,GAAG,CAAC,KAAK,IAAI,EAAE;QAC3B,OAAO,CAAC,WAAW,EAAE,CAAC;YAClB,IAAI,CAAC;gBACD,OAAO,IAAI,EAAE,CAAC;oBACV,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;oBAC9C,IAAI,IAAI;wBAAE,MAAM;oBAChB,oDAAoD;oBACpD,IACI,KAAK;wBACL,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC;wBACzD,KAAK,CAAC,EAAE,KAAK,SAAS;wBACtB,KAAK,CAAC,EAAE,KAAK,IAAI,EACnB,CAAC;wBACC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBAC9B,CAAC;oBACD,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBAC9B,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,GAAG,CAAC,KAAK,CAAC,0BAA0B,EAAE;oBAClC,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACxD,CAAC,CAAC;YACP,CAAC;YACD,IAAI,WAAW;gBAAE,MAAM;YACvB,oDAAoD;YACpD,MAAM,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACvC,CAAC;IACL,CAAC,CAAC,EAAE,CAAC;IAEL,qEAAqE;IACrE,uEAAuE;IACvE,sEAAsE;IACtE,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE;QACvC,KAAK,CAAC,KAAK,IAAI,EAAE;YACb,IAAI,CAAC;gBACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;gBAC3C,4DAA4D;gBAC5D,yDAAyD;gBACzD,WAAW;gBACX,IACI,GAAG,CAAC,MAAM,KAAK,SAAS;oBACxB,GAAG,CAAC,EAAE,KAAK,SAAS;oBACpB,GAAG,CAAC,EAAE,KAAK,IAAI,EACjB,CAAC;oBACC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBAC9B,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;gBACnD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;oBACjB,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;oBAC9D,yDAAyD;oBACzD,uDAAuD;oBACvD,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;gBAChC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACL,GAAG,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACxE,CAAC;QACL,CAAC,CAAC,EAAE,CAAC;IACT,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;QACT,WAAW,GAAG,IAAI,CAAC;QACnB,GAAG,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7C,GAAG,CAAC,KAAK,EAAE,CAAC;IACZ,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare function hex(bytes: Uint8Array): string;
|
|
2
|
+
declare function fromHex(s: string): Uint8Array;
|
|
3
|
+
export interface Keypair {
|
|
4
|
+
privateKeyHex: string;
|
|
5
|
+
publicKeyHex: string;
|
|
6
|
+
suiAddress: string;
|
|
7
|
+
}
|
|
8
|
+
/** Generate a fresh Ed25519 keypair and derive its Sui address. */
|
|
9
|
+
export declare function generateKeypair(): Promise<Keypair>;
|
|
10
|
+
/**
|
|
11
|
+
* Sui Ed25519 address: blake2b-256(0x00 || pubkey).
|
|
12
|
+
* The `0x00` is the Ed25519 scheme flag byte.
|
|
13
|
+
*/
|
|
14
|
+
export declare function deriveSuiAddress(pubKey: Uint8Array): string;
|
|
15
|
+
export { hex as bytesToHex, fromHex as hexToBytes };
|
|
16
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAMA,iBAAS,GAAG,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAItC;AAED,iBAAS,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAOtC;AAED,MAAM,WAAW,OAAO;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,mEAAmE;AACnE,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAQxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAM3D;AAED,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,OAAO,IAAI,UAAU,EAAE,CAAC"}
|
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ed25519 helpers — pure-JS via @noble/ed25519.
|
|
3
|
+
*/
|
|
4
|
+
import { getPublicKeyAsync, utils } from "@noble/ed25519";
|
|
5
|
+
import { blake2b } from "@noble/hashes/blake2.js";
|
|
6
|
+
function hex(bytes) {
|
|
7
|
+
return Array.from(bytes)
|
|
8
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
9
|
+
.join("");
|
|
10
|
+
}
|
|
11
|
+
function fromHex(s) {
|
|
12
|
+
const clean = s.startsWith("0x") ? s.slice(2) : s;
|
|
13
|
+
const out = new Uint8Array(clean.length / 2);
|
|
14
|
+
for (let i = 0; i < out.length; i++) {
|
|
15
|
+
out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
/** Generate a fresh Ed25519 keypair and derive its Sui address. */
|
|
20
|
+
export async function generateKeypair() {
|
|
21
|
+
const seed = utils.randomPrivateKey(); // 32 bytes
|
|
22
|
+
const pub = await getPublicKeyAsync(seed);
|
|
23
|
+
return {
|
|
24
|
+
privateKeyHex: hex(seed),
|
|
25
|
+
publicKeyHex: hex(pub),
|
|
26
|
+
suiAddress: deriveSuiAddress(pub),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Sui Ed25519 address: blake2b-256(0x00 || pubkey).
|
|
31
|
+
* The `0x00` is the Ed25519 scheme flag byte.
|
|
32
|
+
*/
|
|
33
|
+
export function deriveSuiAddress(pubKey) {
|
|
34
|
+
const buf = new Uint8Array(1 + pubKey.length);
|
|
35
|
+
buf[0] = 0x00;
|
|
36
|
+
buf.set(pubKey, 1);
|
|
37
|
+
const digest = blake2b.create({ dkLen: 32 }).update(buf).digest();
|
|
38
|
+
return "0x" + hex(digest);
|
|
39
|
+
}
|
|
40
|
+
export { hex as bytesToHex, fromHex as hexToBytes };
|
|
41
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElD,SAAS,GAAG,CAAC,KAAiB;IAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACnB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACtB,MAAM,KAAK,GAAG,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAQD,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,eAAe;IACjC,MAAM,IAAI,GAAG,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,WAAW;IAClD,MAAM,GAAG,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC1C,OAAO;QACH,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC;QACxB,YAAY,EAAE,GAAG,CAAC,GAAG,CAAC;QACtB,UAAU,EAAE,gBAAgB,CAAC,GAAG,CAAC;KACpC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IAC/C,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9C,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACd,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACnB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IAClE,OAAO,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,OAAO,IAAI,UAAU,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function main(argv?: string[]): Promise<void>;
|
|
2
|
+
export { loadCreds, saveCreds, clearCreds, credsPath } from "./auth.js";
|
|
3
|
+
export { loginFlow } from "./login.js";
|
|
4
|
+
export { runBridge } from "./bridge.js";
|
|
5
|
+
export type { MemWalCredentials } from "./auth.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2FA,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA2GhF;AA+DD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemWal MCP — orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Boot sequence:
|
|
5
|
+
* 1. If `--logout` flag → wipe credentials.json and exit.
|
|
6
|
+
* 2. Load credentials from `~/.memwal/credentials.json`.
|
|
7
|
+
* 3. If missing → run `loginFlow()` (browser-based wallet sign-in).
|
|
8
|
+
* 4. Bridge stdio MCP ↔ remote SSE relayer using the loaded credentials.
|
|
9
|
+
* 5. On 401 (revoked key), the bridge wipes credentials before throwing
|
|
10
|
+
* — the next process spawn will re-trigger login.
|
|
11
|
+
*/
|
|
12
|
+
import { clearCreds, credsPath, loadCreds } from "./auth.js";
|
|
13
|
+
import { runAuthRequiredServer } from "./auth-required.js";
|
|
14
|
+
import { runBridge } from "./bridge.js";
|
|
15
|
+
import { loginFlow } from "./login.js";
|
|
16
|
+
import { log, note } from "./logger.js";
|
|
17
|
+
/** Per-environment URL shortcuts. `--dev`/`--staging`/`--local` set both
|
|
18
|
+
* relayer + web in one flag. Explicit `--relayer` / `--web-url` override. */
|
|
19
|
+
const ENV_PRESETS = {
|
|
20
|
+
prod: { relayer: "https://relayer.memwal.ai", web: "https://memwal.ai" },
|
|
21
|
+
dev: { relayer: "https://relayer.dev.memwal.ai", web: "https://dev.memwal.ai" },
|
|
22
|
+
staging: { relayer: "https://relayer.staging.memwal.ai", web: "https://staging.memwal.ai" },
|
|
23
|
+
local: { relayer: "http://127.0.0.1:8000", web: "http://localhost:5173" },
|
|
24
|
+
};
|
|
25
|
+
function parseArgs(argv) {
|
|
26
|
+
const out = { help: false, logout: false, forceLogin: false };
|
|
27
|
+
for (let i = 0; i < argv.length; i++) {
|
|
28
|
+
const a = argv[i];
|
|
29
|
+
const next = () => argv[++i];
|
|
30
|
+
switch (a) {
|
|
31
|
+
case "--help":
|
|
32
|
+
case "-h":
|
|
33
|
+
out.help = true;
|
|
34
|
+
break;
|
|
35
|
+
case "--logout":
|
|
36
|
+
out.logout = true;
|
|
37
|
+
break;
|
|
38
|
+
case "--login":
|
|
39
|
+
case "login":
|
|
40
|
+
out.forceLogin = true;
|
|
41
|
+
break;
|
|
42
|
+
case "--prod":
|
|
43
|
+
case "--dev":
|
|
44
|
+
case "--staging":
|
|
45
|
+
case "--local": {
|
|
46
|
+
const preset = ENV_PRESETS[a.slice(2)];
|
|
47
|
+
if (preset) {
|
|
48
|
+
out.relayerUrl ??= preset.relayer;
|
|
49
|
+
out.webUrl ??= preset.web;
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "--relayer":
|
|
54
|
+
case "--relayer-url":
|
|
55
|
+
out.relayerUrl = next();
|
|
56
|
+
break;
|
|
57
|
+
case "--web-url":
|
|
58
|
+
case "--web":
|
|
59
|
+
out.webUrl = next();
|
|
60
|
+
break;
|
|
61
|
+
case "--label":
|
|
62
|
+
out.label = next();
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
// Allow `--relayer=URL` and `--web-url=URL` forms too.
|
|
66
|
+
if (a?.startsWith("--relayer="))
|
|
67
|
+
out.relayerUrl = a.split("=", 2)[1];
|
|
68
|
+
else if (a?.startsWith("--web-url="))
|
|
69
|
+
out.webUrl = a.split("=", 2)[1];
|
|
70
|
+
else if (a?.startsWith("--label="))
|
|
71
|
+
out.label = a.split("=", 2)[1];
|
|
72
|
+
// Unknown flag: ignore silently.
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
79
|
+
const args = parseArgs(argv);
|
|
80
|
+
if (args.help) {
|
|
81
|
+
printHelp();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (args.logout) {
|
|
85
|
+
clearCreds();
|
|
86
|
+
note(`Credentials removed (${credsPath()}).`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (args.forceLogin) {
|
|
90
|
+
clearCreds();
|
|
91
|
+
}
|
|
92
|
+
// Resolve URLs: CLI > env > default.
|
|
93
|
+
const relayerUrl = args.relayerUrl ?? process.env.MEMWAL_SERVER_URL ?? "https://relayer.memwal.ai";
|
|
94
|
+
const webUrl = args.webUrl ?? process.env.MEMWAL_WEB_URL ?? "https://memwal.ai";
|
|
95
|
+
// Label is the on-chain delegate-key name shown in the dashboard. We
|
|
96
|
+
// default to a generic value at registration time — the bridge updates
|
|
97
|
+
// it to the actual client's `clientInfo.name` ("Cursor", "Claude",
|
|
98
|
+
// "Antigravity", ...) after the first MCP `initialize` request. User
|
|
99
|
+
// can rename anytime from the dashboard.
|
|
100
|
+
const label = args.label ?? process.env.MEMWAL_CLIENT_LABEL ?? "MCP Client";
|
|
101
|
+
let creds = loadCreds();
|
|
102
|
+
if (creds && args.relayerUrl && creds.relayerUrl !== args.relayerUrl) {
|
|
103
|
+
// Caller wants a different relayer than what's saved. NEVER silently
|
|
104
|
+
// mutate the saved relayerUrl — a malicious config snippet (e.g.
|
|
105
|
+
// copy-pasted from a forum) carrying `--relayer https://attacker`
|
|
106
|
+
// would otherwise rewrite the saved creds so even subsequent runs
|
|
107
|
+
// without the flag ship the seed to the attacker (audit H4).
|
|
108
|
+
//
|
|
109
|
+
// In-memory override is fine for THIS process — the saved file is
|
|
110
|
+
// left untouched, so the next spawn falls back to the saved URL.
|
|
111
|
+
log.warn("creds.relayer_override.transient_only", {
|
|
112
|
+
saved: creds.relayerUrl,
|
|
113
|
+
override: args.relayerUrl,
|
|
114
|
+
});
|
|
115
|
+
note(`--relayer flag (${args.relayerUrl}) overrides saved relayer ` +
|
|
116
|
+
`(${creds.relayerUrl}) for THIS process only. The saved file ` +
|
|
117
|
+
`is not modified. To rotate the saved relayer, run ` +
|
|
118
|
+
`\`memwal-mcp login --logout\` then a fresh login.`);
|
|
119
|
+
creds = { ...creds, relayerUrl: args.relayerUrl };
|
|
120
|
+
}
|
|
121
|
+
const wasLoggedIn = !!creds;
|
|
122
|
+
if (!creds) {
|
|
123
|
+
if (!process.stdin.isTTY) {
|
|
124
|
+
// Spawned by an MCP client (Cursor / Claude Desktop / etc.).
|
|
125
|
+
// Instead of exiting — which makes the client UI show "Failed to
|
|
126
|
+
// start" with no actionable next step — boot a minimal stdio MCP
|
|
127
|
+
// server that responds to `initialize` and `tools/list` but
|
|
128
|
+
// returns an `isError: true` envelope on every `tools/call` with
|
|
129
|
+
// a friendly login instruction. The user sees the message
|
|
130
|
+
// INLINE in their chat, not buried in stderr logs.
|
|
131
|
+
//
|
|
132
|
+
// Phase B.5 (see plans/memwal-mcp-package-with-login.md) will
|
|
133
|
+
// replace this with the MCP OAuth flow so the client's host
|
|
134
|
+
// drives the browser dance and retries the tool call
|
|
135
|
+
// automatically — no client restart required.
|
|
136
|
+
log.warn("creds.missing_at_spawn.serving_auth_required", {
|
|
137
|
+
credsPath: credsPath(),
|
|
138
|
+
});
|
|
139
|
+
await runAuthRequiredServer();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// TTY = manual invocation. Block on the browser flow as before.
|
|
143
|
+
note("MemWal MCP is not authorized yet — opening browser to connect your Sui wallet.");
|
|
144
|
+
creds = await loginFlow({ relayerUrl, webUrl, label });
|
|
145
|
+
note(`Authorized as ${creds.walletAddress.slice(0, 10)}...`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
log.info("creds.loaded", {
|
|
149
|
+
accountId: creds.accountId,
|
|
150
|
+
delegateAddress: creds.delegateAddress,
|
|
151
|
+
label: creds.label,
|
|
152
|
+
relayerUrl: creds.relayerUrl,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Manual invocation from a real terminal: print status and exit.
|
|
156
|
+
// Bridge mode only makes sense when an MCP client is attached on the
|
|
157
|
+
// other end of stdin (Cursor / Claude Desktop / ...). A TTY means the
|
|
158
|
+
// user is the one looking at stdout — there's no MCP client to bridge
|
|
159
|
+
// with, so hanging the process is the wrong default.
|
|
160
|
+
if (process.stdin.isTTY) {
|
|
161
|
+
note(``);
|
|
162
|
+
if (wasLoggedIn) {
|
|
163
|
+
note(`✅ Already authorized as ${creds.walletAddress.slice(0, 10)}...${creds.walletAddress.slice(-6)}`);
|
|
164
|
+
note(` Account: ${creds.accountId}`);
|
|
165
|
+
note(` Relayer: ${creds.relayerUrl}`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
note(`✅ Login complete. Credentials saved to ${credsPath()}`);
|
|
169
|
+
}
|
|
170
|
+
note(``);
|
|
171
|
+
note(`Next: add this package to your MCP client config (Cursor / Claude Desktop / etc).`);
|
|
172
|
+
note(`See \`memwal-mcp --help\` for ready-to-paste snippets.`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
await runBridge(creds);
|
|
176
|
+
}
|
|
177
|
+
function printHelp() {
|
|
178
|
+
const help = [
|
|
179
|
+
"memwal-mcp — MemWal Model Context Protocol client",
|
|
180
|
+
"",
|
|
181
|
+
"Usage:",
|
|
182
|
+
" memwal-mcp Run the MCP stdio server (default).",
|
|
183
|
+
" Triggers a one-time browser login",
|
|
184
|
+
" if ~/.memwal/credentials.json is",
|
|
185
|
+
" missing.",
|
|
186
|
+
" memwal-mcp login Force re-authentication (wipes",
|
|
187
|
+
" existing credentials and opens",
|
|
188
|
+
" browser).",
|
|
189
|
+
" memwal-mcp --logout Delete saved credentials without",
|
|
190
|
+
" re-running login.",
|
|
191
|
+
" memwal-mcp --help Show this help.",
|
|
192
|
+
"",
|
|
193
|
+
"Options:",
|
|
194
|
+
" --relayer <url> Override the relayer base URL.",
|
|
195
|
+
" Default: https://relayer.memwal.ai",
|
|
196
|
+
" (or saved value from credentials).",
|
|
197
|
+
" --web-url <url> Override the dashboard URL the",
|
|
198
|
+
" browser opens during login.",
|
|
199
|
+
" Default: https://memwal.ai",
|
|
200
|
+
" --label <text> Friendly delegate-key label",
|
|
201
|
+
" registered on-chain. Default:",
|
|
202
|
+
' "MemWal MCP"',
|
|
203
|
+
"",
|
|
204
|
+
"Environment (equivalent to options):",
|
|
205
|
+
" MEMWAL_SERVER_URL same as --relayer",
|
|
206
|
+
" MEMWAL_WEB_URL same as --web-url",
|
|
207
|
+
" MEMWAL_CLIENT_LABEL same as --label",
|
|
208
|
+
" MEMWAL_MCP_DEBUG=1 Verbose stderr logging.",
|
|
209
|
+
"",
|
|
210
|
+
"Minimal MCP client config (Cursor, Claude Desktop, etc.):",
|
|
211
|
+
" {",
|
|
212
|
+
' "mcpServers": {',
|
|
213
|
+
' "memwal": {',
|
|
214
|
+
' "command": "npx",',
|
|
215
|
+
' "args": ["-y", "@mysten-incubation/memwal-mcp"]',
|
|
216
|
+
" }",
|
|
217
|
+
" }",
|
|
218
|
+
" }",
|
|
219
|
+
"",
|
|
220
|
+
"With explicit relayer override (e.g. dev environment):",
|
|
221
|
+
" {",
|
|
222
|
+
' "mcpServers": {',
|
|
223
|
+
' "memwal": {',
|
|
224
|
+
' "command": "npx",',
|
|
225
|
+
' "args": [',
|
|
226
|
+
' "-y", "@mysten-incubation/memwal-mcp",',
|
|
227
|
+
' "--relayer", "https://relayer.dev.memwal.ai"',
|
|
228
|
+
" ]",
|
|
229
|
+
" }",
|
|
230
|
+
" }",
|
|
231
|
+
" }",
|
|
232
|
+
"",
|
|
233
|
+
].join("\n");
|
|
234
|
+
process.stderr.write(help + "\n");
|
|
235
|
+
}
|
|
236
|
+
// Re-exports — handy if someone wants to embed this in another tool.
|
|
237
|
+
export { loadCreds, saveCreds, clearCreds, credsPath } from "./auth.js";
|
|
238
|
+
export { loginFlow } from "./login.js";
|
|
239
|
+
export { runBridge } from "./bridge.js";
|
|
240
|
+
//# sourceMappingURL=index.js.map
|