kill-switch-mcp 1.1.10 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.js +202 -218
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { existsSync as existsSync3 } from "fs";
|
|
15
15
|
import { readFile as readFile2 } from "fs/promises";
|
|
16
16
|
import { join as join3 } from "path";
|
|
17
|
+
import { homedir as homedir3 } from "os";
|
|
17
18
|
|
|
18
19
|
// src/sdk/types.ts
|
|
19
20
|
var PRAYER_NAMES = [
|
|
@@ -3452,6 +3453,7 @@ class BotActions {
|
|
|
3452
3453
|
import { readFile } from "fs/promises";
|
|
3453
3454
|
import { join } from "path";
|
|
3454
3455
|
import { existsSync } from "fs";
|
|
3456
|
+
import { homedir } from "os";
|
|
3455
3457
|
|
|
3456
3458
|
class BotManager {
|
|
3457
3459
|
connections = new Map;
|
|
@@ -3471,9 +3473,9 @@ class BotManager {
|
|
|
3471
3473
|
let gateway = gatewayUrl || this.defaultGatewayUrl;
|
|
3472
3474
|
let showChat = false;
|
|
3473
3475
|
if (!password) {
|
|
3474
|
-
const envPath = join(
|
|
3476
|
+
const envPath = join(homedir(), ".killswitch", "bots", name, "bot.env");
|
|
3475
3477
|
if (!existsSync(envPath)) {
|
|
3476
|
-
throw new Error(`Bot "${name}" not found. No bots/${name}/bot.env file exists. Call login with an agent_name to create one.`);
|
|
3478
|
+
throw new Error(`Bot "${name}" not found. No ~/.killswitch/bots/${name}/bot.env file exists. Call login with an agent_name to create one.`);
|
|
3477
3479
|
}
|
|
3478
3480
|
const envContent = await readFile(envPath, "utf-8");
|
|
3479
3481
|
const env = this.parseEnv(envContent);
|
|
@@ -3801,7 +3803,7 @@ function formatWorldState(state, stateAgeMs) {
|
|
|
3801
3803
|
// src/wallet/index.ts
|
|
3802
3804
|
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
3803
3805
|
import { join as join2 } from "path";
|
|
3804
|
-
import { homedir } from "os";
|
|
3806
|
+
import { homedir as homedir2 } from "os";
|
|
3805
3807
|
import {
|
|
3806
3808
|
createPublicClient,
|
|
3807
3809
|
createWalletClient,
|
|
@@ -3812,7 +3814,7 @@ import {
|
|
|
3812
3814
|
pad
|
|
3813
3815
|
} from "viem";
|
|
3814
3816
|
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
|
|
3815
|
-
var WALLET_DIR = join2(
|
|
3817
|
+
var WALLET_DIR = join2(homedir2(), ".killswitch");
|
|
3816
3818
|
var WALLET_FILE = join2(WALLET_DIR, "access-key.json");
|
|
3817
3819
|
var TEMPO_RPC = process.env.TEMPO_RPC_URL || "https://rpc.moderato.tempo.xyz";
|
|
3818
3820
|
var TEMPO_CHAIN_ID = parseInt(process.env.TEMPO_CHAIN_ID || "42431");
|
|
@@ -4067,81 +4069,144 @@ var SERVER_URL2 = (() => {
|
|
|
4067
4069
|
return process.env.KILL_SWITCH_SERVER || "localhost";
|
|
4068
4070
|
})();
|
|
4069
4071
|
console.error(`[Kill Switch MCP] Server: ${SERVER_URL2}`);
|
|
4070
|
-
var
|
|
4072
|
+
var KILLSWITCH_HOME = join3(homedir3(), ".killswitch");
|
|
4073
|
+
var BOTS_DIR = join3(KILLSWITCH_HOME, "bots");
|
|
4074
|
+
var GUIDE = `# Kill Switch — Player Guide
|
|
4075
|
+
|
|
4076
|
+
You are playing Kill Switch — a Hunger Games-style battle royale where AI agents fight to the death.
|
|
4077
|
+
|
|
4078
|
+
YOU are the brain of your bot. Your human talks strategy, you execute it in the game.
|
|
4079
|
+
|
|
4080
|
+
**PERMADEATH**: If your agent dies, it's dead forever. No respawns. Only the last one standing wins.
|
|
4081
|
+
|
|
4082
|
+
---
|
|
4071
4083
|
|
|
4072
|
-
|
|
4073
|
-
execute it by sending code to the game via execute_code.
|
|
4084
|
+
## Quick Start
|
|
4074
4085
|
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4086
|
+
1. \`login\` — connects to the game (creates account if needed)
|
|
4087
|
+
2. \`join_game\` with mode \`"shorty"\` — queues you up
|
|
4088
|
+
3. \`wait_for_game_start\` — blocks until teleported to the arena
|
|
4089
|
+
4. \`execute_code\` — fight! Use \`bot\`, \`sdk\`, and \`actions\` globals.
|
|
4090
|
+
|
|
4091
|
+
Before writing any execute_code, read \`killswitch://sdk-reference\` for the complete API.
|
|
4092
|
+
|
|
4093
|
+
---
|
|
4082
4094
|
|
|
4083
4095
|
## Game Modes
|
|
4084
|
-
|
|
4085
|
-
|
|
4096
|
+
|
|
4097
|
+
### Shorty (Quick Battle Royale)
|
|
4098
|
+
- **Arena**: Draynor Manor fenced grounds
|
|
4099
|
+
- **Stats**: 50 Attack, 50 Strength, 50 Defence, 99 Hitpoints
|
|
4100
|
+
- **Start**: Empty inventory — everything comes from ground loot
|
|
4101
|
+
- **Win condition**: Last agent alive
|
|
4102
|
+
|
|
4103
|
+
### Longy (Coming Soon)
|
|
4104
|
+
- Large map: Falador to Port Sarim
|
|
4105
|
+
- Level 1 everything — skill up, craft gear, fish food, outlast everyone
|
|
4106
|
+
|
|
4107
|
+
---
|
|
4086
4108
|
|
|
4087
4109
|
## The Arena (Shorty)
|
|
4088
|
-
- You spawn inside Draynor Manor's fenced arena with nothing
|
|
4089
|
-
- Items are scattered on the ground in a cornucopia pattern:
|
|
4090
|
-
- Outer ring: bronze/iron weapons, leather armor, bread/meat
|
|
4091
|
-
- Mid ring: mithril/adamant weapons, chainmail, trout/salmon
|
|
4092
|
-
- Center: rune weapons, rune/adamant armor, lobster/swordfish
|
|
4093
|
-
- Better items are closer to the center — but so is everyone else!
|
|
4094
4110
|
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
-
|
|
4111
|
+
You spawn on the perimeter of Draynor Manor with **nothing**. Items are scattered in a cornucopia pattern:
|
|
4112
|
+
|
|
4113
|
+
- **Outer ring** (safe, near spawn): Bronze/iron weapons, leather armor, bread/meat
|
|
4114
|
+
- **Mid ring**: Mithril/adamant weapons, chainmail, trout/salmon
|
|
4115
|
+
- **Center** (dangerous, high traffic): Rune weapons, rune/adamant armor, lobster/swordfish
|
|
4098
4116
|
|
|
4099
|
-
|
|
4117
|
+
**Better loot = more risk.** Everyone converges on the center.
|
|
4100
4118
|
|
|
4101
|
-
|
|
4102
|
-
- **bot**: High-level actions (await required — they wait for the effect to complete)
|
|
4103
|
-
- **sdk**: Low-level state access and direct commands
|
|
4104
|
-
- **actions**: Pre-built strategy functions from your bot's actions/ directory
|
|
4119
|
+
---
|
|
4105
4120
|
|
|
4106
|
-
|
|
4107
|
-
- bot.pickupItem(/item name/i) — walk to and pick up a ground item
|
|
4108
|
-
- bot.equipItem(/item name/i) — equip from inventory
|
|
4109
|
-
- bot.eatFood(/item name/i) — eat food to heal
|
|
4110
|
-
- bot.attackPlayer("name" or /pattern/i) — attack a player
|
|
4111
|
-
- bot.attackNpc("name" or /pattern/i) — attack an NPC
|
|
4112
|
-
- bot.walkTo(x, z) — pathfind and walk (complex, uses server pathfinding)
|
|
4121
|
+
## How to Play
|
|
4113
4122
|
|
|
4114
|
-
###
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4123
|
+
### Interactive Mode (default)
|
|
4124
|
+
Your human gives you strategy between rounds:
|
|
4125
|
+
- "Rush the center, grab the best gear"
|
|
4126
|
+
- "Play safe, loot the outer ring and wait"
|
|
4127
|
+
- "Focus the weakest player"
|
|
4119
4128
|
|
|
4120
|
-
|
|
4121
|
-
- sdk.getState().player.worldX / .worldZ — your position
|
|
4122
|
-
- sdk.getState().player.hp / .maxHp — your hitpoints
|
|
4123
|
-
- Ground items use .x / .z (NOT .worldX)
|
|
4129
|
+
You turn their words into execute_code calls. Keep each call to **15-30 seconds**, then report back what happened so they can adjust.
|
|
4124
4130
|
|
|
4125
|
-
###
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
-
|
|
4129
|
-
|
|
4131
|
+
### Autonomous Mode (\`claude -p\`)
|
|
4132
|
+
Run a full game with no human input:
|
|
4133
|
+
\`\`\`
|
|
4134
|
+
claude -p 'Play Kill Switch. Create agent named Fury. Join shorty, play aggressively. Exit when done.'
|
|
4135
|
+
\`\`\`
|
|
4136
|
+
Claude makes all decisions. Same tools, same flow — just no human between rounds.
|
|
4137
|
+
|
|
4138
|
+
---
|
|
4130
4139
|
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4140
|
+
## Pre-Built Actions
|
|
4141
|
+
|
|
4142
|
+
Your bot ships with action functions in the \`actions\` object. **Use these first** — they handle the common patterns:
|
|
4143
|
+
|
|
4144
|
+
\`\`\`typescript
|
|
4145
|
+
// Grab nearby items and equip best gear
|
|
4146
|
+
await actions.lootAndEquip({ maxItems: 5 });
|
|
4147
|
+
|
|
4148
|
+
// Fight nearest player with auto-eat (15 seconds)
|
|
4149
|
+
await actions.fightLoop({ eatAt: 25, duration: 15000 });
|
|
4150
|
+
|
|
4151
|
+
// Fight a specific player
|
|
4152
|
+
await actions.fightLoop({ target: /vex/i, eatAt: 20 });
|
|
4153
|
+
|
|
4154
|
+
// Retreat while eating to recover HP
|
|
4155
|
+
await actions.kite({ safeHp: 40 });
|
|
4156
|
+
\`\`\`
|
|
4139
4157
|
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4158
|
+
Combine them for a full turn:
|
|
4159
|
+
\`\`\`typescript
|
|
4160
|
+
const loot = await actions.lootAndEquip({ maxItems: 3 });
|
|
4161
|
+
const result = await actions.fightLoop({ eatAt: 25, duration: 15000 });
|
|
4162
|
+
if (result.hp < 15 && result.foodLeft > 0) {
|
|
4163
|
+
await actions.kite({ safeHp: 35 });
|
|
4164
|
+
}
|
|
4165
|
+
return result;
|
|
4166
|
+
\`\`\`
|
|
4167
|
+
|
|
4168
|
+
### Custom Actions
|
|
4169
|
+
Create custom actions in \`~/.killswitch/bots/<name>/actions/\`. Each \`.ts\` file exports a default function that becomes \`actions.functionName()\`. Re-login to pick up new actions.
|
|
4170
|
+
|
|
4171
|
+
---
|
|
4172
|
+
|
|
4173
|
+
## Key Rules
|
|
4174
|
+
|
|
4175
|
+
1. **Use MCP tools only** — never bash commands or scripts for gameplay
|
|
4176
|
+
2. **Read killswitch://sdk-reference** before writing execute_code
|
|
4177
|
+
3. **Pick up items first** — you start empty, looting is survival
|
|
4178
|
+
4. **Equip before fighting** — weapon first, then armor
|
|
4179
|
+
5. **Eat when HP < 25** — any food is better than no food
|
|
4180
|
+
6. **Keep execute_code calls short** (15-30s) — return state, evaluate, adapt
|
|
4181
|
+
7. **You cannot see other players' HP or inventory** — only name, combat level, position, distance
|
|
4182
|
+
8. **bot methods throw on failure** — use try/catch if you want to handle errors
|
|
4183
|
+
9. **sdk.sendWalk(x, z) for movement** — bot.walkTo() uses server pathfinding (slower, can fail)
|
|
4184
|
+
10. **PERMADEATH** — play smart!
|
|
4185
|
+
|
|
4186
|
+
---
|
|
4187
|
+
|
|
4188
|
+
## Bot Data
|
|
4189
|
+
|
|
4190
|
+
Bot credentials and custom actions are stored in \`~/.killswitch/bots/<name>/\`:
|
|
4191
|
+
\`\`\`
|
|
4192
|
+
~/.killswitch/
|
|
4193
|
+
bots/
|
|
4194
|
+
<name>/
|
|
4195
|
+
bot.env # BOT_USERNAME, PASSWORD, SERVER
|
|
4196
|
+
actions/ # Custom action files (.ts)
|
|
4197
|
+
\`\`\`
|
|
4198
|
+
|
|
4199
|
+
The \`login\` tool creates this automatically. You don't need to set up files manually.
|
|
4200
|
+
|
|
4201
|
+
---
|
|
4202
|
+
|
|
4203
|
+
## Troubleshooting
|
|
4204
|
+
|
|
4205
|
+
- **"Not connected"** — Call \`login\` again. It reconnects automatically.
|
|
4206
|
+
- **"No game state"** — The browser client needs a moment to load. Wait a few seconds.
|
|
4207
|
+
- **Agent is dead** — That agent is gone forever (permadeath). Create a new one with a different name.
|
|
4208
|
+
- **Items not picking up** — Check the SDK reference for correct method signatures and property names.
|
|
4209
|
+
`;
|
|
4145
4210
|
var server = new Server({ name: "kill-switch", version: "3.0.0" }, { capabilities: { tools: {}, resources: {} } });
|
|
4146
4211
|
var SDK_REFERENCE = `# Kill Switch SDK Reference
|
|
4147
4212
|
|
|
@@ -4313,10 +4378,16 @@ For combat/arena play, use \`sdk.sendWalk(x, z)\` + \`await sdk.waitForTicks(n)\
|
|
|
4313
4378
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
4314
4379
|
return {
|
|
4315
4380
|
resources: [
|
|
4381
|
+
{
|
|
4382
|
+
uri: "killswitch://guide",
|
|
4383
|
+
name: "Kill Switch Player Guide",
|
|
4384
|
+
description: "START HERE. Game overview, how to play, common patterns, and key rules. Read this before doing anything.",
|
|
4385
|
+
mimeType: "text/markdown"
|
|
4386
|
+
},
|
|
4316
4387
|
{
|
|
4317
4388
|
uri: "killswitch://sdk-reference",
|
|
4318
4389
|
name: "Kill Switch SDK Reference",
|
|
4319
|
-
description: "Complete API reference for bot, sdk, and actions objects used in execute_code.
|
|
4390
|
+
description: "Complete API reference for bot, sdk, and actions objects used in execute_code. Read before writing game code.",
|
|
4320
4391
|
mimeType: "text/markdown"
|
|
4321
4392
|
}
|
|
4322
4393
|
]
|
|
@@ -4324,6 +4395,17 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
4324
4395
|
});
|
|
4325
4396
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
4326
4397
|
const { uri } = request.params;
|
|
4398
|
+
if (uri === "killswitch://guide") {
|
|
4399
|
+
return {
|
|
4400
|
+
contents: [
|
|
4401
|
+
{
|
|
4402
|
+
uri: "killswitch://guide",
|
|
4403
|
+
mimeType: "text/markdown",
|
|
4404
|
+
text: GUIDE
|
|
4405
|
+
}
|
|
4406
|
+
]
|
|
4407
|
+
};
|
|
4408
|
+
}
|
|
4327
4409
|
if (uri === "killswitch://sdk-reference") {
|
|
4328
4410
|
return {
|
|
4329
4411
|
contents: [
|
|
@@ -4342,44 +4424,26 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
4342
4424
|
tools: [
|
|
4343
4425
|
{
|
|
4344
4426
|
name: "login",
|
|
4345
|
-
description:
|
|
4346
|
-
|
|
4347
|
-
Call this when the user says anything like "let's play", "join the game", "log in", "connect", etc.
|
|
4348
|
-
|
|
4349
|
-
If the user already has a bot, this will reconnect. If not, it creates a new one. Idempotent — safe to call multiple times.
|
|
4350
|
-
|
|
4351
|
-
${GAME_DESCRIPTION}`,
|
|
4427
|
+
description: "Log in to Kill Switch. Creates account if needed, connects to game server, opens browser client. Idempotent — safe to call again. Read killswitch://guide first for game overview.",
|
|
4352
4428
|
inputSchema: {
|
|
4353
4429
|
type: "object",
|
|
4354
4430
|
properties: {
|
|
4355
4431
|
agent_name: {
|
|
4356
4432
|
type: "string",
|
|
4357
|
-
description: "Name for the bot (max 12 chars, alphanumeric). If not provided,
|
|
4433
|
+
description: "Name for the bot (max 12 chars, alphanumeric). If not provided, reuses existing bot or generates random name."
|
|
4358
4434
|
}
|
|
4359
4435
|
}
|
|
4360
4436
|
}
|
|
4361
4437
|
},
|
|
4362
4438
|
{
|
|
4363
4439
|
name: "execute_code",
|
|
4364
|
-
description:
|
|
4365
|
-
|
|
4366
|
-
IMPORTANT: Read the "Kill Switch SDK Reference" resource (killswitch://sdk-reference) for the complete API.
|
|
4367
|
-
Do NOT guess method names or property paths — only use what is documented in the SDK reference.
|
|
4368
|
-
|
|
4369
|
-
Quick reminders:
|
|
4370
|
-
- bot methods are async and THROW on failure
|
|
4371
|
-
- sdk.getState().player.worldX / .worldZ / .hp / .maxHp
|
|
4372
|
-
- Ground items use .x / .z (NOT .worldX)
|
|
4373
|
-
- sdk.sendWalk(x, z) for fast movement, bot.walkTo(x, z) for pathfinding
|
|
4374
|
-
- Blocking UI is auto-dismissed — do NOT call bot.dismissBlockingUI()
|
|
4375
|
-
|
|
4376
|
-
You MUST call login before using this tool.`,
|
|
4440
|
+
description: "Execute TypeScript code on your bot. Globals: bot, sdk, actions. Read killswitch://sdk-reference for API. Bot methods throw on failure. Blocking UI is auto-dismissed.",
|
|
4377
4441
|
inputSchema: {
|
|
4378
4442
|
type: "object",
|
|
4379
4443
|
properties: {
|
|
4380
4444
|
code: {
|
|
4381
4445
|
type: "string",
|
|
4382
|
-
description: "TypeScript code to execute. Has access to bot (BotActions)
|
|
4446
|
+
description: "TypeScript code to execute. Has access to bot (BotActions), sdk (BotSDK), and actions."
|
|
4383
4447
|
},
|
|
4384
4448
|
timeout: {
|
|
4385
4449
|
type: "number",
|
|
@@ -4391,7 +4455,7 @@ You MUST call login before using this tool.`,
|
|
|
4391
4455
|
},
|
|
4392
4456
|
{
|
|
4393
4457
|
name: "get_status",
|
|
4394
|
-
description: "Check
|
|
4458
|
+
description: "Check bot state (position, HP, inventory, nearby players) without executing code.",
|
|
4395
4459
|
inputSchema: {
|
|
4396
4460
|
type: "object",
|
|
4397
4461
|
properties: {}
|
|
@@ -4399,16 +4463,7 @@ You MUST call login before using this tool.`,
|
|
|
4399
4463
|
},
|
|
4400
4464
|
{
|
|
4401
4465
|
name: "wait_for_game_start",
|
|
4402
|
-
description:
|
|
4403
|
-
|
|
4404
|
-
This tool blocks until the game starts and your bot is teleported to the arena. When it returns, the fight is ON — immediately start your combat loop!
|
|
4405
|
-
|
|
4406
|
-
Typical flow:
|
|
4407
|
-
1. login → connect to game
|
|
4408
|
-
2. join_game → pick a mode
|
|
4409
|
-
3. Chat strategy with human
|
|
4410
|
-
4. wait_for_game_start → blocks until game starts
|
|
4411
|
-
5. execute_code → start fighting!`,
|
|
4466
|
+
description: "Block until the match starts and you are teleported to the arena. Call after join_game.",
|
|
4412
4467
|
inputSchema: {
|
|
4413
4468
|
type: "object",
|
|
4414
4469
|
properties: {
|
|
@@ -4421,20 +4476,14 @@ Typical flow:
|
|
|
4421
4476
|
},
|
|
4422
4477
|
{
|
|
4423
4478
|
name: "join_game",
|
|
4424
|
-
description:
|
|
4425
|
-
|
|
4426
|
-
Available modes:
|
|
4427
|
-
- "shorty": Quick battle royale at Draynor Manor. 50 atk/str/def, 99 HP. Grab loot, fight, last one standing wins.
|
|
4428
|
-
- "longy": (Coming soon) Large survival map with skill progression.
|
|
4429
|
-
|
|
4430
|
-
After joining, call wait_for_game_start to wait for the game to begin.`,
|
|
4479
|
+
description: "Join a game mode queue. Call after login. After joining, call wait_for_game_start.",
|
|
4431
4480
|
inputSchema: {
|
|
4432
4481
|
type: "object",
|
|
4433
4482
|
properties: {
|
|
4434
4483
|
mode: {
|
|
4435
4484
|
type: "string",
|
|
4436
4485
|
enum: ["shorty", "longy"],
|
|
4437
|
-
description:
|
|
4486
|
+
description: 'Game mode: "shorty" (quick battle royale) or "longy" (coming soon)'
|
|
4438
4487
|
}
|
|
4439
4488
|
},
|
|
4440
4489
|
required: ["mode"]
|
|
@@ -4442,7 +4491,7 @@ After joining, call wait_for_game_start to wait for the game to begin.`,
|
|
|
4442
4491
|
},
|
|
4443
4492
|
{
|
|
4444
4493
|
name: "disconnect_bot",
|
|
4445
|
-
description: "Disconnect from the game.
|
|
4494
|
+
description: "Disconnect from the game.",
|
|
4446
4495
|
inputSchema: {
|
|
4447
4496
|
type: "object",
|
|
4448
4497
|
properties: {}
|
|
@@ -4450,23 +4499,13 @@ After joining, call wait_for_game_start to wait for the game to begin.`,
|
|
|
4450
4499
|
},
|
|
4451
4500
|
{
|
|
4452
4501
|
name: "setup_game_wallet",
|
|
4453
|
-
description:
|
|
4454
|
-
|
|
4455
|
-
IMPORTANT: This tool handles real (or testnet) money. Explain to the user what's happening at each step.
|
|
4456
|
-
|
|
4457
|
-
Prerequisites:
|
|
4458
|
-
- The user must have the Tempo CLI installed (curl -fsSL https://tempo.xyz/install | bash)
|
|
4459
|
-
- The user must have run "tempo wallet login" to create their Tempo account
|
|
4460
|
-
|
|
4461
|
-
This tool generates a local access key. The user then needs to authorize it on their Tempo account (requires biometric/passkey confirmation). After that, the key is stored locally in ~/.killswitch/ and used for all tournament deposits.
|
|
4462
|
-
|
|
4463
|
-
Learn more about Tempo access keys: https://docs.tempo.xyz/protocol/tips/tip-1011`,
|
|
4502
|
+
description: 'Set up a Tempo wallet access key for paid tournaments. Requires Tempo CLI installed and "tempo wallet login" completed. Handles real money — explain each step to user.',
|
|
4464
4503
|
inputSchema: {
|
|
4465
4504
|
type: "object",
|
|
4466
4505
|
properties: {
|
|
4467
4506
|
tempo_account_address: {
|
|
4468
4507
|
type: "string",
|
|
4469
|
-
description: `The user's Tempo account address (0x...). Get
|
|
4508
|
+
description: `The user's Tempo account address (0x...). Get via "tempo wallet whoami".`
|
|
4470
4509
|
}
|
|
4471
4510
|
},
|
|
4472
4511
|
required: ["tempo_account_address"]
|
|
@@ -4474,9 +4513,7 @@ Learn more about Tempo access keys: https://docs.tempo.xyz/protocol/tips/tip-101
|
|
|
4474
4513
|
},
|
|
4475
4514
|
{
|
|
4476
4515
|
name: "check_wallet",
|
|
4477
|
-
description:
|
|
4478
|
-
|
|
4479
|
-
If no game wallet is set up, this will tell you to run setup_game_wallet first.`,
|
|
4516
|
+
description: "Check game wallet address and USDC balance for tournament buy-ins.",
|
|
4480
4517
|
inputSchema: {
|
|
4481
4518
|
type: "object",
|
|
4482
4519
|
properties: {}
|
|
@@ -4484,9 +4521,7 @@ If no game wallet is set up, this will tell you to run setup_game_wallet first.`
|
|
|
4484
4521
|
},
|
|
4485
4522
|
{
|
|
4486
4523
|
name: "tournament_schedule",
|
|
4487
|
-
description:
|
|
4488
|
-
|
|
4489
|
-
This is a read-only tool — it doesn't cost anything to check the schedule.`,
|
|
4524
|
+
description: "View upcoming paid tournaments — start times, buy-ins, player counts. Read-only.",
|
|
4490
4525
|
inputSchema: {
|
|
4491
4526
|
type: "object",
|
|
4492
4527
|
properties: {}
|
|
@@ -4494,26 +4529,13 @@ This is a read-only tool — it doesn't cost anything to check the schedule.`,
|
|
|
4494
4529
|
},
|
|
4495
4530
|
{
|
|
4496
4531
|
name: "join_tournament",
|
|
4497
|
-
description:
|
|
4498
|
-
|
|
4499
|
-
THIS TOOL SPENDS REAL MONEY (or testnet money). Before calling this tool:
|
|
4500
|
-
1. Tell the user exactly which tournament they're joining (time, buy-in amount)
|
|
4501
|
-
2. Show their current wallet balance
|
|
4502
|
-
3. Ask them to confirm they want to proceed
|
|
4503
|
-
4. Only then call this tool
|
|
4504
|
-
|
|
4505
|
-
The deposit is sent on-chain to the tournament's escrow contract. The user's buy-in is held in escrow until:
|
|
4506
|
-
- They win → they receive 90% of the pot
|
|
4507
|
-
- The match is cancelled → they get a full refund
|
|
4508
|
-
- The server goes down → they can claim a refund after 1 hour
|
|
4509
|
-
|
|
4510
|
-
The user must be logged in (call login first) so we know their in-game username.`,
|
|
4532
|
+
description: "Join a paid tournament by depositing buy-in from game wallet. SPENDS REAL MONEY. Confirm with user before calling. Must be logged in.",
|
|
4511
4533
|
inputSchema: {
|
|
4512
4534
|
type: "object",
|
|
4513
4535
|
properties: {
|
|
4514
4536
|
tournament_address: {
|
|
4515
4537
|
type: "string",
|
|
4516
|
-
description: "
|
|
4538
|
+
description: "Tournament contract address (from tournament_schedule)"
|
|
4517
4539
|
}
|
|
4518
4540
|
},
|
|
4519
4541
|
required: ["tournament_address"]
|
|
@@ -4529,8 +4551,7 @@ async function loadActions(botName, bot, sdk) {
|
|
|
4529
4551
|
const { existsSync: existsSync4, readdirSync, cpSync, readFileSync: readFileSync2 } = await import("fs");
|
|
4530
4552
|
const { join: join4, basename } = await import("path");
|
|
4531
4553
|
const { pathToFileURL } = await import("url");
|
|
4532
|
-
const
|
|
4533
|
-
const actionsDir = join4(botsDir, botName, "actions");
|
|
4554
|
+
const actionsDir = join4(BOTS_DIR, botName, "actions");
|
|
4534
4555
|
const defaultsDir = join4(new URL(".", import.meta.url).pathname, "..", "defaults", "actions");
|
|
4535
4556
|
console.error(`[Kill Switch] Loading actions from ${actionsDir}`);
|
|
4536
4557
|
if (!existsSync4(actionsDir) && existsSync4(defaultsDir)) {
|
|
@@ -4619,20 +4640,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
4619
4640
|
const parts = [`Already connected as "${activeBotName}".`];
|
|
4620
4641
|
if (state) {
|
|
4621
4642
|
parts.push("");
|
|
4622
|
-
parts.push("── Current State ──");
|
|
4623
4643
|
parts.push(formatWorldState(state, existing.sdk.getStateAge()));
|
|
4624
4644
|
}
|
|
4625
|
-
parts.push("");
|
|
4626
|
-
parts.push("Your bot is in the game. Talk strategy with your human while waiting for the tournament to start!");
|
|
4627
4645
|
return { content: [{ type: "text", text: parts.join(`
|
|
4628
4646
|
`) }] };
|
|
4629
4647
|
}
|
|
4630
4648
|
}
|
|
4631
|
-
const botsDir = join3(process.cwd(), "bots");
|
|
4632
4649
|
let username;
|
|
4633
4650
|
let password;
|
|
4651
|
+
const legacyBotsDir = join3(process.cwd(), "bots");
|
|
4652
|
+
if (botName && !existsSync3(join3(BOTS_DIR, botName, "bot.env")) && existsSync3(join3(legacyBotsDir, botName, "bot.env"))) {
|
|
4653
|
+
const { mkdirSync: mkdirSync2, cpSync } = await import("fs");
|
|
4654
|
+
mkdirSync2(join3(BOTS_DIR, botName), { recursive: true });
|
|
4655
|
+
cpSync(join3(legacyBotsDir, botName), join3(BOTS_DIR, botName), { recursive: true });
|
|
4656
|
+
console.error(`[Kill Switch] Migrated bot "${botName}" from ./bots/ to ~/.killswitch/bots/`);
|
|
4657
|
+
}
|
|
4634
4658
|
if (botName) {
|
|
4635
|
-
const envPath = join3(
|
|
4659
|
+
const envPath = join3(BOTS_DIR, botName, "bot.env");
|
|
4636
4660
|
if (existsSync3(envPath)) {
|
|
4637
4661
|
console.error(`[Kill Switch] Found existing bot "${botName}"`);
|
|
4638
4662
|
const env = parseEnv(await readFile2(envPath, "utf-8"));
|
|
@@ -4643,7 +4667,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
4643
4667
|
username = botName;
|
|
4644
4668
|
password = generateRandomPassword();
|
|
4645
4669
|
const { mkdirSync: mkdirSync2, writeFileSync: writeFileSync2 } = await import("fs");
|
|
4646
|
-
const botDir = join3(
|
|
4670
|
+
const botDir = join3(BOTS_DIR, botName);
|
|
4647
4671
|
mkdirSync2(botDir, { recursive: true });
|
|
4648
4672
|
writeFileSync2(join3(botDir, "bot.env"), [
|
|
4649
4673
|
`BOT_USERNAME=${username}`,
|
|
@@ -4654,21 +4678,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
4654
4678
|
`));
|
|
4655
4679
|
}
|
|
4656
4680
|
} else {
|
|
4657
|
-
const { readdirSync } = await import("fs");
|
|
4658
|
-
if (existsSync3(
|
|
4659
|
-
const
|
|
4681
|
+
const { readdirSync, mkdirSync: mkdirSync2, writeFileSync: writeFileSync2, cpSync } = await import("fs");
|
|
4682
|
+
if (existsSync3(legacyBotsDir)) {
|
|
4683
|
+
const legacyDirs = readdirSync(legacyBotsDir).filter((d) => d !== "_template" && existsSync3(join3(legacyBotsDir, d, "bot.env")));
|
|
4684
|
+
for (const legacyName of legacyDirs) {
|
|
4685
|
+
if (!existsSync3(join3(BOTS_DIR, legacyName, "bot.env"))) {
|
|
4686
|
+
mkdirSync2(join3(BOTS_DIR, legacyName), { recursive: true });
|
|
4687
|
+
cpSync(join3(legacyBotsDir, legacyName), join3(BOTS_DIR, legacyName), { recursive: true });
|
|
4688
|
+
console.error(`[Kill Switch] Migrated bot "${legacyName}" from ./bots/ to ~/.killswitch/bots/`);
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
if (existsSync3(BOTS_DIR)) {
|
|
4693
|
+
const dirs = readdirSync(BOTS_DIR).filter((d) => d !== "_template" && existsSync3(join3(BOTS_DIR, d, "bot.env")));
|
|
4660
4694
|
if (dirs.length > 0) {
|
|
4661
4695
|
botName = dirs[0];
|
|
4662
4696
|
console.error(`[Kill Switch] Reusing existing bot "${botName}"`);
|
|
4663
|
-
const env = parseEnv(await readFile2(join3(
|
|
4697
|
+
const env = parseEnv(await readFile2(join3(BOTS_DIR, botName, "bot.env"), "utf-8"));
|
|
4664
4698
|
username = env.BOT_USERNAME || botName;
|
|
4665
4699
|
password = env.PASSWORD || "";
|
|
4666
4700
|
} else {
|
|
4667
4701
|
botName = generateRandomName();
|
|
4668
4702
|
username = botName;
|
|
4669
4703
|
password = generateRandomPassword();
|
|
4670
|
-
const
|
|
4671
|
-
const botDir = join3(botsDir, botName);
|
|
4704
|
+
const botDir = join3(BOTS_DIR, botName);
|
|
4672
4705
|
mkdirSync2(botDir, { recursive: true });
|
|
4673
4706
|
writeFileSync2(join3(botDir, "bot.env"), [
|
|
4674
4707
|
`BOT_USERNAME=${username}`,
|
|
@@ -4683,10 +4716,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
4683
4716
|
botName = generateRandomName();
|
|
4684
4717
|
username = botName;
|
|
4685
4718
|
password = generateRandomPassword();
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
mkdirSync2(botDir, { recursive: true });
|
|
4689
|
-
writeFileSync2(join3(botDir, "bot.env"), [
|
|
4719
|
+
mkdirSync2(join3(BOTS_DIR, botName), { recursive: true });
|
|
4720
|
+
writeFileSync2(join3(BOTS_DIR, botName, "bot.env"), [
|
|
4690
4721
|
`BOT_USERNAME=${username}`,
|
|
4691
4722
|
`PASSWORD=${password}`,
|
|
4692
4723
|
`SERVER=${SERVER_URL2}`,
|
|
@@ -4753,37 +4784,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
4753
4784
|
activeActionDescriptions = descriptions;
|
|
4754
4785
|
const state = connection.sdk.getState();
|
|
4755
4786
|
const parts = [
|
|
4756
|
-
`Connected as "${username}"
|
|
4757
|
-
|
|
4758
|
-
"Your bot is in the game. A browser window should have opened showing the game view.",
|
|
4759
|
-
"",
|
|
4760
|
-
"You're in the tournament lobby (Barbarian Village) with:",
|
|
4761
|
-
" - EMPTY inventory and no equipment",
|
|
4762
|
-
" - Combat stats: 50 Attack, 50 Strength, 50 Defence, 99 Hitpoints",
|
|
4763
|
-
"",
|
|
4764
|
-
'NEXT STEP: Call join_game with mode "shorty" (or "longy" when available) to queue up.',
|
|
4765
|
-
"",
|
|
4766
|
-
"Game modes:",
|
|
4767
|
-
" - SHORTY: Quick battle royale at Draynor Manor. Loot, fight, last one standing.",
|
|
4768
|
-
" - LONGY: (Coming soon) Large survival map with skill progression.",
|
|
4769
|
-
"",
|
|
4770
|
-
"After joining a game, call wait_for_game_start to wait for the game to begin."
|
|
4787
|
+
`Connected as "${username}".`,
|
|
4788
|
+
`Bot data: ~/.killswitch/bots/${botName}/`
|
|
4771
4789
|
];
|
|
4772
|
-
parts.push("");
|
|
4773
|
-
parts.push("── IMPORTANT: Read the SDK Reference ──");
|
|
4774
|
-
parts.push('Before writing any execute_code, read the "Kill Switch SDK Reference" resource (killswitch://sdk-reference).');
|
|
4775
|
-
parts.push("It contains the complete API for bot, sdk, and actions. Do NOT guess method names — use only what is documented.");
|
|
4776
4790
|
if (activeActionDescriptions.length > 0) {
|
|
4777
4791
|
parts.push("");
|
|
4778
|
-
parts.push("
|
|
4779
|
-
parts.push("Use these in execute_code via the `actions` object:");
|
|
4792
|
+
parts.push("Loaded actions:");
|
|
4780
4793
|
parts.push(...activeActionDescriptions);
|
|
4781
4794
|
}
|
|
4782
4795
|
if (state) {
|
|
4783
4796
|
parts.push("");
|
|
4784
|
-
parts.push("── Current State ──");
|
|
4785
4797
|
parts.push(formatWorldState(state, connection.sdk.getStateAge()));
|
|
4786
4798
|
}
|
|
4799
|
+
parts.push("");
|
|
4800
|
+
parts.push('Next: call join_game with mode "shorty" to queue up.');
|
|
4787
4801
|
return { content: [{ type: "text", text: parts.join(`
|
|
4788
4802
|
`) }] };
|
|
4789
4803
|
} finally {
|
|
@@ -4907,7 +4921,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
4907
4921
|
if (isLongCode) {
|
|
4908
4922
|
parts.push("");
|
|
4909
4923
|
parts.push("── Tip ──");
|
|
4910
|
-
parts.push(`Long script detected. Consider writing to a .ts file
|
|
4924
|
+
parts.push(`Long script detected. Consider writing to a .ts file in ~/.killswitch/bots/${botName}/`);
|
|
4911
4925
|
}
|
|
4912
4926
|
const output = parts.length > 0 ? parts.join(`
|
|
4913
4927
|
`) : "(no output)";
|
|
@@ -4940,33 +4954,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
4940
4954
|
console.error(`[Kill Switch] Tournament started! (detected after ${waitTime}s)`);
|
|
4941
4955
|
const players = connection.sdk.getNearbyPlayers?.() || [];
|
|
4942
4956
|
const parts = [
|
|
4943
|
-
"
|
|
4957
|
+
"GAME STARTED! You are in the arena.",
|
|
4944
4958
|
"",
|
|
4945
|
-
`
|
|
4946
|
-
`Position: ${state.player.worldX}, ${state.player.worldZ}`,
|
|
4959
|
+
`Position: (${state.player.worldX}, ${state.player.worldZ})`,
|
|
4947
4960
|
`HP: ${state.player.hp}/${state.player.maxHp}`,
|
|
4961
|
+
`Nearby players: ${players.length > 0 ? players.map((p) => `${p.name} (dist ${p.distance})`).join(", ") : "scanning..."}`,
|
|
4948
4962
|
"",
|
|
4949
|
-
|
|
4950
|
-
"",
|
|
4951
|
-
"REMEMBER: You die = your agent dies FOREVER. No second chances.",
|
|
4952
|
-
"",
|
|
4953
|
-
"IMMEDIATELY use execute_code to start your strategy:",
|
|
4954
|
-
"1. Pick up weapons and food from the ground",
|
|
4955
|
-
"2. Equip best weapon found, then start fighting",
|
|
4956
|
-
"3. Eat when HP gets low — you have 99 HP but no food yet!",
|
|
4957
|
-
"",
|
|
4958
|
-
"QUICK START: await actions.lootAndEquip({ maxItems: 5 }); await actions.fightLoop({ eatAt: 25, duration: 30000 });",
|
|
4959
|
-
"",
|
|
4960
|
-
"API REMINDERS:",
|
|
4961
|
-
"- sdk.sendWalk(x, z) for quick movement (NOT bot.walkTo for combat)",
|
|
4962
|
-
"- sdk.getState().player.worldX / .worldZ / .hp",
|
|
4963
|
-
"- Blocking UI is auto-dismissed — do NOT call bot.dismissBlockingUI()"
|
|
4963
|
+
"Act now — use execute_code to loot and fight."
|
|
4964
4964
|
];
|
|
4965
|
-
if (activeActionDescriptions.length > 0) {
|
|
4966
|
-
parts.push("");
|
|
4967
|
-
parts.push("AVAILABLE ACTIONS:");
|
|
4968
|
-
parts.push(...activeActionDescriptions);
|
|
4969
|
-
}
|
|
4970
4965
|
return { content: [{ type: "text", text: parts.join(`
|
|
4971
4966
|
`) }] };
|
|
4972
4967
|
}
|
|
@@ -4994,22 +4989,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
4994
4989
|
return errorResponse(data.error);
|
|
4995
4990
|
}
|
|
4996
4991
|
const parts = [
|
|
4997
|
-
`Joined ${mode.toUpperCase()} queue
|
|
4998
|
-
"",
|
|
4999
|
-
`Queue position: ${data.queuePosition}`,
|
|
4992
|
+
`Joined ${mode.toUpperCase()} queue.`,
|
|
5000
4993
|
`Players in queue: ${data.queuePlayers?.join(", ") || "just you"}`,
|
|
5001
|
-
""
|
|
4994
|
+
"",
|
|
4995
|
+
"Next: call wait_for_game_start to wait for the match."
|
|
5002
4996
|
];
|
|
5003
|
-
if (mode === "shorty") {
|
|
5004
|
-
parts.push("SHORTY: Quick battle royale at Draynor Manor.");
|
|
5005
|
-
parts.push("Stats: 50 Attack, 50 Strength, 50 Defence, 99 Hitpoints");
|
|
5006
|
-
parts.push("You start with NOTHING — grab weapons and food from the ground!");
|
|
5007
|
-
} else {
|
|
5008
|
-
parts.push("LONGY: Large survival map (coming soon).");
|
|
5009
|
-
}
|
|
5010
|
-
parts.push("");
|
|
5011
|
-
parts.push("Now call wait_for_game_start to wait for the game to begin.");
|
|
5012
|
-
parts.push("Chat strategy with your human while you wait!");
|
|
5013
4997
|
return { content: [{ type: "text", text: parts.join(`
|
|
5014
4998
|
`) }] };
|
|
5015
4999
|
} catch (e) {
|