paralith-mcp 0.2.0 → 0.3.1
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/README.md +10 -2
- package/dist/chains.json +94 -43
- package/dist/client.mjs +1 -1
- package/dist/graph.json +2 -2
- package/dist/http.mjs +215 -31
- package/dist/server.mjs +644 -149
- package/package.json +1 -1
package/dist/server.mjs
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res, err) => function __init() {
|
|
5
|
+
if (err) throw err[0];
|
|
6
|
+
try {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
throw err = [e], e;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
10
16
|
|
|
11
17
|
// ../../backend/native/ans104.mjs
|
|
12
18
|
import crypto from "node:crypto";
|
|
13
|
-
var enc = new TextEncoder();
|
|
14
|
-
var sha384 = (b) => crypto.createHash("sha384").update(b).digest();
|
|
15
|
-
var sha256 = (b) => crypto.createHash("sha256").update(b).digest();
|
|
16
|
-
var b64url = (b) => Buffer.from(b).toString("base64url");
|
|
17
|
-
var b64urlDec = (s) => Buffer.from(s, "base64url");
|
|
18
19
|
function deepHash(value) {
|
|
19
20
|
if (Array.isArray(value)) {
|
|
20
21
|
const tag2 = enc.encode(`list${value.length}`);
|
|
@@ -102,7 +103,6 @@ async function signDataItem(jwk, item) {
|
|
|
102
103
|
data
|
|
103
104
|
]);
|
|
104
105
|
}
|
|
105
|
-
var jwkToAddress = (jwk) => b64url(sha256(b64urlDec(jwk.n)));
|
|
106
106
|
function walletShim(jwk) {
|
|
107
107
|
const address = jwkToAddress(jwk);
|
|
108
108
|
return {
|
|
@@ -122,6 +122,18 @@ function walletShim(jwk) {
|
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
|
+
var enc, sha384, sha256, b64url, b64urlDec, jwkToAddress;
|
|
126
|
+
var init_ans104 = __esm({
|
|
127
|
+
"../../backend/native/ans104.mjs"() {
|
|
128
|
+
"use strict";
|
|
129
|
+
enc = new TextEncoder();
|
|
130
|
+
sha384 = (b) => crypto.createHash("sha384").update(b).digest();
|
|
131
|
+
sha256 = (b) => crypto.createHash("sha256").update(b).digest();
|
|
132
|
+
b64url = (b) => Buffer.from(b).toString("base64url");
|
|
133
|
+
b64urlDec = (s) => Buffer.from(s, "base64url");
|
|
134
|
+
jwkToAddress = (jwk) => b64url(sha256(b64urlDec(jwk.n)));
|
|
135
|
+
}
|
|
136
|
+
});
|
|
125
137
|
|
|
126
138
|
// src/keys.mjs
|
|
127
139
|
import crypto2 from "node:crypto";
|
|
@@ -134,7 +146,6 @@ import { mnemonicToAccount } from "viem/accounts";
|
|
|
134
146
|
import { Keypair } from "@solana/web3.js";
|
|
135
147
|
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
|
|
136
148
|
import { getKeyFromMnemonic } from "arweave-mnemonic-keys";
|
|
137
|
-
var CACHE_DIR = process.env.PARALITH_MCP_HOME || path.join(os.homedir(), ".paralith", "mcp");
|
|
138
149
|
function readPhrase(env = process.env) {
|
|
139
150
|
const raw = env.PARALITH_MNEMONIC ?? (env.PARALITH_MNEMONIC_FILE ? fs.readFileSync(env.PARALITH_MNEMONIC_FILE, "utf8") : null);
|
|
140
151
|
if (!raw) {
|
|
@@ -161,60 +172,260 @@ function slip10(seed, derivation) {
|
|
|
161
172
|
async function aoJwk(phrase, env) {
|
|
162
173
|
if (env.PARALITH_AO_JWK) return JSON.parse(fs.readFileSync(env.PARALITH_AO_JWK, "utf8"));
|
|
163
174
|
const id = crypto2.createHash("sha256").update(phrase).digest("hex").slice(0, 24);
|
|
164
|
-
const file = path.join(
|
|
175
|
+
const file = path.join(HOME, `ao-${id}.json`);
|
|
165
176
|
if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
166
177
|
const jwk = await getKeyFromMnemonic(phrase);
|
|
167
|
-
fs.mkdirSync(
|
|
178
|
+
fs.mkdirSync(HOME, { recursive: true, mode: 448 });
|
|
168
179
|
fs.writeFileSync(file, JSON.stringify(jwk), { mode: 384 });
|
|
169
180
|
return jwk;
|
|
170
181
|
}
|
|
171
|
-
|
|
182
|
+
function chainWallets(phrase) {
|
|
172
183
|
const evm = mnemonicToAccount(phrase, { path: "m/44'/60'/0'/0/0" });
|
|
173
184
|
const solana = Keypair.fromSeed(slip10(mnemonicToSeedSync(phrase), "m/44'/501'/0'/0'"));
|
|
174
185
|
const sui = Ed25519Keypair.deriveKeypair(phrase, "m/44'/784'/0'/0'/0'");
|
|
175
|
-
const jwk = await aoJwk(phrase, env);
|
|
176
186
|
return {
|
|
177
|
-
ao: { jwk, address: jwkToAddress(jwk) },
|
|
178
187
|
base: { account: evm, address: evm.address },
|
|
179
188
|
solana: { keypair: solana, address: solana.publicKey.toBase58() },
|
|
180
189
|
sui: { keypair: sui, address: sui.getPublicKey().toSuiAddress() }
|
|
181
190
|
};
|
|
182
191
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
async function deriveWallets(phrase, env = process.env) {
|
|
193
|
+
const jwk = await aoJwk(phrase, env);
|
|
194
|
+
return { ao: { jwk, address: jwkToAddress(jwk) }, ...chainWallets(phrase) };
|
|
195
|
+
}
|
|
196
|
+
var HOME, publicAddresses;
|
|
197
|
+
var init_keys = __esm({
|
|
198
|
+
"src/keys.mjs"() {
|
|
199
|
+
"use strict";
|
|
200
|
+
init_ans104();
|
|
201
|
+
HOME = process.env.PARALITH_MCP_HOME || path.join(os.homedir(), ".paralith", "mcp");
|
|
202
|
+
publicAddresses = (w) => ({
|
|
203
|
+
ao: w.ao.address,
|
|
204
|
+
base: w.base.address,
|
|
205
|
+
solana: w.solana.address,
|
|
206
|
+
sui: w.sui.address
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// src/agents.mjs
|
|
212
|
+
import fs2 from "node:fs";
|
|
213
|
+
import path2 from "node:path";
|
|
214
|
+
function readAgents() {
|
|
215
|
+
try {
|
|
216
|
+
return JSON.parse(fs2.readFileSync(FILE, "utf8"));
|
|
217
|
+
} catch {
|
|
218
|
+
return {};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function saveAgent(name, entry) {
|
|
222
|
+
const all = readAgents();
|
|
223
|
+
all[name] = { ...all[name], ...entry, addresses: { ...all[name]?.addresses, ...entry.addresses } };
|
|
224
|
+
fs2.mkdirSync(HOME, { recursive: true, mode: 448 });
|
|
225
|
+
fs2.writeFileSync(FILE, `${JSON.stringify(all, null, 2)}
|
|
226
|
+
`);
|
|
227
|
+
return all[name];
|
|
228
|
+
}
|
|
229
|
+
var FILE;
|
|
230
|
+
var init_agents = __esm({
|
|
231
|
+
"src/agents.mjs"() {
|
|
232
|
+
"use strict";
|
|
233
|
+
init_keys();
|
|
234
|
+
FILE = path2.join(HOME, "agents.json");
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// src/setup.mjs
|
|
239
|
+
var setup_exports = {};
|
|
240
|
+
__export(setup_exports, {
|
|
241
|
+
setup: () => setup,
|
|
242
|
+
upsertToml: () => upsertToml
|
|
243
|
+
});
|
|
244
|
+
import fs3 from "node:fs";
|
|
245
|
+
import os2 from "node:os";
|
|
246
|
+
import path3 from "node:path";
|
|
247
|
+
import { spawnSync } from "node:child_process";
|
|
248
|
+
import { generateMnemonic } from "@scure/bip39";
|
|
249
|
+
import { wordlist as wordlist2 } from "@scure/bip39/wordlists/english.js";
|
|
250
|
+
function run(cli, args) {
|
|
251
|
+
const r = WIN ? spawnSync([cli, ...args].map((a) => /[\s"&|<>^]/.test(a) ? `"${a}"` : a).join(" "), { shell: true, encoding: "utf8" }) : spawnSync(cli, args, { encoding: "utf8" });
|
|
252
|
+
return { ok: r.status === 0, out: `${r.stdout ?? ""}${r.stderr ?? ""}`.trim() };
|
|
253
|
+
}
|
|
254
|
+
function desktopConfig() {
|
|
255
|
+
const dir = WIN ? path3.join(process.env.APPDATA ?? path3.join(os2.homedir(), "AppData", "Roaming"), "Claude") : process.platform === "darwin" ? path3.join(os2.homedir(), "Library", "Application Support", "Claude") : path3.join(os2.homedir(), ".config", "Claude");
|
|
256
|
+
return path3.join(dir, "claude_desktop_config.json");
|
|
257
|
+
}
|
|
258
|
+
function upsertToml(text, env) {
|
|
259
|
+
const kept = [];
|
|
260
|
+
let skipping = false;
|
|
261
|
+
for (const line of text.split(/\r?\n/)) {
|
|
262
|
+
const header = /^\s*\[/.test(line);
|
|
263
|
+
if (header) skipping = /^\s*\[\s*mcp_servers\.(paralith|"paralith")\s*[\].]/.test(line);
|
|
264
|
+
if (!skipping) kept.push(line);
|
|
265
|
+
}
|
|
266
|
+
const s = JSON.stringify;
|
|
267
|
+
const block = [
|
|
268
|
+
"[mcp_servers.paralith]",
|
|
269
|
+
`command = ${s(LAUNCH.command)}`,
|
|
270
|
+
`args = [${LAUNCH.args.map(s).join(", ")}]`,
|
|
271
|
+
"startup_timeout_sec = 120",
|
|
272
|
+
// first run downloads the package
|
|
273
|
+
"tool_timeout_sec = 1200",
|
|
274
|
+
// a bridge deposit waits for the relayer
|
|
275
|
+
"",
|
|
276
|
+
"[mcp_servers.paralith.env]",
|
|
277
|
+
...Object.entries(env).map(([k, v]) => `${k} = ${s(v)}`)
|
|
278
|
+
];
|
|
279
|
+
const body = kept.join("\n").replace(/\s+$/, "");
|
|
280
|
+
return `${body ? `${body}
|
|
281
|
+
|
|
282
|
+
` : ""}${block.join("\n")}
|
|
283
|
+
`;
|
|
284
|
+
}
|
|
285
|
+
function writeToml(file, env) {
|
|
286
|
+
fs3.mkdirSync(path3.dirname(file), { recursive: true });
|
|
287
|
+
fs3.writeFileSync(file, upsertToml(fs3.existsSync(file) ? fs3.readFileSync(file, "utf8") : "", env));
|
|
288
|
+
}
|
|
289
|
+
function phraseFile(agent) {
|
|
290
|
+
const file = path3.join(HOME, "agents", `${agent}.txt`);
|
|
291
|
+
if (!fs3.existsSync(file)) {
|
|
292
|
+
fs3.mkdirSync(path3.dirname(file), { recursive: true, mode: 448 });
|
|
293
|
+
fs3.writeFileSync(file, `${generateMnemonic(wordlist2, 256)}
|
|
294
|
+
`, { mode: 384 });
|
|
295
|
+
}
|
|
296
|
+
return file;
|
|
297
|
+
}
|
|
298
|
+
async function setup(argv) {
|
|
299
|
+
const only = argv.find((a) => a.startsWith("--only"))?.split("=")[1]?.split(",") ?? (argv.includes("--only") ? argv[argv.indexOf("--only") + 1]?.split(",") : null);
|
|
300
|
+
const unknown = (only ?? []).filter((c) => !CLIENTS[c]);
|
|
301
|
+
if (unknown.length) throw new Error(`Unknown app: ${unknown.join(", ")}. Choose from ${Object.keys(CLIENTS).join(", ")}.`);
|
|
302
|
+
const done = [];
|
|
303
|
+
for (const [id, client] of Object.entries(CLIENTS)) {
|
|
304
|
+
if (only ? !only.includes(id) : !client.found()) continue;
|
|
305
|
+
const file = phraseFile(client.wallet);
|
|
306
|
+
const chain = chainWallets(readPhrase({ PARALITH_MNEMONIC_FILE: file }));
|
|
307
|
+
saveAgent(client.wallet, {
|
|
308
|
+
phraseFile: slash(file),
|
|
309
|
+
addresses: { base: chain.base.address, solana: chain.solana.address, sui: chain.sui.address }
|
|
310
|
+
});
|
|
311
|
+
try {
|
|
312
|
+
client.connect({ PARALITH_MNEMONIC_FILE: slash(file), PARALITH_AGENT: client.wallet });
|
|
313
|
+
done.push(client.label);
|
|
314
|
+
console.log(`\u2713 ${client.label} connected (wallet: ${client.wallet})`);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.log(`\u2717 ${client.label}: ${error.message.split("\n")[0]}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (!done.length) {
|
|
320
|
+
console.log("No agent app found. Install Claude Code, Claude Desktop, Codex or Grok, or name one with --only.");
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const agents = readAgents();
|
|
324
|
+
console.log("\nFund these to bridge (gas only; playing and trading need nothing):");
|
|
325
|
+
for (const name of new Set(Object.values(CLIENTS).filter((c) => done.includes(c.label)).map((c) => c.wallet))) {
|
|
326
|
+
const a = agents[name].addresses;
|
|
327
|
+
console.log(`
|
|
328
|
+
${name}
|
|
329
|
+
Base (ETH) ${a.base}
|
|
330
|
+
Solana (SOL) ${a.solana}
|
|
331
|
+
Sui (SUI) ${a.sui}`);
|
|
332
|
+
}
|
|
333
|
+
console.log(`
|
|
334
|
+
Phrases: ${slash(path3.join(HOME, "agents"))} (back them up; anything funded is spendable by the agent).`);
|
|
335
|
+
console.log(`Restart ${done.join(", ")}, then say: "Play Rune Realm and show me." Ask any of them "how are my agents doing?"`);
|
|
336
|
+
}
|
|
337
|
+
var WIN, NPX, LAUNCH, slash, installed, CLIENTS;
|
|
338
|
+
var init_setup = __esm({
|
|
339
|
+
"src/setup.mjs"() {
|
|
340
|
+
"use strict";
|
|
341
|
+
init_keys();
|
|
342
|
+
init_agents();
|
|
343
|
+
WIN = process.platform === "win32";
|
|
344
|
+
NPX = ["npx", "-y", "paralith-mcp@latest"];
|
|
345
|
+
LAUNCH = WIN ? { command: "cmd", args: ["/c", ...NPX] } : { command: NPX[0], args: NPX.slice(1) };
|
|
346
|
+
slash = (p) => p.replace(/\\/g, "/");
|
|
347
|
+
installed = (cli) => run(cli, ["--version"]).ok;
|
|
348
|
+
CLIENTS = {
|
|
349
|
+
claude: {
|
|
350
|
+
label: "Claude Code",
|
|
351
|
+
wallet: "claude",
|
|
352
|
+
found: () => installed("claude"),
|
|
353
|
+
connect(env) {
|
|
354
|
+
run("claude", ["mcp", "remove", "paralith", "--scope", "user"]);
|
|
355
|
+
const r = run("claude", [
|
|
356
|
+
"mcp",
|
|
357
|
+
"add",
|
|
358
|
+
"paralith",
|
|
359
|
+
"--scope",
|
|
360
|
+
"user",
|
|
361
|
+
...Object.entries(env).flatMap(([k, v]) => ["-e", `${k}=${v}`]),
|
|
362
|
+
"--",
|
|
363
|
+
LAUNCH.command,
|
|
364
|
+
...LAUNCH.args
|
|
365
|
+
]);
|
|
366
|
+
if (!r.ok) throw new Error(r.out);
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
"claude-desktop": {
|
|
370
|
+
label: "Claude Desktop",
|
|
371
|
+
wallet: "claude",
|
|
372
|
+
found: () => fs3.existsSync(path3.dirname(desktopConfig())),
|
|
373
|
+
connect(env) {
|
|
374
|
+
const file = desktopConfig();
|
|
375
|
+
const config = fs3.existsSync(file) ? JSON.parse(fs3.readFileSync(file, "utf8")) : {};
|
|
376
|
+
config.mcpServers = { ...config.mcpServers, paralith: { ...LAUNCH, env } };
|
|
377
|
+
fs3.writeFileSync(file, `${JSON.stringify(config, null, 2)}
|
|
378
|
+
`);
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
codex: {
|
|
382
|
+
label: "Codex",
|
|
383
|
+
wallet: "codex",
|
|
384
|
+
found: () => installed("codex") || fs3.existsSync(path3.join(os2.homedir(), ".codex")),
|
|
385
|
+
connect: (env) => writeToml(path3.join(os2.homedir(), ".codex", "config.toml"), env)
|
|
386
|
+
},
|
|
387
|
+
grok: {
|
|
388
|
+
label: "Grok",
|
|
389
|
+
wallet: "grok",
|
|
390
|
+
found: () => installed("grok") || fs3.existsSync(path3.join(os2.homedir(), ".grok")),
|
|
391
|
+
connect: (env) => writeToml(path3.join(os2.homedir(), ".grok", "config.toml"), env)
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
188
395
|
});
|
|
189
396
|
|
|
190
397
|
// src/runtime.mjs
|
|
191
|
-
var
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
398
|
+
var runtime_exports = {};
|
|
399
|
+
__export(runtime_exports, {
|
|
400
|
+
ID: () => ID,
|
|
401
|
+
NODE: () => NODE,
|
|
402
|
+
PROCESS_ALIASES: () => PROCESS_ALIASES,
|
|
403
|
+
READ_ONLY: () => READ_ONLY,
|
|
404
|
+
chains: () => chains,
|
|
405
|
+
formatAtoms: () => formatAtoms,
|
|
406
|
+
graph: () => graph,
|
|
407
|
+
parseAmount: () => parseAmount,
|
|
408
|
+
processId: () => processId,
|
|
409
|
+
readAtoms: () => readAtoms,
|
|
410
|
+
readKey: () => readKey,
|
|
411
|
+
runtime: () => runtime,
|
|
412
|
+
sleep: () => sleep,
|
|
413
|
+
until: () => until
|
|
414
|
+
});
|
|
415
|
+
import fs4 from "node:fs";
|
|
416
|
+
import path4 from "node:path";
|
|
417
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
206
418
|
function processId(nameOrId) {
|
|
207
419
|
if (ID.test(nameOrId)) return nameOrId;
|
|
208
420
|
const id = PROCESS_ALIASES[nameOrId];
|
|
209
421
|
if (!id) throw new Error(`Unknown process "${nameOrId}". Use a 43-character id or one of: ${Object.keys(PROCESS_ALIASES).join(", ")}`);
|
|
210
422
|
return id;
|
|
211
423
|
}
|
|
212
|
-
var READ_ONLY = !process.env.PARALITH_MNEMONIC && !process.env.PARALITH_MNEMONIC_FILE;
|
|
213
|
-
var loaded = null;
|
|
214
424
|
function runtime() {
|
|
215
425
|
loaded ??= (async () => {
|
|
216
426
|
const wallets = READ_ONLY ? null : await deriveWallets(readPhrase());
|
|
217
|
-
|
|
427
|
+
if (wallets && process.env.PARALITH_AGENT) saveAgent(process.env.PARALITH_AGENT, { addresses: publicAddresses(wallets) });
|
|
428
|
+
const api = await import(pathToFileURL(path4.join(DIST, "client.mjs")).href);
|
|
218
429
|
if (wallets) api.swarmWalletSlot.wallet = walletShim(wallets.ao.jwk);
|
|
219
430
|
return { api, wallets };
|
|
220
431
|
})();
|
|
@@ -223,7 +434,6 @@ function runtime() {
|
|
|
223
434
|
});
|
|
224
435
|
return loaded;
|
|
225
436
|
}
|
|
226
|
-
var HTML = /^\s*(?:<!doctype\s+html|<html|<head|<body)/i;
|
|
227
437
|
async function readKey(process2, key, node = NODE) {
|
|
228
438
|
const res = await fetch(
|
|
229
439
|
`${node}/${processId(process2)}~process@1.0/now/${key}`,
|
|
@@ -263,7 +473,6 @@ function parseAmount(text, decimals = 6) {
|
|
|
263
473
|
if (atoms <= 0n) throw new Error("Amount must be positive");
|
|
264
474
|
return atoms;
|
|
265
475
|
}
|
|
266
|
-
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
267
476
|
async function until(what, read, done, timeoutMs = 6e5, everyMs = 3e3) {
|
|
268
477
|
const deadline = Date.now() + timeoutMs;
|
|
269
478
|
for (; ; ) {
|
|
@@ -273,19 +482,58 @@ async function until(what, read, done, timeoutMs = 6e5, everyMs = 3e3) {
|
|
|
273
482
|
await sleep(everyMs);
|
|
274
483
|
}
|
|
275
484
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
485
|
+
var HERE, DIST, graph, NODE, chains, PROCESS_ALIASES, ID, READ_ONLY, loaded, HTML, sleep;
|
|
486
|
+
var init_runtime = __esm({
|
|
487
|
+
"src/runtime.mjs"() {
|
|
488
|
+
"use strict";
|
|
489
|
+
init_ans104();
|
|
490
|
+
init_keys();
|
|
491
|
+
init_agents();
|
|
492
|
+
HERE = path4.dirname(fileURLToPath(import.meta.url));
|
|
493
|
+
DIST = fs4.existsSync(path4.join(HERE, "graph.json")) ? HERE : path4.join(HERE, "..", "dist");
|
|
494
|
+
graph = JSON.parse(fs4.readFileSync(path4.join(DIST, "graph.json"), "utf8"));
|
|
495
|
+
NODE = graph.node.replace(/\/$/, "");
|
|
496
|
+
chains = JSON.parse(fs4.readFileSync(path4.join(DIST, "chains.json"), "utf8"));
|
|
497
|
+
PROCESS_ALIASES = {
|
|
498
|
+
game: graph.processes.game,
|
|
499
|
+
rune: graph.processes.rune,
|
|
500
|
+
pusd: graph.processes.pusd,
|
|
501
|
+
...graph.processes.dao ? { dao: graph.processes.dao } : {},
|
|
502
|
+
"game-venue": graph.processes.internalVenue,
|
|
503
|
+
"token-venue": graph.processes.externalVenue,
|
|
504
|
+
...Object.fromEntries(Object.entries(graph.processes.elements ?? {}).map(([k, v]) => [`${k}-token`, v]))
|
|
505
|
+
};
|
|
506
|
+
ID = /^[A-Za-z0-9_-]{43}$/;
|
|
507
|
+
READ_ONLY = !process.env.PARALITH_MNEMONIC && !process.env.PARALITH_MNEMONIC_FILE;
|
|
508
|
+
loaded = null;
|
|
509
|
+
HTML = /^\s*(?:<!doctype\s+html|<html|<head|<body)/i;
|
|
510
|
+
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
511
|
+
}
|
|
512
|
+
});
|
|
280
513
|
|
|
281
514
|
// src/tool.mjs
|
|
282
515
|
import { z } from "zod";
|
|
283
|
-
function
|
|
516
|
+
async function migrationRefusal(read = readKey, now = Date.now()) {
|
|
517
|
+
if (!migrationSeen || now - migrationSeen.at > 15e3) {
|
|
518
|
+
migrationSeen = { at: now, value: await read("game", "migration").catch(() => null) };
|
|
519
|
+
}
|
|
520
|
+
const m = migrationSeen.value;
|
|
521
|
+
if (!m || typeof m !== "object" || m.phase === "live") return null;
|
|
522
|
+
if (m.phase === "retired") {
|
|
523
|
+
return `Paralith has moved to new processes (the game is now ${m.successor}). Nothing was sent. Update paralith-mcp to its latest version to use them.`;
|
|
524
|
+
}
|
|
525
|
+
return "A migration is in progress: Paralith is paused while every balance moves to new processes. Nothing was sent, and nothing is lost. Reads still work; try writes again later.";
|
|
526
|
+
}
|
|
527
|
+
function tool(server, name, description, inputSchema, run2, annotations = {}, view = null) {
|
|
284
528
|
if (READ_ONLY && annotations !== READ) return;
|
|
285
529
|
const schema = view ? { ...inputSchema, full: z.boolean().optional().describe("Return the raw record instead of the compact view.") } : inputSchema;
|
|
286
530
|
server.registerTool(name, { description, inputSchema: schema, annotations }, async (args) => {
|
|
287
531
|
try {
|
|
288
|
-
|
|
532
|
+
if (annotations === WRITE) {
|
|
533
|
+
const refusal = await migrationRefusal();
|
|
534
|
+
if (refusal) return { isError: true, content: [{ type: "text", text: refusal }] };
|
|
535
|
+
}
|
|
536
|
+
const raw = await run2(args ?? {});
|
|
289
537
|
const value = view && !args?.full ? await view(raw) : raw;
|
|
290
538
|
const text = typeof value === "string" ? value : JSON.stringify(value, bigints, 2);
|
|
291
539
|
return { content: [{ type: "text", text: text ?? "null" }] };
|
|
@@ -294,7 +542,6 @@ function tool(server, name, description, inputSchema, run, annotations = {}, vie
|
|
|
294
542
|
}
|
|
295
543
|
});
|
|
296
544
|
}
|
|
297
|
-
var bigints = (_k, v) => typeof v === "bigint" ? v.toString() : v;
|
|
298
545
|
function explain(error) {
|
|
299
546
|
const name = error?.name ?? "";
|
|
300
547
|
const message = error?.message ?? String(error);
|
|
@@ -305,16 +552,20 @@ The message WAS accepted (slot ${error?.slot ?? "?"}); its result could not be c
|
|
|
305
552
|
}
|
|
306
553
|
return message;
|
|
307
554
|
}
|
|
308
|
-
var
|
|
309
|
-
var
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
555
|
+
var migrationSeen, bigints, READ, OWN, WRITE;
|
|
556
|
+
var init_tool = __esm({
|
|
557
|
+
"src/tool.mjs"() {
|
|
558
|
+
"use strict";
|
|
559
|
+
init_runtime();
|
|
560
|
+
migrationSeen = null;
|
|
561
|
+
bigints = (_k, v) => typeof v === "bigint" ? v.toString() : v;
|
|
562
|
+
READ = { readOnlyHint: true, openWorldHint: true };
|
|
563
|
+
OWN = { readOnlyHint: true, openWorldHint: true };
|
|
564
|
+
WRITE = { readOnlyHint: false, destructiveHint: false, openWorldHint: true };
|
|
565
|
+
}
|
|
566
|
+
});
|
|
314
567
|
|
|
315
568
|
// src/views.mjs
|
|
316
|
-
var iso = (ms) => ms ? new Date(Number(ms)).toISOString() : void 0;
|
|
317
|
-
var uses = (moves) => Object.fromEntries(Object.entries(moves ?? {}).map(([n, m]) => [n, m?.count ?? m]));
|
|
318
569
|
function companion(m) {
|
|
319
570
|
if (!m) return void 0;
|
|
320
571
|
return {
|
|
@@ -332,7 +583,6 @@ function companion(m) {
|
|
|
332
583
|
...m.status?.until_time > Date.now() ? { busyUntil: iso(m.status.until_time) } : {}
|
|
333
584
|
};
|
|
334
585
|
}
|
|
335
|
-
var EXTRAS = ["rewards", "lootResult", "dailyClaimed", "economyResult", "withdrawal", "arenaLast", "lastHunt", "listing", "error"];
|
|
336
586
|
function player(p) {
|
|
337
587
|
if (!p) return p;
|
|
338
588
|
const companions = { ...p.monsters ?? {}, ...p.collection ?? {} };
|
|
@@ -401,19 +651,27 @@ function battle(b, address) {
|
|
|
401
651
|
recentTurns: turns
|
|
402
652
|
};
|
|
403
653
|
}
|
|
404
|
-
function hunt(
|
|
405
|
-
if (!
|
|
406
|
-
const { battle: fight, encounter, ...rest } =
|
|
654
|
+
function hunt(run2) {
|
|
655
|
+
if (!run2 || typeof run2 !== "object") return run2;
|
|
656
|
+
const { battle: fight, encounter, ...rest } = run2;
|
|
407
657
|
return {
|
|
408
658
|
...rest,
|
|
409
659
|
...encounter ? { encounter: companion(encounter) } : {},
|
|
410
|
-
...fight ? { battle: battle(fight,
|
|
660
|
+
...fight ? { battle: battle(fight, run2.playerId) } : {}
|
|
411
661
|
};
|
|
412
662
|
}
|
|
663
|
+
var iso, uses, EXTRAS;
|
|
664
|
+
var init_views = __esm({
|
|
665
|
+
"src/views.mjs"() {
|
|
666
|
+
"use strict";
|
|
667
|
+
iso = (ms) => ms ? new Date(Number(ms)).toISOString() : void 0;
|
|
668
|
+
uses = (moves) => Object.fromEntries(Object.entries(moves ?? {}).map(([n, m]) => [n, m?.count ?? m]));
|
|
669
|
+
EXTRAS = ["rewards", "lootResult", "dailyClaimed", "economyResult", "withdrawal", "arenaLast", "lastHunt", "listing", "error"];
|
|
670
|
+
}
|
|
671
|
+
});
|
|
413
672
|
|
|
414
673
|
// src/game.mjs
|
|
415
|
-
|
|
416
|
-
var BERRIES = ["air_berry", "fire_berry", "rock_berry", "water_berry"];
|
|
674
|
+
import { z as z2 } from "zod";
|
|
417
675
|
async function me() {
|
|
418
676
|
const { api, wallets } = await runtime();
|
|
419
677
|
return { api, address: wallets?.ao.address };
|
|
@@ -538,7 +796,7 @@ function registerGame(server) {
|
|
|
538
796
|
tool(
|
|
539
797
|
server,
|
|
540
798
|
"game_daily",
|
|
541
|
-
"Claim the daily crate and Rune. Once per UTC day.",
|
|
799
|
+
"Claim the daily crate (and accrued Rune, pass holders only). Once per UTC day.",
|
|
542
800
|
{},
|
|
543
801
|
async () => (await me()).api.claimDaily(),
|
|
544
802
|
WRITE,
|
|
@@ -768,7 +1026,6 @@ function registerGame(server) {
|
|
|
768
1026
|
WRITE
|
|
769
1027
|
);
|
|
770
1028
|
}
|
|
771
|
-
var asPlayer = (r) => r?.address && r?.inventory ? player(r) : r;
|
|
772
1029
|
async function battleView(b) {
|
|
773
1030
|
return b?.id ? battle(b, (await me()).address) : b;
|
|
774
1031
|
}
|
|
@@ -784,16 +1041,26 @@ async function attackView(p) {
|
|
|
784
1041
|
...arenaLast ? { arenaLast } : {}
|
|
785
1042
|
};
|
|
786
1043
|
}
|
|
787
|
-
var huntView = (r) => r?.runId ? hunt(r) : asPlayer(r);
|
|
788
1044
|
function need(value, name) {
|
|
789
1045
|
if (value === void 0 || value === null || value === "") throw new Error(`${name} is required for this action`);
|
|
790
1046
|
return value;
|
|
791
1047
|
}
|
|
1048
|
+
var FACTIONS, BERRIES, asPlayer, huntView;
|
|
1049
|
+
var init_game = __esm({
|
|
1050
|
+
"src/game.mjs"() {
|
|
1051
|
+
"use strict";
|
|
1052
|
+
init_runtime();
|
|
1053
|
+
init_tool();
|
|
1054
|
+
init_views();
|
|
1055
|
+
FACTIONS = ["Sky Nomads", "Aqua Guardians", "Inferno Blades", "Stone Titans"];
|
|
1056
|
+
BERRIES = ["air_berry", "fire_berry", "rock_berry", "water_berry"];
|
|
1057
|
+
asPlayer = (r) => r?.address && r?.inventory ? player(r) : r;
|
|
1058
|
+
huntView = (r) => r?.runId ? hunt(r) : asPlayer(r);
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
792
1061
|
|
|
793
1062
|
// src/exchange.mjs
|
|
794
1063
|
import { z as z3 } from "zod";
|
|
795
|
-
var VENUE = z3.enum(["game", "token"]).describe("game: items vs Gold. token: element tokens vs TEST-RUNE.");
|
|
796
|
-
var venueProcess = (venue) => venue === "game" ? graph.processes.internalVenue : graph.processes.externalVenue;
|
|
797
1064
|
async function terms(venue) {
|
|
798
1065
|
const { api } = await runtime();
|
|
799
1066
|
const process2 = venueProcess(venue);
|
|
@@ -816,7 +1083,6 @@ function toWire(t, m, price, quantity) {
|
|
|
816
1083
|
if (perLot <= 0n) throw new Error("Price is below one quote atom per lot.");
|
|
817
1084
|
return { lots: (baseAtoms / lot).toString(), perLot: perLot.toString() };
|
|
818
1085
|
}
|
|
819
|
-
var human = (t, free) => Object.fromEntries(Object.entries(free ?? {}).map(([asset, atoms]) => [asset, formatAtoms(atoms, t.den(asset))]));
|
|
820
1086
|
function registerExchange(server) {
|
|
821
1087
|
tool(
|
|
822
1088
|
server,
|
|
@@ -971,6 +1237,17 @@ function registerExchange(server) {
|
|
|
971
1237
|
WRITE
|
|
972
1238
|
);
|
|
973
1239
|
}
|
|
1240
|
+
var VENUE, venueProcess, human;
|
|
1241
|
+
var init_exchange = __esm({
|
|
1242
|
+
"src/exchange.mjs"() {
|
|
1243
|
+
"use strict";
|
|
1244
|
+
init_runtime();
|
|
1245
|
+
init_tool();
|
|
1246
|
+
VENUE = z3.enum(["game", "token"]).describe("game: items vs Gold. token: element tokens vs TEST-RUNE.");
|
|
1247
|
+
venueProcess = (venue) => venue === "game" ? graph.processes.internalVenue : graph.processes.externalVenue;
|
|
1248
|
+
human = (t, free) => Object.fromEntries(Object.entries(free ?? {}).map(([asset, atoms]) => [asset, formatAtoms(atoms, t.den(asset))]));
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
974
1251
|
|
|
975
1252
|
// src/bridge.mjs
|
|
976
1253
|
import crypto3 from "node:crypto";
|
|
@@ -987,11 +1264,6 @@ import {
|
|
|
987
1264
|
} from "@solana/web3.js";
|
|
988
1265
|
import { SuiGrpcClient } from "@mysten/sui/grpc";
|
|
989
1266
|
import { Transaction as SuiTransaction, coinWithBalance } from "@mysten/sui/transactions";
|
|
990
|
-
var DAILY_FAUCET = 100000000n;
|
|
991
|
-
var DAY_MS = 864e5;
|
|
992
|
-
var CHAIN = z4.enum(["base", "solana", "sui"]);
|
|
993
|
-
var PUSD = graph.processes.pusd;
|
|
994
|
-
var faucetToken = (cfg) => (cfg.tokens ?? []).find((t) => t.faucet) ?? cfg.tokens?.[0];
|
|
995
1267
|
function baseChain(w) {
|
|
996
1268
|
const cfg = chains.base;
|
|
997
1269
|
const token = faucetToken(cfg);
|
|
@@ -1152,11 +1424,23 @@ function suiChain(w) {
|
|
|
1152
1424
|
}
|
|
1153
1425
|
};
|
|
1154
1426
|
}
|
|
1427
|
+
async function gasOf(name, address) {
|
|
1428
|
+
if (name === "base") {
|
|
1429
|
+
const rpc = process.env.PARALITH_BASE_RPC || chains.base.rpc;
|
|
1430
|
+
const pub = createPublicClient({ chain: { ...base, id: chains.base.chainId }, transport: http(rpc) });
|
|
1431
|
+
return `${formatEther(await pub.getBalance({ address }))} ETH`;
|
|
1432
|
+
}
|
|
1433
|
+
if (name === "solana") {
|
|
1434
|
+
const conn = new Connection(process.env.PARALITH_SOLANA_RPC || chains.solana.rpc, "confirmed");
|
|
1435
|
+
return `${await conn.getBalance(new PublicKey(address)) / 1e9} SOL`;
|
|
1436
|
+
}
|
|
1437
|
+
const client = new SuiGrpcClient({ network: chains.sui.network, baseUrl: process.env.PARALITH_SUI_RPC || chains.sui.rpc });
|
|
1438
|
+
return `${Number((await client.getBalance({ owner: address })).balance.balance) / 1e9} SUI`;
|
|
1439
|
+
}
|
|
1155
1440
|
async function chainFor(name) {
|
|
1156
1441
|
const { wallets } = await runtime();
|
|
1157
1442
|
return { base: baseChain, solana: solanaChain, sui: suiChain }[name](wallets);
|
|
1158
1443
|
}
|
|
1159
|
-
var pusdBalance = (address) => readAtoms(PUSD, `balance-${address}`);
|
|
1160
1444
|
function registerBridge(server) {
|
|
1161
1445
|
tool(
|
|
1162
1446
|
server,
|
|
@@ -1280,28 +1564,32 @@ function registerBridge(server) {
|
|
|
1280
1564
|
READ
|
|
1281
1565
|
);
|
|
1282
1566
|
}
|
|
1567
|
+
var DAILY_FAUCET, DAY_MS, CHAIN, PUSD, faucetToken, pusdBalance;
|
|
1568
|
+
var init_bridge = __esm({
|
|
1569
|
+
"src/bridge.mjs"() {
|
|
1570
|
+
"use strict";
|
|
1571
|
+
init_runtime();
|
|
1572
|
+
init_tool();
|
|
1573
|
+
DAILY_FAUCET = 100000000n;
|
|
1574
|
+
DAY_MS = 864e5;
|
|
1575
|
+
CHAIN = z4.enum(["base", "solana", "sui"]);
|
|
1576
|
+
PUSD = graph.processes.pusd;
|
|
1577
|
+
faucetToken = (cfg) => (cfg.tokens ?? []).find((t) => t.faucet) ?? cfg.tokens?.[0];
|
|
1578
|
+
pusdBalance = (address) => readAtoms(PUSD, `balance-${address}`);
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1283
1581
|
|
|
1284
1582
|
// src/dao.mjs
|
|
1285
1583
|
import { z as z5 } from "zod";
|
|
1286
|
-
var RUNE = graph.processes.rune;
|
|
1287
|
-
var DEN = 6;
|
|
1288
1584
|
function daoProcess() {
|
|
1289
1585
|
const id = process.env.PARALITH_DAO || graph.processes.dao;
|
|
1290
1586
|
if (!ID.test(id ?? "")) throw new Error("The DAO is not deployed yet. Read the design at https://docs.paralithmarkets.com/dao/governance");
|
|
1291
1587
|
return id;
|
|
1292
1588
|
}
|
|
1293
|
-
var tags = (record) => Object.entries(record).filter(([, v]) => v !== void 0 && v !== "").map(([name, value]) => ({ name, value: String(value) }));
|
|
1294
1589
|
async function stakeOf(address) {
|
|
1295
1590
|
const row = await readKey(daoProcess(), `stake-${address}`);
|
|
1296
1591
|
return row ?? { active: "0", cooling: "0", releaseAt: 0, lockedUntil: 0, votes: {} };
|
|
1297
1592
|
}
|
|
1298
|
-
var runeView = (row) => row && {
|
|
1299
|
-
...row,
|
|
1300
|
-
active: formatAtoms(row.active ?? 0, DEN),
|
|
1301
|
-
cooling: formatAtoms(row.cooling ?? 0, DEN),
|
|
1302
|
-
...row.releaseAt ? { releaseAt: new Date(row.releaseAt).toISOString() } : {},
|
|
1303
|
-
...row.lockedUntil ? { lockedUntil: new Date(row.lockedUntil).toISOString() } : {}
|
|
1304
|
-
};
|
|
1305
1593
|
async function send(record, options = {}) {
|
|
1306
1594
|
const { api } = await runtime();
|
|
1307
1595
|
const reply = await api.rawSend(tags(record), { process: daoProcess(), node: NODE, ...options });
|
|
@@ -1446,13 +1734,27 @@ function registerDao(server) {
|
|
|
1446
1734
|
WRITE
|
|
1447
1735
|
);
|
|
1448
1736
|
}
|
|
1737
|
+
var RUNE, DEN, tags, runeView;
|
|
1738
|
+
var init_dao = __esm({
|
|
1739
|
+
"src/dao.mjs"() {
|
|
1740
|
+
"use strict";
|
|
1741
|
+
init_runtime();
|
|
1742
|
+
init_tool();
|
|
1743
|
+
RUNE = graph.processes.rune;
|
|
1744
|
+
DEN = 6;
|
|
1745
|
+
tags = (record) => Object.entries(record).filter(([, v]) => v !== void 0 && v !== "").map(([name, value]) => ({ name, value: String(value) }));
|
|
1746
|
+
runeView = (row) => row && {
|
|
1747
|
+
...row,
|
|
1748
|
+
active: formatAtoms(row.active ?? 0, DEN),
|
|
1749
|
+
cooling: formatAtoms(row.cooling ?? 0, DEN),
|
|
1750
|
+
...row.releaseAt ? { releaseAt: new Date(row.releaseAt).toISOString() } : {},
|
|
1751
|
+
...row.lockedUntil ? { lockedUntil: new Date(row.lockedUntil).toISOString() } : {}
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1449
1755
|
|
|
1450
1756
|
// src/vaults.mjs
|
|
1451
1757
|
import { z as z6 } from "zod";
|
|
1452
|
-
var VENUE2 = z6.enum(["game", "token"]).describe("game: items vs Gold. token: element tokens vs TEST-RUNE.");
|
|
1453
|
-
var STRATEGY = z6.enum(["amm", "active", "grid"]).describe("amm: passive, steady, close to holding. active: concentrated, best sideways, loses most in trends. grid: fixed steps, best range-bound, loses in trends.");
|
|
1454
|
-
var venueProcess2 = (venue) => venue === "game" ? graph.processes.internalVenue : graph.processes.externalVenue;
|
|
1455
|
-
var DAY_MS2 = 864e5;
|
|
1456
1758
|
async function terms2(venue) {
|
|
1457
1759
|
const { api } = await runtime();
|
|
1458
1760
|
const process2 = venueProcess2(venue);
|
|
@@ -1465,8 +1767,6 @@ function marketFor2(t, item) {
|
|
|
1465
1767
|
if (!entry) throw new Error(`No market for "${item}". Markets: ${Object.keys(t.markets).join(", ")}`);
|
|
1466
1768
|
return { id: entry[0], ...entry[1] };
|
|
1467
1769
|
}
|
|
1468
|
-
var pct = (part, whole) => BigInt(whole || 0) > 0n ? `${(Number(BigInt(part || 0) * 10000n / BigInt(whole)) / 100).toFixed(2)}%` : "0%";
|
|
1469
|
-
var iso2 = (ms) => ms && Number(ms) > 0 ? new Date(Number(ms)).toISOString() : void 0;
|
|
1470
1770
|
function vaultView(t, m, v) {
|
|
1471
1771
|
const b = (atoms) => formatAtoms(atoms ?? 0, t.den(m.base));
|
|
1472
1772
|
const q = (atoms) => formatAtoms(atoms ?? 0, t.den(m.quote));
|
|
@@ -1624,36 +1924,34 @@ function registerVaults(server) {
|
|
|
1624
1924
|
WRITE
|
|
1625
1925
|
);
|
|
1626
1926
|
}
|
|
1927
|
+
var VENUE2, STRATEGY, venueProcess2, DAY_MS2, pct, iso2;
|
|
1928
|
+
var init_vaults = __esm({
|
|
1929
|
+
"src/vaults.mjs"() {
|
|
1930
|
+
"use strict";
|
|
1931
|
+
init_runtime();
|
|
1932
|
+
init_tool();
|
|
1933
|
+
VENUE2 = z6.enum(["game", "token"]).describe("game: items vs Gold. token: element tokens vs TEST-RUNE.");
|
|
1934
|
+
STRATEGY = z6.enum(["amm", "active", "grid"]).describe("amm: passive, steady, close to holding. active: concentrated, best sideways, loses most in trends. grid: fixed steps, best range-bound, loses in trends.");
|
|
1935
|
+
venueProcess2 = (venue) => venue === "game" ? graph.processes.internalVenue : graph.processes.externalVenue;
|
|
1936
|
+
DAY_MS2 = 864e5;
|
|
1937
|
+
pct = (part, whole) => BigInt(whole || 0) > 0n ? `${(Number(BigInt(part || 0) * 10000n / BigInt(whole)) / 100).toFixed(2)}%` : "0%";
|
|
1938
|
+
iso2 = (ms) => ms && Number(ms) > 0 ? new Date(Number(ms)).toISOString() : void 0;
|
|
1939
|
+
}
|
|
1940
|
+
});
|
|
1627
1941
|
|
|
1628
1942
|
// src/show.mjs
|
|
1629
|
-
import
|
|
1630
|
-
import
|
|
1943
|
+
import fs5 from "node:fs";
|
|
1944
|
+
import path5 from "node:path";
|
|
1631
1945
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1632
1946
|
import { z as z7 } from "zod";
|
|
1633
|
-
import {
|
|
1634
|
-
|
|
1635
|
-
var TRADE = (process.env.PARALITH_TRADE_URL || "https://trade.paralithmarkets.com").replace(/\/$/, "");
|
|
1636
|
-
var URI = "ui://paralith/view";
|
|
1637
|
-
var HERE2 = path3.dirname(fileURLToPath2(import.meta.url));
|
|
1947
|
+
import { spawn } from "node:child_process";
|
|
1948
|
+
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE, getUiCapability } from "@modelcontextprotocol/ext-apps/server";
|
|
1638
1949
|
function viewHtml() {
|
|
1639
|
-
for (const file of [
|
|
1640
|
-
if (
|
|
1950
|
+
for (const file of [path5.join(HERE2, "view.html"), path5.join(HERE2, "..", "dist", "view.html")]) {
|
|
1951
|
+
if (fs5.existsSync(file)) return fs5.readFileSync(file, "utf8");
|
|
1641
1952
|
}
|
|
1642
1953
|
throw new Error("view.html is missing: run npm run build");
|
|
1643
1954
|
}
|
|
1644
|
-
var link = (base2, page, params) => {
|
|
1645
|
-
const query = new URLSearchParams(Object.entries(params).filter(([, v]) => v !== void 0 && v !== ""));
|
|
1646
|
-
return `${base2}/embed/${page}${query.size ? `?${query}` : ""}`;
|
|
1647
|
-
};
|
|
1648
|
-
var VIEWS = {
|
|
1649
|
-
battle: (a) => a.battle_id && a.worker ? link(GAME, "battle", { w: a.worker, b: a.battle_id, a: a.address }) : link(GAME, "battle", { a: need2(a, "address") }),
|
|
1650
|
-
companion: (a) => link(GAME, "companion", { a: need2(a, "address"), m: a.monster_id }),
|
|
1651
|
-
hunt: (a) => link(GAME, "hunt", { a: need2(a, "address") }),
|
|
1652
|
-
market: (a) => link(TRADE, "market", { venue: a.venue ?? "token", m: need2(a, "market") }),
|
|
1653
|
-
vault: (a) => link(TRADE, "vault", { venue: a.venue ?? "token", m: need2(a, "market"), v: a.vault ?? "amm" }),
|
|
1654
|
-
position: (a) => link(TRADE, "position", { venue: a.venue ?? "token", a: need2(a, "address") }),
|
|
1655
|
-
bridge: (a) => link(TRADE, "bridge", { a: a.address, chain: a.chain, tx: a.tx })
|
|
1656
|
-
};
|
|
1657
1955
|
function need2(args, key) {
|
|
1658
1956
|
if (!args[key]) throw new Error(`${key} is required to show this.`);
|
|
1659
1957
|
return args[key];
|
|
@@ -1663,6 +1961,16 @@ async function ownAddress() {
|
|
|
1663
1961
|
const { wallets } = await runtime();
|
|
1664
1962
|
return wallets?.ao.address;
|
|
1665
1963
|
}
|
|
1964
|
+
function openInBrowser(url) {
|
|
1965
|
+
const [cmd, args] = process.platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", url]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
|
|
1966
|
+
try {
|
|
1967
|
+
spawn(cmd, args, { detached: true, stdio: "ignore" }).on("error", () => {
|
|
1968
|
+
}).unref();
|
|
1969
|
+
return true;
|
|
1970
|
+
} catch {
|
|
1971
|
+
return false;
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1666
1974
|
function registerShow(server) {
|
|
1667
1975
|
const csp = { frameDomains: [GAME, TRADE] };
|
|
1668
1976
|
registerAppResource(
|
|
@@ -1673,7 +1981,7 @@ function registerShow(server) {
|
|
|
1673
1981
|
async () => ({ contents: [{ uri: URI, mimeType: RESOURCE_MIME_TYPE, text: viewHtml(), _meta: { ui: { csp } } }] })
|
|
1674
1982
|
);
|
|
1675
1983
|
registerAppTool(server, "show", {
|
|
1676
|
-
description: "Show something with the game's and exchange's own visuals, live: battle (follows a player's arena fights, animated), companion (card), hunt, market (order book and chart), vault, position, bridge (transfers).
|
|
1984
|
+
description: "Show something with the game's and exchange's own visuals, live: battle (follows a player's arena fights, animated), companion (card), hunt, market (order book and chart), vault, position, bridge (transfers). Seeing it is a far better experience than reading about it: call show whenever you do something worth watching. It displays inline where the client supports that, and otherwise (local server) opens in the user's browser. Address defaults to yours.",
|
|
1677
1985
|
inputSchema: {
|
|
1678
1986
|
what: z7.enum(Object.keys(VIEWS)),
|
|
1679
1987
|
address: z7.string().optional().describe("AO address; omit for yours."),
|
|
@@ -1684,45 +1992,167 @@ function registerShow(server) {
|
|
|
1684
1992
|
market: z7.string().optional().describe("market/vault: the market, as exchange_markets lists it."),
|
|
1685
1993
|
vault: z7.enum(["amm", "active", "grid"]).optional(),
|
|
1686
1994
|
chain: z7.enum(["base", "solana", "sui"]).optional().describe("bridge: with tx, one transfer."),
|
|
1687
|
-
tx: z7.string().optional()
|
|
1995
|
+
tx: z7.string().optional(),
|
|
1996
|
+
open: z7.boolean().optional().describe("Open in the user's browser. Default: only when this client cannot display it inline.")
|
|
1688
1997
|
},
|
|
1689
1998
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
1690
1999
|
_meta: { ui: { resourceUri: URI } }
|
|
1691
2000
|
}, async (args) => {
|
|
1692
2001
|
try {
|
|
1693
2002
|
const view = VIEWS[args.what]({ ...args, address: args.address ?? await ownAddress() });
|
|
1694
|
-
|
|
2003
|
+
const inline = !!getUiCapability(server.server.getClientCapabilities());
|
|
2004
|
+
const opened = !READ_ONLY && (args.open ?? !inline) && openInBrowser(view);
|
|
2005
|
+
const where = inline ? "Displayed inline." : opened ? "Opened in the user's browser." : "Give the user this link.";
|
|
2006
|
+
return { structuredContent: { what: args.what, view }, content: [{ type: "text", text: `${args.what}: ${view}
|
|
2007
|
+
${where}` }] };
|
|
1695
2008
|
} catch (error) {
|
|
1696
2009
|
return { isError: true, content: [{ type: "text", text: error.message }] };
|
|
1697
2010
|
}
|
|
1698
2011
|
});
|
|
1699
2012
|
}
|
|
2013
|
+
var GAME, TRADE, URI, HERE2, link, VIEWS;
|
|
2014
|
+
var init_show = __esm({
|
|
2015
|
+
"src/show.mjs"() {
|
|
2016
|
+
"use strict";
|
|
2017
|
+
init_runtime();
|
|
2018
|
+
GAME = (process.env.PARALITH_GAME_URL || "https://game.paralithmarkets.com").replace(/\/$/, "");
|
|
2019
|
+
TRADE = (process.env.PARALITH_TRADE_URL || "https://trade.paralithmarkets.com").replace(/\/$/, "");
|
|
2020
|
+
URI = "ui://paralith/view";
|
|
2021
|
+
HERE2 = path5.dirname(fileURLToPath2(import.meta.url));
|
|
2022
|
+
link = (base2, page, params) => {
|
|
2023
|
+
const query = new URLSearchParams(Object.entries(params).filter(([, v]) => v !== void 0 && v !== ""));
|
|
2024
|
+
return `${base2}/embed/${page}${query.size ? `?${query}` : ""}`;
|
|
2025
|
+
};
|
|
2026
|
+
VIEWS = {
|
|
2027
|
+
battle: (a) => a.battle_id && a.worker ? link(GAME, "battle", { w: a.worker, b: a.battle_id, a: a.address }) : link(GAME, "battle", { a: need2(a, "address") }),
|
|
2028
|
+
companion: (a) => link(GAME, "companion", { a: need2(a, "address"), m: a.monster_id }),
|
|
2029
|
+
hunt: (a) => link(GAME, "hunt", { a: need2(a, "address") }),
|
|
2030
|
+
market: (a) => link(TRADE, "market", { venue: a.venue ?? "token", m: need2(a, "market") }),
|
|
2031
|
+
vault: (a) => link(TRADE, "vault", { venue: a.venue ?? "token", m: need2(a, "market"), v: a.vault ?? "amm" }),
|
|
2032
|
+
position: (a) => link(TRADE, "position", { venue: a.venue ?? "token", a: need2(a, "address") }),
|
|
2033
|
+
bridge: (a) => link(TRADE, "bridge", { a: a.address, chain: a.chain, tx: a.tx })
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
});
|
|
1700
2037
|
|
|
1701
|
-
// src/
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
2038
|
+
// src/progress.mjs
|
|
2039
|
+
import { z as z8 } from "zod";
|
|
2040
|
+
async function targets(who) {
|
|
2041
|
+
const { wallets } = await runtime();
|
|
2042
|
+
const agents = readAgents();
|
|
2043
|
+
const self = wallets && { name: process.env.PARALITH_AGENT ?? "you", addresses: publicAddresses(wallets) };
|
|
2044
|
+
if (!who || who === "me") {
|
|
2045
|
+
if (!self) throw new Error("This server has no wallet; pass who: an AO address.");
|
|
2046
|
+
return [self];
|
|
2047
|
+
}
|
|
2048
|
+
if (who === "all") {
|
|
2049
|
+
const list = Object.entries(agents).map(([name, a]) => ({ name, addresses: a.addresses }));
|
|
2050
|
+
if (self && !list.some((t) => t.name === self.name)) list.unshift(self);
|
|
2051
|
+
if (!list.length) throw new Error("No agents are set up here. Run: npx -y paralith-mcp@latest setup");
|
|
2052
|
+
return list;
|
|
2053
|
+
}
|
|
2054
|
+
if (agents[who]) return [{ name: who, addresses: agents[who].addresses }];
|
|
2055
|
+
if (ID.test(who)) return [{ name: who, addresses: { ao: who } }];
|
|
2056
|
+
throw new Error(`"${who}" is not an agent here (${Object.keys(agents).join(", ") || "none"}) or an AO address.`);
|
|
2057
|
+
}
|
|
2058
|
+
async function report({ name, addresses: a }) {
|
|
2059
|
+
const { api } = await runtime();
|
|
2060
|
+
const out = { agent: name, addresses: a };
|
|
2061
|
+
const todo = [];
|
|
2062
|
+
const gas = Object.fromEntries(await Promise.all(["base", "solana", "sui"].filter((c) => a[c]).map(async (c) => [c, await gasOf(c, a[c]).catch(() => "unreadable")])));
|
|
2063
|
+
if (Object.keys(gas).length) out.gas = gas;
|
|
2064
|
+
if (!a.ao) {
|
|
2065
|
+
out.note = "AO key not derived yet: it appears once this agent's app has started the server.";
|
|
2066
|
+
return out;
|
|
2067
|
+
}
|
|
2068
|
+
const [p, tokens, venues, redemptions] = await Promise.all([
|
|
2069
|
+
api.readPlayer(a.ao).catch(() => null),
|
|
2070
|
+
Promise.all(Object.entries({ rune: graph.processes.rune, pusd: graph.processes.pusd, ...graph.processes.elements }).map(async ([t, id]) => [t, await readAtoms(id, `balance-${a.ao}`).catch(() => 0n)])),
|
|
2071
|
+
Promise.all(Object.entries(VENUES).map(async ([v, pid]) => [
|
|
2072
|
+
v,
|
|
2073
|
+
await api.readVenuePosition(pid, a.ao).catch(() => null),
|
|
2074
|
+
await api.readVenueVaultPositions(pid, a.ao).catch(() => null)
|
|
2075
|
+
])),
|
|
2076
|
+
readKey("pusd", "withdrawals").catch(() => null)
|
|
2077
|
+
]);
|
|
2078
|
+
if (!p) {
|
|
2079
|
+
out.game = { joined: false };
|
|
2080
|
+
todo.push("Join the game (game_join).");
|
|
2081
|
+
} else {
|
|
2082
|
+
const raw = p.monsters?.[p.activeId] ?? p.collection?.[p.activeId];
|
|
2083
|
+
const c = companion(raw);
|
|
2084
|
+
out.game = {
|
|
2085
|
+
faction: p.faction,
|
|
2086
|
+
gold: p.gold,
|
|
2087
|
+
rune: p.inventory?.rune ?? 0,
|
|
2088
|
+
companion: c && { name: c.name, level: c.level, exp: c.exp, energy: c.energy, happiness: c.happiness },
|
|
2089
|
+
arena: { wins: p.wins ?? 0, losses: p.losses ?? 0, rating: p.rating, sessionBattlesLeft: p.battlesRemaining ?? 0 },
|
|
2090
|
+
...p.battleFleet ? { inBattle: p.battleFleet.battleId } : {},
|
|
2091
|
+
...p.hunt ? { onHunt: p.hunt.runId } : {}
|
|
2092
|
+
};
|
|
2093
|
+
if (!p.dailyReadyAt || Number(p.dailyReadyAt) <= Date.now()) todo.push("Daily crate is ready (game_daily).");
|
|
2094
|
+
if (count(p.lootboxes)) todo.push(`${count(p.lootboxes)} loot box(es) to open (game_lootbox).`);
|
|
2095
|
+
if (raw && Number(raw.exp ?? 0) >= Number(raw.nextLevelExp ?? Infinity)) todo.push("Companion can level up (game_level_up).");
|
|
2096
|
+
if (c && (c.energy < 25 || c.happiness < 25)) todo.push("Companion is below the arena's 25 energy/happiness: feed or play.");
|
|
2097
|
+
}
|
|
2098
|
+
const held = Object.fromEntries(tokens.filter(([, v]) => v > 0n).map(([t, v]) => [t, formatAtoms(v)]));
|
|
2099
|
+
if (Object.keys(held).length) out.tokens = held;
|
|
2100
|
+
const trading = {};
|
|
2101
|
+
for (const [v, pos, vaults] of venues) {
|
|
2102
|
+
const row = {};
|
|
2103
|
+
if (pos?.orders?.length) row.openOrders = pos.orders.length;
|
|
2104
|
+
if (pos?.fills?.length) row.recentFills = pos.fills.length;
|
|
2105
|
+
const shares = Object.entries(vaults ?? {}).flatMap(([m, r]) => Object.entries(r).filter(([, x]) => BigInt(x?.shares ?? 0) > 0n).map(([s]) => `${m} ${s}`));
|
|
2106
|
+
if (shares.length) row.vaults = shares;
|
|
2107
|
+
if (Object.keys(row).length) trading[v] = row;
|
|
2108
|
+
}
|
|
2109
|
+
if (Object.keys(trading).length) out.trading = trading;
|
|
2110
|
+
const pending = (redemptions ?? []).filter((w) => w.account === a.ao && w.status !== "released");
|
|
2111
|
+
if (pending.length) out.bridge = { pendingRedemptions: pending.length };
|
|
2112
|
+
if (Object.keys(gas).length && Object.values(gas).every((g) => /^0 /.test(g))) {
|
|
2113
|
+
todo.push("No chain gas yet: fund an address above to bridge.");
|
|
2114
|
+
}
|
|
2115
|
+
if (todo.length) out.todo = todo;
|
|
2116
|
+
return out;
|
|
2117
|
+
}
|
|
2118
|
+
function registerProgress(server) {
|
|
2119
|
+
tool(
|
|
2120
|
+
server,
|
|
2121
|
+
"progress",
|
|
2122
|
+
'How an agent is doing, in one free read: game (companion, Gold, Rune, arena record, live battle or hunt), tokens, open orders and vault shares on both venues, pending bridge redemptions, chain gas, and what needs doing next. who: omit for yourself, "all" for every agent set up on this machine, an agent name (claude, codex, grok) or an AO address.',
|
|
2123
|
+
{ who: z8.string().optional() },
|
|
2124
|
+
async ({ who }) => {
|
|
2125
|
+
const list = await Promise.all((await targets(who)).map(report));
|
|
2126
|
+
return list.length === 1 ? list[0] : list;
|
|
2127
|
+
},
|
|
2128
|
+
READ
|
|
2129
|
+
);
|
|
2130
|
+
}
|
|
2131
|
+
var VENUES, count;
|
|
2132
|
+
var init_progress = __esm({
|
|
2133
|
+
"src/progress.mjs"() {
|
|
2134
|
+
"use strict";
|
|
2135
|
+
init_runtime();
|
|
2136
|
+
init_tool();
|
|
2137
|
+
init_agents();
|
|
2138
|
+
init_keys();
|
|
2139
|
+
init_bridge();
|
|
2140
|
+
init_views();
|
|
2141
|
+
VENUES = { game: graph.processes.internalVenue, token: graph.processes.externalVenue };
|
|
2142
|
+
count = (x) => Array.isArray(x) ? x.length : Object.values(x ?? {}).reduce((n, v) => n + Number(v || 0), 0);
|
|
2143
|
+
}
|
|
2144
|
+
});
|
|
1718
2145
|
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
2146
|
+
// src/build.mjs
|
|
2147
|
+
var build_exports = {};
|
|
2148
|
+
__export(build_exports, {
|
|
2149
|
+
buildServer: () => buildServer
|
|
2150
|
+
});
|
|
2151
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2152
|
+
import { z as z9 } from "zod";
|
|
1723
2153
|
function buildServer() {
|
|
1724
2154
|
const server = new McpServer(
|
|
1725
|
-
{ name: "paralith", version: "0.
|
|
2155
|
+
{ name: "paralith", version: "0.3.1" },
|
|
1726
2156
|
{ instructions: READ_ONLY ? `${READ_ONLY_NOTE}
|
|
1727
2157
|
|
|
1728
2158
|
${INSTRUCTIONS}` : INSTRUCTIONS }
|
|
@@ -1753,7 +2183,7 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
|
|
|
1753
2183
|
server,
|
|
1754
2184
|
"read_state",
|
|
1755
2185
|
`Read any published key of any process, free: /<process>~process@1.0/now/<key>. process is an id or an alias (${Object.keys(PROCESS_ALIASES).join(", ")}). Returns null when absent.`,
|
|
1756
|
-
{ process:
|
|
2186
|
+
{ process: z9.string(), key: z9.string() },
|
|
1757
2187
|
async ({ process: process2, key }) => readKey(process2, key),
|
|
1758
2188
|
READ
|
|
1759
2189
|
);
|
|
@@ -1762,10 +2192,10 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
|
|
|
1762
2192
|
"send_message",
|
|
1763
2193
|
"Escape hatch: sign and send a raw message to any process and return its reply. Prefer the specific tools; use this only for an action they do not cover. Tag values must be strings; never use a tag named Target.",
|
|
1764
2194
|
{
|
|
1765
|
-
process:
|
|
1766
|
-
tags:
|
|
1767
|
-
data:
|
|
1768
|
-
push_outbox:
|
|
2195
|
+
process: z9.string(),
|
|
2196
|
+
tags: z9.record(z9.string(), z9.string()).describe('e.g. {"Action": "Pass.Info"}'),
|
|
2197
|
+
data: z9.string().optional(),
|
|
2198
|
+
push_outbox: z9.boolean().optional().describe("Deliver the messages this one emits to other processes (e.g. a Transfer to a process).")
|
|
1769
2199
|
},
|
|
1770
2200
|
async ({ process: process2, tags: tags2, data, push_outbox }) => {
|
|
1771
2201
|
if (Object.keys(tags2).some((k) => k.toLowerCase() === "target")) throw new Error("Target is not a usable tag name.");
|
|
@@ -1795,9 +2225,74 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
|
|
|
1795
2225
|
registerDao(server);
|
|
1796
2226
|
registerVaults(server);
|
|
1797
2227
|
registerShow(server);
|
|
2228
|
+
registerProgress(server);
|
|
1798
2229
|
return server;
|
|
1799
2230
|
}
|
|
2231
|
+
var DOCS, INSTRUCTIONS, READ_ONLY_NOTE;
|
|
2232
|
+
var init_build = __esm({
|
|
2233
|
+
"src/build.mjs"() {
|
|
2234
|
+
"use strict";
|
|
2235
|
+
init_runtime();
|
|
2236
|
+
init_keys();
|
|
2237
|
+
init_tool();
|
|
2238
|
+
init_game();
|
|
2239
|
+
init_exchange();
|
|
2240
|
+
init_bridge();
|
|
2241
|
+
init_dao();
|
|
2242
|
+
init_vaults();
|
|
2243
|
+
init_show();
|
|
2244
|
+
init_progress();
|
|
2245
|
+
DOCS = process.env.PARALITH_DOCS_URL || "https://docs.paralithmarkets.com";
|
|
2246
|
+
INSTRUCTIONS = `Paralith on AO (HyperBEAM): Rune Realm (a companion game), two order books, and the PUSD bridge.
|
|
2247
|
+
Everything is TEST-prefixed while unreleased. Reads are free; every write is a signed message (a few seconds round trip, longer under load).
|
|
2248
|
+
|
|
2249
|
+
Start: wallet -> game_player. No account? game_join (pick a faction).
|
|
2250
|
+
Core loop: game_daily once a day; game_companion feed/play/quest/claim to keep energy and happiness up and earn exp and Gold;
|
|
2251
|
+
game_level_up when exp allows; game_learn_move when an offer is pending.
|
|
2252
|
+
Arena: arena_enter (25 energy + 25 happiness, 4 fights), arena_fight mode=bot, then arena_attack each round
|
|
2253
|
+
(read arena_battle for moves and uses left; Rally and Mend are free once each), arena_end leave when done.
|
|
2254
|
+
Hunt: hunt_begin -> hunt_act search -> attack until defeated -> capture (1-3 Rune + a Scroll) or decline -> end.
|
|
2255
|
+
Markets: exchange_markets / exchange_book / exchange_order on venue "game" (items vs Gold) or "token" (elements vs TEST-RUNE).
|
|
2256
|
+
Vaults (pooled liquidity, one amm/active/grid per market): vault_list, vault_position, vault_deposit, vault_shares (withdraw/lock/burn), vault_fund, vault_crank.
|
|
2257
|
+
Rune: rune_move withdraw (game -> token) / deposit (token -> game). Bridge: bridge_balances, bridge_faucet, bridge_deposit, bridge_redeem.
|
|
2258
|
+
DAO (stake TEST-RUNE; a passed proposal may send one attached message): dao_info, dao_proposals, dao_stake stake/unstake/withdraw, dao_propose, dao_vote.
|
|
2259
|
+
|
|
2260
|
+
HOW RESOURCES FLOW. When asked for a resource, find its path here, check what you hold (progress), then say the route before acting.
|
|
2261
|
+
- Gold (in-game only, never leaves the game). Earn: companion play/quest/claim (capped at 60 Gold per 20 h), arena bot wins,
|
|
2262
|
+
selling berries/Scrolls/Rune to the shop (game_shop sell) or on the game venue. Spend: arena stakes, shop buys, game-venue buys.
|
|
2263
|
+
- Berries and Scrolls. Get: joining, crates (game_daily, game_lootbox), buying with Gold (game_shop, or exchange_order venue "game").
|
|
2264
|
+
Use: feed the companion, hunt escrow (2 of each berry), arena boosts; a Scroll plus 1-3 Rune for a hunt capture.
|
|
2265
|
+
- Rune exists twice: in-game Rune and the TEST-RUNE wallet token; rune_move converts 1:1 either way.
|
|
2266
|
+
Get Rune: (1) the shop, only if game_shop quote shows an ask (it often only bids); (2) sell a companion on companion_market;
|
|
2267
|
+
(3) the token route, which always works: element_claim (free daily, 1-10 of one element) -> exchange_order venue "token"
|
|
2268
|
+
sell that element for TEST-RUNE -> rune_move deposit. Public TEST accounts accrue no Rune from game_daily (pass holders only).
|
|
2269
|
+
Spend in-game Rune: game_level_up, hunt capture, companion_market buys. TEST-RUNE: token-venue buys, vaults, dao_stake.
|
|
2270
|
+
- Element tokens (TEST-EARTH/AIR/FIRE/WATER): element_claim daily; trade against TEST-RUNE on the token venue; vault liquidity.
|
|
2271
|
+
- PUSD: bridge in TEST-USDC from Base, Solana or Sui (needs that chain's gas, which the user funds; bridge_faucet gives TEST-USDC).
|
|
2272
|
+
No PUSD market is listed yet. Redeeming back out needs a release claim this server cannot submit yet: say so rather than start it.
|
|
2273
|
+
- Vaults: deposit both sides of a market to earn from its strategy; withdraw with vault_shares.
|
|
2274
|
+
|
|
2275
|
+
To report how you (or every agent set up on this machine) are doing, call progress (who: "all" for every agent); it also lists what needs doing next.
|
|
2276
|
+
|
|
2277
|
+
SHOW IT. Watching beats reading: call show for anything worth seeing (a fight as it happens, the companion after a level-up, the book
|
|
2278
|
+
you are trading, a vault, a bridge transfer). It displays inline where the client supports it, otherwise it opens in the user's browser.
|
|
2279
|
+
Show once per thing, not once per round.
|
|
2280
|
+
|
|
2281
|
+
Rules: if a tool says a write WAS accepted but unconfirmed, never resend it; re-read state instead.
|
|
2282
|
+
A refusal ({"error": ...}) costs nothing. Use game_catalog for exact costs and move stats; docs at ${DOCS}/llms-full.txt.`;
|
|
2283
|
+
READ_ONLY_NOTE = `This endpoint is READ-ONLY: it explains the protocol and reads live state for any address.
|
|
2284
|
+
To play, trade or bridge, run the local server with a wallet phrase: ${DOCS}/ai/mcp`;
|
|
2285
|
+
}
|
|
2286
|
+
});
|
|
1800
2287
|
|
|
1801
2288
|
// src/server.mjs
|
|
1802
|
-
|
|
1803
|
-
|
|
2289
|
+
if (process.argv[2] === "setup") {
|
|
2290
|
+
const { setup: setup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
2291
|
+
await setup2(process.argv.slice(3));
|
|
2292
|
+
} else {
|
|
2293
|
+
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
2294
|
+
const { runtime: runtime2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
|
|
2295
|
+
const { buildServer: buildServer2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
2296
|
+
await buildServer2().connect(new StdioServerTransport());
|
|
2297
|
+
runtime2().catch((error) => console.error(`paralith-mcp: ${error.message}`));
|
|
2298
|
+
}
|