Files
max bittkerandClaude Opus 5.5 df3d8888ae feat(sdk,mcp): cancellable execute_code, bot.mineRock, bounded walkTo
execute_code
- Cancellation now reaches inside the SDK via an AsyncLocalStorage scope:
  timeout, MCP cancel, or a newer call rejects pending waits and blocks
  further action packets, including those inside walkTo().
- One execution per bot: a new call supersedes the running one and starts
  once it has unwound; disconnect_bot does the same.
- Cancelled user code parks on bot/sdk calls instead of throwing, so a
  catch-all loop can't spin and starve the server.
- Console capture is routed per execution, so concurrent bots no longer
  clobber each other's output; leftover logs go to stderr.
- Stale world state warns and still runs, so a stale client can be diagnosed.
- Credentials are redacted from logs, results and launch messages.

SDK
- bot.mineRock() waits for ore plus Mining XP; interactLoc docs note that
  success can mean the action only started.
- walkTo() takes options.timeout (default 120s), refuses stale state, and
  reports the real last position.
- runScript uses the same cancellation scope for timeouts and signals.

Docs trimmed of duplicated or stale execute_code text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:30:19 -04:00

2.6 KiB

Mining

Successful patterns for mining automation.

Finding Rocks

Rocks are locations (not NPCs). Filter for rocks with a "Mine" option:

The client names live rocks Rocks copper ore, Rocks tin ore, and so on. Use /copper ore/i to find a rock; /^copper ore$/i matches the inventory item only. Depleted rocks can appear as plain Rocks; select a live ore-specific rock again.

const state = sdk.getState();
const rock = state?.nearbyLocs
  .filter((loc) => /rocks?/i.test(loc.name))
  .filter((loc) => loc.optionsWithIndex.some((o) => /^mine$/i.test(o.text)))
  .sort((a, b) => a.distance - b.distance)[0];

Mining Action

if (!rock) throw new Error("No mineable rock nearby");
const result = await bot.mineRock(rock);
if (!result.success) console.warn(result.reason, result.message);

For lower-level control, sendInteractLoc() only confirms client dispatch. Take an XP/inventory baseline and wait for the intended change rather than sleeping for an arbitrary duration.

Detecting Mining Activity

Animation ID 625 indicates active mining:

const animation = sdk.getState()?.player?.animId ?? -1;
const isMining = animation === 625;
const isIdle = animation === -1;

Note: Al Kharid mine is full of Lvl 14 scorpions. Combat 27+ with defensive style is enough to survive while mining. The scorpion fights actually train Defence passively.

Reliable Locations

Location Coordinates Notes
SE Varrock mine (3285, 3365) Copper, tin, iron
Al Kharid mine (3295, 3287) Iron, coal, gold, silver, mithril, tin. Scorpions!
Lumbridge Swamp mine -

Getting to Al Kharid mine from Lumbridge: Pay 10gp toll at gate (3268, 3227), walk NE. Dialog sequence: continue → continue → "Yes, ok." (index 3) → continue.

Counting Ore

function countOre() {
  const state = sdk.getState();
  if (!state) return 0;
  return state.inventory
    .filter((i) => /ore$/i.test(i.name))
    .reduce((sum, i) => sum + i.count, 0);
}

Drop When Full

const state = sdk.getState();
if (!state) throw new Error("No world state");
if (state.inventory.length >= 28) {
  const ores = state.inventory.filter((i) => /ore$/i.test(i.name));
  for (const ore of ores) {
    await sdk.sendDropItem(ore.slot);
    await new Promise((r) => setTimeout(r, 100));
  }
}