kill-switch-mcp 1.2.3 → 1.2.5
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/server.js +198 -83
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -150,7 +150,7 @@ class BotSDK {
|
|
|
150
150
|
const status = await this.checkBotStatus();
|
|
151
151
|
const shouldLaunch = this.shouldLaunchBrowser(status);
|
|
152
152
|
if (shouldLaunch) {
|
|
153
|
-
console.
|
|
153
|
+
console.error(`[BotSDK] Launching browser...`);
|
|
154
154
|
this.browserLaunched = true;
|
|
155
155
|
await this.launchBrowser();
|
|
156
156
|
await this.waitForBotConnection();
|
|
@@ -212,7 +212,7 @@ class BotSDK {
|
|
|
212
212
|
this.browserLaunched = false;
|
|
213
213
|
}
|
|
214
214
|
this.waitForReady(15000).then(() => {
|
|
215
|
-
console.
|
|
215
|
+
console.error("[BotSDK] Connected and game state ready");
|
|
216
216
|
resolve();
|
|
217
217
|
}).catch((error) => {
|
|
218
218
|
console.warn("[BotSDK] Connected but game state not ready:", error.message);
|
|
@@ -246,21 +246,21 @@ class BotSDK {
|
|
|
246
246
|
}
|
|
247
247
|
scheduleReconnect() {
|
|
248
248
|
if (this.reconnectAttempt >= this.config.reconnectMaxRetries) {
|
|
249
|
-
console.
|
|
249
|
+
console.error(`[BotSDK] Max reconnection attempts (${this.config.reconnectMaxRetries}) reached, giving up`);
|
|
250
250
|
this.setConnectionState("disconnected");
|
|
251
251
|
return;
|
|
252
252
|
}
|
|
253
253
|
this.reconnectAttempt++;
|
|
254
254
|
this.setConnectionState("reconnecting", this.reconnectAttempt);
|
|
255
255
|
const delay = Math.min(this.config.reconnectBaseDelay * Math.pow(2, this.reconnectAttempt - 1), this.config.reconnectMaxDelay);
|
|
256
|
-
console.
|
|
256
|
+
console.error(`[BotSDK] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempt})`);
|
|
257
257
|
this.reconnectTimer = setTimeout(async () => {
|
|
258
258
|
this.reconnectTimer = null;
|
|
259
259
|
try {
|
|
260
260
|
await this.connect();
|
|
261
|
-
console.
|
|
261
|
+
console.error(`[BotSDK] Reconnected successfully after ${this.reconnectAttempt} attempt(s)`);
|
|
262
262
|
} catch (e) {
|
|
263
|
-
console.
|
|
263
|
+
console.error(`[BotSDK] Reconnection attempt ${this.reconnectAttempt} failed`);
|
|
264
264
|
}
|
|
265
265
|
}, delay);
|
|
266
266
|
}
|
|
@@ -304,17 +304,17 @@ class BotSDK {
|
|
|
304
304
|
async checkBotStatus() {
|
|
305
305
|
const statusUrl = this.getStatusUrl();
|
|
306
306
|
try {
|
|
307
|
-
console.
|
|
307
|
+
console.error(`[BotSDK] Checking bot status via URL: ${statusUrl}`);
|
|
308
308
|
const response = await fetch(statusUrl);
|
|
309
309
|
if (!response.ok) {
|
|
310
|
-
console.
|
|
310
|
+
console.error(`[BotSDK] Status check HTTP error: ${response.status} ${response.statusText} (URL: ${statusUrl})`);
|
|
311
311
|
throw new Error(`Status check failed: ${response.status}`);
|
|
312
312
|
}
|
|
313
313
|
const data = await response.json();
|
|
314
314
|
return data;
|
|
315
315
|
} catch (error) {
|
|
316
316
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
317
|
-
console.
|
|
317
|
+
console.error(`[BotSDK] Status check failed: ${errorMsg} (URL: ${statusUrl})`);
|
|
318
318
|
return {
|
|
319
319
|
status: "dead",
|
|
320
320
|
inGame: false,
|
|
@@ -334,15 +334,15 @@ class BotSDK {
|
|
|
334
334
|
return false;
|
|
335
335
|
}
|
|
336
336
|
if (status.status === "dead") {
|
|
337
|
-
console.
|
|
337
|
+
console.error(`[BotSDK] Bot not connected`);
|
|
338
338
|
return true;
|
|
339
339
|
}
|
|
340
|
-
console.
|
|
340
|
+
console.error(`[BotSDK] Active client detected, skipping browser launch`);
|
|
341
341
|
return false;
|
|
342
342
|
}
|
|
343
343
|
async launchBrowser() {
|
|
344
344
|
const url = this.buildClientUrl();
|
|
345
|
-
console.
|
|
345
|
+
console.error(`[BotSDK] Opening browser: ${url}`);
|
|
346
346
|
const { exec } = await import("child_process");
|
|
347
347
|
const command = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
348
348
|
return new Promise((resolve, reject) => {
|
|
@@ -360,14 +360,14 @@ class BotSDK {
|
|
|
360
360
|
const startTime = Date.now();
|
|
361
361
|
const pollInterval = 500;
|
|
362
362
|
let attemptCount = 0;
|
|
363
|
-
console.
|
|
363
|
+
console.error(`[BotSDK] Waiting for bot to connect and load game (timeout: ${timeoutMs}ms)...`);
|
|
364
364
|
while (Date.now() - startTime < timeoutMs) {
|
|
365
365
|
attemptCount++;
|
|
366
366
|
const elapsed = Date.now() - startTime;
|
|
367
367
|
const status = await this.checkBotStatus();
|
|
368
|
-
console.
|
|
368
|
+
console.error(`[BotSDK] Poll attempt ${attemptCount} (${elapsed}ms): status="${status.status}", inGame=${status.inGame}, controllers=${status.controllers.length}, observers=${status.observers.length}`);
|
|
369
369
|
if (status.status !== "dead" && status.inGame) {
|
|
370
|
-
console.
|
|
370
|
+
console.error(`[BotSDK] Bot connected and in-game!`);
|
|
371
371
|
return;
|
|
372
372
|
}
|
|
373
373
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
@@ -402,7 +402,7 @@ class BotSDK {
|
|
|
402
402
|
username: this.config.botUsername,
|
|
403
403
|
password: this.config.password
|
|
404
404
|
}));
|
|
405
|
-
console.
|
|
405
|
+
console.error(`[BotSDK] Sent login command for ${this.config.botUsername} through gateway`);
|
|
406
406
|
}
|
|
407
407
|
async waitForConnection(timeout = 60000) {
|
|
408
408
|
if (this.isConnected()) {
|
|
@@ -573,7 +573,7 @@ class BotSDK {
|
|
|
573
573
|
}
|
|
574
574
|
async sendAction(action) {
|
|
575
575
|
if (this.connectionState === "reconnecting") {
|
|
576
|
-
console.
|
|
576
|
+
console.error(`[BotSDK] Waiting for reconnection before sending action: ${action.type}`);
|
|
577
577
|
await this.waitForConnection();
|
|
578
578
|
}
|
|
579
579
|
if (!this.isConnected()) {
|
|
@@ -723,7 +723,7 @@ class BotSDK {
|
|
|
723
723
|
}
|
|
724
724
|
async sendScreenshot(timeout = 1e4) {
|
|
725
725
|
if (this.connectionState === "reconnecting") {
|
|
726
|
-
console.
|
|
726
|
+
console.error(`[BotSDK] Waiting for reconnection before requesting screenshot`);
|
|
727
727
|
await this.waitForConnection();
|
|
728
728
|
}
|
|
729
729
|
if (!this.isConnected()) {
|
|
@@ -765,22 +765,22 @@ class BotSDK {
|
|
|
765
765
|
return this.findPath(destX, destZ, maxWaypoints);
|
|
766
766
|
}
|
|
767
767
|
async waitForReady(timeout = 15000) {
|
|
768
|
-
console.
|
|
768
|
+
console.error("[BotSDK] Waiting for game state to be ready...");
|
|
769
769
|
try {
|
|
770
770
|
const state = await this.waitForCondition((s) => {
|
|
771
771
|
const validPosition = !!(s.player && s.player.worldX !== 0 && s.player.worldZ !== 0);
|
|
772
772
|
const inGame = s.inGame;
|
|
773
773
|
const hasEntities = s.nearbyNpcs.length > 0 || s.nearbyLocs.length > 0 || s.groundItems.length > 0;
|
|
774
774
|
if (!validPosition) {
|
|
775
|
-
console.
|
|
775
|
+
console.error(`[BotSDK] Waiting - invalid position: (${s.player?.worldX}, ${s.player?.worldZ})`);
|
|
776
776
|
} else if (!inGame) {
|
|
777
|
-
console.
|
|
777
|
+
console.error("[BotSDK] Waiting - not in game");
|
|
778
778
|
} else if (!hasEntities) {
|
|
779
|
-
console.
|
|
779
|
+
console.error("[BotSDK] Waiting - no entities loaded yet");
|
|
780
780
|
}
|
|
781
781
|
return inGame && validPosition && hasEntities;
|
|
782
782
|
}, timeout);
|
|
783
|
-
console.
|
|
783
|
+
console.error("[BotSDK] Game state ready!");
|
|
784
784
|
return state;
|
|
785
785
|
} catch (error) {
|
|
786
786
|
console.error("[BotSDK] Timeout waiting for game state to be ready");
|
|
@@ -1721,7 +1721,7 @@ class BotActions {
|
|
|
1721
1721
|
if (!blockedDoors.has(key)) {
|
|
1722
1722
|
blockedDoors.add(key);
|
|
1723
1723
|
blockDoor(level, nearest.x, nearest.z);
|
|
1724
|
-
console.
|
|
1724
|
+
console.error(`[walkTo] Blocked impassable door at (${nearest.x}, ${nearest.z}) — re-routing`);
|
|
1725
1725
|
}
|
|
1726
1726
|
}
|
|
1727
1727
|
return false;
|
|
@@ -1732,6 +1732,9 @@ class BotActions {
|
|
|
1732
1732
|
await this.sdk.waitForTicks(1);
|
|
1733
1733
|
path = await this.sdk.sendFindPath(x, z, 500);
|
|
1734
1734
|
if (!path.success || !path.waypoints?.length) {
|
|
1735
|
+
if (path.error?.includes("zone not allocated")) {
|
|
1736
|
+
return this._walkToServerSide(x, z, tolerance);
|
|
1737
|
+
}
|
|
1735
1738
|
console.error(`[walkTo] PATHFINDING FAILED: ${path.error ?? "no waypoints"} - from (${pos.x}, ${pos.z}) to (${x}, ${z})`);
|
|
1736
1739
|
return { success: false, message: `No path to (${x}, ${z}) from (${pos.x}, ${pos.z}): ${path.error ?? "no waypoints"}` };
|
|
1737
1740
|
}
|
|
@@ -1761,7 +1764,7 @@ class BotActions {
|
|
|
1761
1764
|
blockedDoors.add(doorKey);
|
|
1762
1765
|
blockDoor(door.level, door.x, door.z);
|
|
1763
1766
|
requiredDoorKeys.delete(doorKey);
|
|
1764
|
-
console.
|
|
1767
|
+
console.error(`[walkTo] Blocked impassable door at (${door.x}, ${door.z}) — re-routing`);
|
|
1765
1768
|
break;
|
|
1766
1769
|
}
|
|
1767
1770
|
break;
|
|
@@ -1795,6 +1798,45 @@ class BotActions {
|
|
|
1795
1798
|
}
|
|
1796
1799
|
return { success: false, message: `Could not reach (${x}, ${z}) - stopped at (${pos.x}, ${pos.z})` };
|
|
1797
1800
|
}
|
|
1801
|
+
async _walkToServerSide(x, z, tolerance) {
|
|
1802
|
+
const distTo = (px, pz) => this.helpers.distance(px, pz, x, z);
|
|
1803
|
+
const MAX_ATTEMPTS = 20;
|
|
1804
|
+
let stuckCount = 0;
|
|
1805
|
+
for (let i = 0;i < MAX_ATTEMPTS; i++) {
|
|
1806
|
+
const state = this.sdk.getState();
|
|
1807
|
+
if (!state?.player)
|
|
1808
|
+
return { success: false, message: "No player state" };
|
|
1809
|
+
const { worldX: px, worldZ: pz } = state.player;
|
|
1810
|
+
if (distTo(px, pz) <= tolerance) {
|
|
1811
|
+
return { success: true, message: "Arrived" };
|
|
1812
|
+
}
|
|
1813
|
+
await this.sdk.sendWalk(x, z);
|
|
1814
|
+
const prevX = px;
|
|
1815
|
+
const prevZ = pz;
|
|
1816
|
+
await this.sdk.waitForTicks(3);
|
|
1817
|
+
const after = this.sdk.getState();
|
|
1818
|
+
if (after?.player) {
|
|
1819
|
+
const movedDist = this.helpers.distance(prevX, prevZ, after.player.worldX, after.player.worldZ);
|
|
1820
|
+
if (movedDist < 1) {
|
|
1821
|
+
stuckCount++;
|
|
1822
|
+
if (stuckCount >= 3) {
|
|
1823
|
+
return { success: false, message: `Stuck at (${after.player.worldX}, ${after.player.worldZ}) trying to reach (${x}, ${z})` };
|
|
1824
|
+
}
|
|
1825
|
+
await this.helpers.tryOpenBlockingDoor();
|
|
1826
|
+
await this.sdk.waitForTicks(1);
|
|
1827
|
+
} else {
|
|
1828
|
+
stuckCount = 0;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
const final = this.sdk.getState();
|
|
1833
|
+
const fx = final?.player?.worldX ?? 0;
|
|
1834
|
+
const fz = final?.player?.worldZ ?? 0;
|
|
1835
|
+
if (distTo(fx, fz) <= tolerance) {
|
|
1836
|
+
return { success: true, message: "Arrived" };
|
|
1837
|
+
}
|
|
1838
|
+
return { success: false, message: `Could not reach (${x}, ${z}) - stopped at (${fx}, ${fz})` };
|
|
1839
|
+
}
|
|
1798
1840
|
async closeShop(timeout = 5000) {
|
|
1799
1841
|
const state = this.sdk.getState();
|
|
1800
1842
|
if (!state?.shop.isOpen && !state?.interface?.isOpen) {
|
|
@@ -3828,12 +3870,42 @@ var tempoChain = defineChain({
|
|
|
3828
3870
|
});
|
|
3829
3871
|
var USDC_ADDRESS = process.env.USDC_ADDRESS || "0x20c0000000000000000000000000000000000000";
|
|
3830
3872
|
var FACTORY_ADDRESS = process.env.FACTORY_ADDRESS;
|
|
3873
|
+
var PENDING_FILE = join2(WALLET_DIR, "pending-wallet.json");
|
|
3831
3874
|
var SERVER_URL = (() => {
|
|
3832
3875
|
const idx = process.argv.indexOf("--server");
|
|
3833
3876
|
if (idx !== -1 && process.argv[idx + 1])
|
|
3834
3877
|
return process.argv[idx + 1];
|
|
3835
3878
|
return process.env.KILL_SWITCH_SERVER || "localhost";
|
|
3836
3879
|
})();
|
|
3880
|
+
function getWebBase() {
|
|
3881
|
+
const isLocal = SERVER_URL === "localhost" || SERVER_URL === "127.0.0.1";
|
|
3882
|
+
return isLocal ? "http://localhost:8888" : `https://${SERVER_URL}`;
|
|
3883
|
+
}
|
|
3884
|
+
function generateLinkToken() {
|
|
3885
|
+
const bytes = new Uint8Array(16);
|
|
3886
|
+
crypto.getRandomValues(bytes);
|
|
3887
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
3888
|
+
}
|
|
3889
|
+
function savePendingWallet(pending) {
|
|
3890
|
+
mkdirSync(WALLET_DIR, { recursive: true });
|
|
3891
|
+
writeFileSync(PENDING_FILE, JSON.stringify(pending, null, 2), { mode: 384 });
|
|
3892
|
+
}
|
|
3893
|
+
function loadPendingWallet() {
|
|
3894
|
+
try {
|
|
3895
|
+
if (existsSync2(PENDING_FILE)) {
|
|
3896
|
+
return JSON.parse(readFileSync(PENDING_FILE, "utf-8"));
|
|
3897
|
+
}
|
|
3898
|
+
} catch {}
|
|
3899
|
+
return null;
|
|
3900
|
+
}
|
|
3901
|
+
function clearPendingWallet() {
|
|
3902
|
+
try {
|
|
3903
|
+
if (existsSync2(PENDING_FILE)) {
|
|
3904
|
+
const fs = __require("fs");
|
|
3905
|
+
fs.unlinkSync(PENDING_FILE);
|
|
3906
|
+
}
|
|
3907
|
+
} catch {}
|
|
3908
|
+
}
|
|
3837
3909
|
var ERC20_ABI = [
|
|
3838
3910
|
{
|
|
3839
3911
|
name: "approve",
|
|
@@ -3961,40 +4033,48 @@ async function getWalletInfo() {
|
|
|
3961
4033
|
balanceRaw
|
|
3962
4034
|
};
|
|
3963
4035
|
}
|
|
3964
|
-
function
|
|
4036
|
+
function initiateWalletSetup() {
|
|
3965
4037
|
const privateKey = generatePrivateKey();
|
|
3966
4038
|
const account = privateKeyToAccount(privateKey);
|
|
3967
|
-
const
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
4039
|
+
const linkToken = generateLinkToken();
|
|
4040
|
+
const chain = TEMPO_CHAIN_ID === 4217 ? "mainnet" : "testnet";
|
|
4041
|
+
const webBase = getWebBase();
|
|
4042
|
+
const authorizeUrl = `${webBase}/authorize?key=${account.address}&t=${linkToken}&chain=${chain}`;
|
|
4043
|
+
const pending = {
|
|
4044
|
+
accessKeyPrivate: privateKey,
|
|
4045
|
+
accessKeyAddress: account.address,
|
|
4046
|
+
linkToken,
|
|
4047
|
+
authorizeUrl,
|
|
4048
|
+
createdAt: new Date().toISOString()
|
|
4049
|
+
};
|
|
4050
|
+
savePendingWallet(pending);
|
|
4051
|
+
return {
|
|
4052
|
+
accessKeyAddress: account.address,
|
|
4053
|
+
linkToken,
|
|
4054
|
+
authorizeUrl
|
|
4055
|
+
};
|
|
4056
|
+
}
|
|
4057
|
+
async function checkWalletCallback(linkToken) {
|
|
4058
|
+
try {
|
|
4059
|
+
const webBase = getWebBase();
|
|
4060
|
+
const response = await fetch(`${webBase}/api/wallet-callback?t=${linkToken}`);
|
|
4061
|
+
const data = await response.json();
|
|
4062
|
+
if (data.status === "complete" && data.tempoAddress) {
|
|
4063
|
+
return { tempoAddress: data.tempoAddress, txHash: data.txHash };
|
|
4064
|
+
}
|
|
4065
|
+
} catch {}
|
|
4066
|
+
return null;
|
|
3991
4067
|
}
|
|
3992
|
-
function
|
|
4068
|
+
function finalizeWalletSetup(tempoAddress) {
|
|
4069
|
+
const pending = loadPendingWallet();
|
|
4070
|
+
if (!pending)
|
|
4071
|
+
throw new Error("No pending wallet setup found.");
|
|
3993
4072
|
saveWallet({
|
|
3994
|
-
accessKeyPrivate:
|
|
3995
|
-
accountAddress,
|
|
4073
|
+
accessKeyPrivate: pending.accessKeyPrivate,
|
|
4074
|
+
accountAddress: tempoAddress,
|
|
3996
4075
|
createdAt: new Date().toISOString()
|
|
3997
4076
|
});
|
|
4077
|
+
clearPendingWallet();
|
|
3998
4078
|
}
|
|
3999
4079
|
async function joinTournament(tournamentAddress, username) {
|
|
4000
4080
|
const wallet = loadWallet();
|
|
@@ -4087,7 +4167,7 @@ Call \`join_game\` with no arguments to see what's available:
|
|
|
4087
4167
|
- **If no game is queued**: You'll see all available buy-in tiers (Free, $5, $15, $50, $100, $1000). Call \`join_game\` with a \`tier_id\` to start a new game.
|
|
4088
4168
|
- **If a game is active**: You must wait for it to finish.
|
|
4089
4169
|
|
|
4090
|
-
**Paid games** require a
|
|
4170
|
+
**Paid games** require a game wallet. Run \`setup_game_wallet\` and open the link in your browser — create a Tempo account with Face ID/Touch ID (no wallet app needed), authorize Kill Switch with one tap, and you're ready. Testnet accounts are auto-funded. Your buy-in is deposited on-chain when you join. No refunds once deposited — the only refund path is if the game is cancelled (not enough players).
|
|
4091
4171
|
|
|
4092
4172
|
**Game start triggers:**
|
|
4093
4173
|
- 12 players join → starts immediately
|
|
@@ -4498,16 +4578,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
4498
4578
|
},
|
|
4499
4579
|
{
|
|
4500
4580
|
name: "setup_game_wallet",
|
|
4501
|
-
description:
|
|
4581
|
+
description: "Set up a game wallet for paid tournaments. Opens a browser page where the user creates a Tempo account with Face ID/Touch ID and authorizes Kill Switch. No wallet app needed.",
|
|
4502
4582
|
inputSchema: {
|
|
4503
4583
|
type: "object",
|
|
4504
|
-
properties: {
|
|
4505
|
-
tempo_account_address: {
|
|
4506
|
-
type: "string",
|
|
4507
|
-
description: `The user's Tempo account address (0x...). Get via "tempo wallet whoami".`
|
|
4508
|
-
}
|
|
4509
|
-
},
|
|
4510
|
-
required: ["tempo_account_address"]
|
|
4584
|
+
properties: {}
|
|
4511
4585
|
}
|
|
4512
4586
|
},
|
|
4513
4587
|
{
|
|
@@ -5042,10 +5116,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
5042
5116
|
return successResponse({ message: `Disconnected "${name2}" from the game.` });
|
|
5043
5117
|
}
|
|
5044
5118
|
case "setup_game_wallet": {
|
|
5045
|
-
const tempoAddress = args?.tempo_account_address;
|
|
5046
|
-
if (!tempoAddress || !tempoAddress.startsWith("0x")) {
|
|
5047
|
-
return errorResponse('A valid Tempo account address (0x...) is required. Run "tempo wallet whoami" to get yours.');
|
|
5048
|
-
}
|
|
5049
5119
|
if (hasGameWallet()) {
|
|
5050
5120
|
try {
|
|
5051
5121
|
const info = await getWalletInfo();
|
|
@@ -5065,28 +5135,62 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
5065
5135
|
};
|
|
5066
5136
|
} catch {}
|
|
5067
5137
|
}
|
|
5068
|
-
const
|
|
5069
|
-
|
|
5138
|
+
const pending = loadPendingWallet();
|
|
5139
|
+
if (pending) {
|
|
5140
|
+
const result = await checkWalletCallback(pending.linkToken);
|
|
5141
|
+
if (result) {
|
|
5142
|
+
finalizeWalletSetup(result.tempoAddress);
|
|
5143
|
+
return {
|
|
5144
|
+
content: [{
|
|
5145
|
+
type: "text",
|
|
5146
|
+
text: [
|
|
5147
|
+
"Wallet setup complete!",
|
|
5148
|
+
"",
|
|
5149
|
+
`Tempo account: ${result.tempoAddress}`,
|
|
5150
|
+
`Access key authorized: tx ${result.txHash.slice(0, 10)}...`,
|
|
5151
|
+
"",
|
|
5152
|
+
"Your wallet is ready. Use check_wallet to see your balance."
|
|
5153
|
+
].join(`
|
|
5154
|
+
`)
|
|
5155
|
+
}]
|
|
5156
|
+
};
|
|
5157
|
+
}
|
|
5158
|
+
return {
|
|
5159
|
+
content: [{
|
|
5160
|
+
type: "text",
|
|
5161
|
+
text: [
|
|
5162
|
+
"Wallet setup is in progress. Open this link to complete it:",
|
|
5163
|
+
"",
|
|
5164
|
+
` ${pending.authorizeUrl}`,
|
|
5165
|
+
"",
|
|
5166
|
+
"Once you've completed the steps in your browser, run setup_game_wallet again."
|
|
5167
|
+
].join(`
|
|
5168
|
+
`)
|
|
5169
|
+
}]
|
|
5170
|
+
};
|
|
5171
|
+
}
|
|
5172
|
+
const { accessKeyAddress, authorizeUrl } = initiateWalletSetup();
|
|
5070
5173
|
return {
|
|
5071
5174
|
content: [{
|
|
5072
5175
|
type: "text",
|
|
5073
5176
|
text: [
|
|
5074
5177
|
"Game wallet setup started!",
|
|
5075
5178
|
"",
|
|
5076
|
-
`
|
|
5077
|
-
|
|
5179
|
+
`Access key: ${accessKeyAddress}`,
|
|
5180
|
+
"",
|
|
5181
|
+
"━━━ OPEN THIS LINK ━━━",
|
|
5078
5182
|
"",
|
|
5079
|
-
|
|
5183
|
+
` ${authorizeUrl}`,
|
|
5080
5184
|
"",
|
|
5081
|
-
"
|
|
5082
|
-
"
|
|
5185
|
+
"This will open a page where you:",
|
|
5186
|
+
"1. Create a Tempo account using Face ID / Touch ID (or sign in)",
|
|
5187
|
+
"2. Authorize Kill Switch to manage game deposits (one tap)",
|
|
5083
5188
|
"",
|
|
5084
|
-
|
|
5189
|
+
"No wallet app needed — just your browser and biometrics.",
|
|
5085
5190
|
"",
|
|
5086
|
-
"
|
|
5191
|
+
"━━━ AFTER YOU'RE DONE ━━━",
|
|
5087
5192
|
"",
|
|
5088
|
-
"
|
|
5089
|
-
"Fund your Tempo account with USDC to start joining paid tournaments."
|
|
5193
|
+
"Come back here and run setup_game_wallet again to confirm."
|
|
5090
5194
|
].join(`
|
|
5091
5195
|
`)
|
|
5092
5196
|
}]
|
|
@@ -5094,13 +5198,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
5094
5198
|
}
|
|
5095
5199
|
case "check_wallet": {
|
|
5096
5200
|
if (!hasGameWallet()) {
|
|
5097
|
-
|
|
5201
|
+
const pendingCheck = loadPendingWallet();
|
|
5202
|
+
if (pendingCheck) {
|
|
5203
|
+
const callbackResult = await checkWalletCallback(pendingCheck.linkToken);
|
|
5204
|
+
if (callbackResult) {
|
|
5205
|
+
finalizeWalletSetup(callbackResult.tempoAddress);
|
|
5206
|
+
} else {
|
|
5207
|
+
return errorResponse(`Wallet setup is in progress. Complete it in your browser:
|
|
5208
|
+
|
|
5209
|
+
${pendingCheck.authorizeUrl}
|
|
5098
5210
|
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5211
|
+
Then run check_wallet again.`);
|
|
5212
|
+
}
|
|
5213
|
+
} else {
|
|
5214
|
+
return errorResponse("No game wallet found. Run setup_game_wallet to get started.");
|
|
5215
|
+
}
|
|
5104
5216
|
}
|
|
5105
5217
|
try {
|
|
5106
5218
|
const info = await getWalletInfo();
|
|
@@ -5147,6 +5259,9 @@ function errorResponse(message) {
|
|
|
5147
5259
|
isError: true
|
|
5148
5260
|
};
|
|
5149
5261
|
}
|
|
5262
|
+
process.on("unhandledRejection", (reason) => {
|
|
5263
|
+
console.error("[Kill Switch MCP] Unhandled rejection (suppressed):", reason instanceof Error ? reason.message : reason);
|
|
5264
|
+
});
|
|
5150
5265
|
async function main() {
|
|
5151
5266
|
console.error("[Kill Switch MCP] Starting Kill Switch MCP server v3.0...");
|
|
5152
5267
|
console.error(`[Kill Switch MCP] Game server: ${SERVER_URL2}`);
|