kill-switch-mcp 1.2.4 → 1.2.6
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 +174 -61
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -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
|
}
|
|
@@ -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) {
|
|
@@ -3814,6 +3856,7 @@ import {
|
|
|
3814
3856
|
pad
|
|
3815
3857
|
} from "viem";
|
|
3816
3858
|
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
|
|
3859
|
+
import { Account } from "viem/tempo";
|
|
3817
3860
|
var WALLET_DIR = join2(homedir2(), ".killswitch");
|
|
3818
3861
|
var WALLET_FILE = join2(WALLET_DIR, "access-key.json");
|
|
3819
3862
|
var TEMPO_RPC = process.env.TEMPO_RPC_URL || "https://rpc.moderato.tempo.xyz";
|
|
@@ -3828,12 +3871,42 @@ var tempoChain = defineChain({
|
|
|
3828
3871
|
});
|
|
3829
3872
|
var USDC_ADDRESS = process.env.USDC_ADDRESS || "0x20c0000000000000000000000000000000000000";
|
|
3830
3873
|
var FACTORY_ADDRESS = process.env.FACTORY_ADDRESS;
|
|
3874
|
+
var PENDING_FILE = join2(WALLET_DIR, "pending-wallet.json");
|
|
3831
3875
|
var SERVER_URL = (() => {
|
|
3832
3876
|
const idx = process.argv.indexOf("--server");
|
|
3833
3877
|
if (idx !== -1 && process.argv[idx + 1])
|
|
3834
3878
|
return process.argv[idx + 1];
|
|
3835
3879
|
return process.env.KILL_SWITCH_SERVER || "localhost";
|
|
3836
3880
|
})();
|
|
3881
|
+
function getWebBase() {
|
|
3882
|
+
const isLocal = SERVER_URL === "localhost" || SERVER_URL === "127.0.0.1";
|
|
3883
|
+
return isLocal ? "http://localhost:8888" : `https://${SERVER_URL}`;
|
|
3884
|
+
}
|
|
3885
|
+
function generateLinkToken() {
|
|
3886
|
+
const bytes = new Uint8Array(16);
|
|
3887
|
+
crypto.getRandomValues(bytes);
|
|
3888
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
3889
|
+
}
|
|
3890
|
+
function savePendingWallet(pending) {
|
|
3891
|
+
mkdirSync(WALLET_DIR, { recursive: true });
|
|
3892
|
+
writeFileSync(PENDING_FILE, JSON.stringify(pending, null, 2), { mode: 384 });
|
|
3893
|
+
}
|
|
3894
|
+
function loadPendingWallet() {
|
|
3895
|
+
try {
|
|
3896
|
+
if (existsSync2(PENDING_FILE)) {
|
|
3897
|
+
return JSON.parse(readFileSync(PENDING_FILE, "utf-8"));
|
|
3898
|
+
}
|
|
3899
|
+
} catch {}
|
|
3900
|
+
return null;
|
|
3901
|
+
}
|
|
3902
|
+
function clearPendingWallet() {
|
|
3903
|
+
try {
|
|
3904
|
+
if (existsSync2(PENDING_FILE)) {
|
|
3905
|
+
const fs = __require("fs");
|
|
3906
|
+
fs.unlinkSync(PENDING_FILE);
|
|
3907
|
+
}
|
|
3908
|
+
} catch {}
|
|
3909
|
+
}
|
|
3837
3910
|
var ERC20_ABI = [
|
|
3838
3911
|
{
|
|
3839
3912
|
name: "approve",
|
|
@@ -3931,8 +4004,8 @@ function getPublicClient() {
|
|
|
3931
4004
|
transport: http()
|
|
3932
4005
|
});
|
|
3933
4006
|
}
|
|
3934
|
-
function getWalletClient(privateKey) {
|
|
3935
|
-
const account =
|
|
4007
|
+
function getWalletClient(privateKey, parentAddress) {
|
|
4008
|
+
const account = Account.fromSecp256k1(privateKey, { access: parentAddress });
|
|
3936
4009
|
return createWalletClient({
|
|
3937
4010
|
account,
|
|
3938
4011
|
chain: tempoChain,
|
|
@@ -3961,40 +4034,48 @@ async function getWalletInfo() {
|
|
|
3961
4034
|
balanceRaw
|
|
3962
4035
|
};
|
|
3963
4036
|
}
|
|
3964
|
-
function
|
|
4037
|
+
function initiateWalletSetup() {
|
|
3965
4038
|
const privateKey = generatePrivateKey();
|
|
3966
4039
|
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
|
-
|
|
4040
|
+
const linkToken = generateLinkToken();
|
|
4041
|
+
const chain = TEMPO_CHAIN_ID === 4217 ? "mainnet" : "testnet";
|
|
4042
|
+
const webBase = getWebBase();
|
|
4043
|
+
const authorizeUrl = `${webBase}/authorize?key=${account.address}&t=${linkToken}&chain=${chain}`;
|
|
4044
|
+
const pending = {
|
|
4045
|
+
accessKeyPrivate: privateKey,
|
|
4046
|
+
accessKeyAddress: account.address,
|
|
4047
|
+
linkToken,
|
|
4048
|
+
authorizeUrl,
|
|
4049
|
+
createdAt: new Date().toISOString()
|
|
4050
|
+
};
|
|
4051
|
+
savePendingWallet(pending);
|
|
4052
|
+
return {
|
|
4053
|
+
accessKeyAddress: account.address,
|
|
4054
|
+
linkToken,
|
|
4055
|
+
authorizeUrl
|
|
4056
|
+
};
|
|
4057
|
+
}
|
|
4058
|
+
async function checkWalletCallback(linkToken) {
|
|
4059
|
+
try {
|
|
4060
|
+
const webBase = getWebBase();
|
|
4061
|
+
const response = await fetch(`${webBase}/api/wallet-callback?t=${linkToken}`);
|
|
4062
|
+
const data = await response.json();
|
|
4063
|
+
if (data.status === "complete" && data.tempoAddress) {
|
|
4064
|
+
return { tempoAddress: data.tempoAddress, txHash: data.txHash };
|
|
4065
|
+
}
|
|
4066
|
+
} catch {}
|
|
4067
|
+
return null;
|
|
3991
4068
|
}
|
|
3992
|
-
function
|
|
4069
|
+
function finalizeWalletSetup(tempoAddress) {
|
|
4070
|
+
const pending = loadPendingWallet();
|
|
4071
|
+
if (!pending)
|
|
4072
|
+
throw new Error("No pending wallet setup found.");
|
|
3993
4073
|
saveWallet({
|
|
3994
|
-
accessKeyPrivate:
|
|
3995
|
-
accountAddress,
|
|
4074
|
+
accessKeyPrivate: pending.accessKeyPrivate,
|
|
4075
|
+
accountAddress: tempoAddress,
|
|
3996
4076
|
createdAt: new Date().toISOString()
|
|
3997
4077
|
});
|
|
4078
|
+
clearPendingWallet();
|
|
3998
4079
|
}
|
|
3999
4080
|
async function joinTournament(tournamentAddress, username) {
|
|
4000
4081
|
const wallet = loadWallet();
|
|
@@ -4002,7 +4083,7 @@ async function joinTournament(tournamentAddress, username) {
|
|
|
4002
4083
|
throw new Error("No game wallet found. Run setup_game_wallet first.");
|
|
4003
4084
|
}
|
|
4004
4085
|
const pub = getPublicClient();
|
|
4005
|
-
const walletClient = getWalletClient(wallet.accessKeyPrivate);
|
|
4086
|
+
const walletClient = getWalletClient(wallet.accessKeyPrivate, wallet.accountAddress);
|
|
4006
4087
|
const buyIn = await pub.readContract({
|
|
4007
4088
|
address: tournamentAddress,
|
|
4008
4089
|
abi: TOURNAMENT_ABI,
|
|
@@ -4087,7 +4168,7 @@ Call \`join_game\` with no arguments to see what's available:
|
|
|
4087
4168
|
- **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
4169
|
- **If a game is active**: You must wait for it to finish.
|
|
4089
4170
|
|
|
4090
|
-
**Paid games** require a
|
|
4171
|
+
**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
4172
|
|
|
4092
4173
|
**Game start triggers:**
|
|
4093
4174
|
- 12 players join → starts immediately
|
|
@@ -4498,16 +4579,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
4498
4579
|
},
|
|
4499
4580
|
{
|
|
4500
4581
|
name: "setup_game_wallet",
|
|
4501
|
-
description:
|
|
4582
|
+
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
4583
|
inputSchema: {
|
|
4503
4584
|
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"]
|
|
4585
|
+
properties: {}
|
|
4511
4586
|
}
|
|
4512
4587
|
},
|
|
4513
4588
|
{
|
|
@@ -5042,10 +5117,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
5042
5117
|
return successResponse({ message: `Disconnected "${name2}" from the game.` });
|
|
5043
5118
|
}
|
|
5044
5119
|
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
5120
|
if (hasGameWallet()) {
|
|
5050
5121
|
try {
|
|
5051
5122
|
const info = await getWalletInfo();
|
|
@@ -5065,28 +5136,62 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
5065
5136
|
};
|
|
5066
5137
|
} catch {}
|
|
5067
5138
|
}
|
|
5068
|
-
const
|
|
5069
|
-
|
|
5139
|
+
const pending = loadPendingWallet();
|
|
5140
|
+
if (pending) {
|
|
5141
|
+
const result = await checkWalletCallback(pending.linkToken);
|
|
5142
|
+
if (result) {
|
|
5143
|
+
finalizeWalletSetup(result.tempoAddress);
|
|
5144
|
+
return {
|
|
5145
|
+
content: [{
|
|
5146
|
+
type: "text",
|
|
5147
|
+
text: [
|
|
5148
|
+
"Wallet setup complete!",
|
|
5149
|
+
"",
|
|
5150
|
+
`Tempo account: ${result.tempoAddress}`,
|
|
5151
|
+
`Access key authorized: tx ${result.txHash.slice(0, 10)}...`,
|
|
5152
|
+
"",
|
|
5153
|
+
"Your wallet is ready. Use check_wallet to see your balance."
|
|
5154
|
+
].join(`
|
|
5155
|
+
`)
|
|
5156
|
+
}]
|
|
5157
|
+
};
|
|
5158
|
+
}
|
|
5159
|
+
return {
|
|
5160
|
+
content: [{
|
|
5161
|
+
type: "text",
|
|
5162
|
+
text: [
|
|
5163
|
+
"Wallet setup is in progress. Open this link to complete it:",
|
|
5164
|
+
"",
|
|
5165
|
+
` ${pending.authorizeUrl}`,
|
|
5166
|
+
"",
|
|
5167
|
+
"Once you've completed the steps in your browser, run setup_game_wallet again."
|
|
5168
|
+
].join(`
|
|
5169
|
+
`)
|
|
5170
|
+
}]
|
|
5171
|
+
};
|
|
5172
|
+
}
|
|
5173
|
+
const { accessKeyAddress, authorizeUrl } = initiateWalletSetup();
|
|
5070
5174
|
return {
|
|
5071
5175
|
content: [{
|
|
5072
5176
|
type: "text",
|
|
5073
5177
|
text: [
|
|
5074
5178
|
"Game wallet setup started!",
|
|
5075
5179
|
"",
|
|
5076
|
-
`
|
|
5077
|
-
`Generated access key: ${accessKeyAddress}`,
|
|
5180
|
+
`Access key: ${accessKeyAddress}`,
|
|
5078
5181
|
"",
|
|
5079
|
-
"
|
|
5182
|
+
"━━━ OPEN THIS LINK ━━━",
|
|
5080
5183
|
"",
|
|
5081
|
-
|
|
5082
|
-
"This requires your passkey (biometric confirmation).",
|
|
5184
|
+
` ${authorizeUrl}`,
|
|
5083
5185
|
"",
|
|
5084
|
-
|
|
5186
|
+
"This will open a page where you:",
|
|
5187
|
+
"1. Create a Tempo account using Face ID / Touch ID (or sign in)",
|
|
5188
|
+
"2. Authorize Kill Switch to manage game deposits (one tap)",
|
|
5085
5189
|
"",
|
|
5086
|
-
"
|
|
5190
|
+
"No wallet app needed — just your browser and biometrics.",
|
|
5087
5191
|
"",
|
|
5088
|
-
"
|
|
5089
|
-
"
|
|
5192
|
+
"━━━ AFTER YOU'RE DONE ━━━",
|
|
5193
|
+
"",
|
|
5194
|
+
"Come back here and run setup_game_wallet again to confirm."
|
|
5090
5195
|
].join(`
|
|
5091
5196
|
`)
|
|
5092
5197
|
}]
|
|
@@ -5094,13 +5199,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
5094
5199
|
}
|
|
5095
5200
|
case "check_wallet": {
|
|
5096
5201
|
if (!hasGameWallet()) {
|
|
5097
|
-
|
|
5202
|
+
const pendingCheck = loadPendingWallet();
|
|
5203
|
+
if (pendingCheck) {
|
|
5204
|
+
const callbackResult = await checkWalletCallback(pendingCheck.linkToken);
|
|
5205
|
+
if (callbackResult) {
|
|
5206
|
+
finalizeWalletSetup(callbackResult.tempoAddress);
|
|
5207
|
+
} else {
|
|
5208
|
+
return errorResponse(`Wallet setup is in progress. Complete it in your browser:
|
|
5098
5209
|
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5210
|
+
${pendingCheck.authorizeUrl}
|
|
5211
|
+
|
|
5212
|
+
Then run check_wallet again.`);
|
|
5213
|
+
}
|
|
5214
|
+
} else {
|
|
5215
|
+
return errorResponse("No game wallet found. Run setup_game_wallet to get started.");
|
|
5216
|
+
}
|
|
5104
5217
|
}
|
|
5105
5218
|
try {
|
|
5106
5219
|
const info = await getWalletInfo();
|