@blockrun/runcode 2.2.5 → 2.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.
@@ -34,6 +34,8 @@ export declare class ModelClient {
34
34
  private walletAddress;
35
35
  private cachedBaseWallet;
36
36
  private cachedSolanaWallet;
37
+ private walletCacheTime;
38
+ private static WALLET_CACHE_TTL;
37
39
  constructor(opts: LLMClientOptions);
38
40
  /**
39
41
  * Stream a completion from the BlockRun API.
package/dist/agent/llm.js CHANGED
@@ -13,6 +13,8 @@ export class ModelClient {
13
13
  walletAddress = '';
14
14
  cachedBaseWallet = null;
15
15
  cachedSolanaWallet = null;
16
+ walletCacheTime = 0;
17
+ static WALLET_CACHE_TTL = 30 * 60 * 1000; // 30 min TTL
16
18
  constructor(opts) {
17
19
  this.apiUrl = opts.apiUrl;
18
20
  this.chain = opts.chain;
@@ -219,8 +221,10 @@ export class ModelClient {
219
221
  }
220
222
  }
221
223
  async signBasePayment(response) {
222
- if (!this.cachedBaseWallet) {
224
+ // Refresh wallet cache after TTL to pick up balance/key changes
225
+ if (!this.cachedBaseWallet || (Date.now() - this.walletCacheTime > ModelClient.WALLET_CACHE_TTL)) {
223
226
  const w = getOrCreateWallet();
227
+ this.walletCacheTime = Date.now();
224
228
  this.cachedBaseWallet = { privateKey: w.privateKey, address: w.address };
225
229
  }
226
230
  const wallet = this.cachedBaseWallet;
@@ -240,8 +244,9 @@ export class ModelClient {
240
244
  return { 'PAYMENT-SIGNATURE': payload };
241
245
  }
242
246
  async signSolanaPayment(response) {
243
- if (!this.cachedSolanaWallet) {
247
+ if (!this.cachedSolanaWallet || (Date.now() - this.walletCacheTime > ModelClient.WALLET_CACHE_TTL)) {
244
248
  const w = await getOrCreateSolanaWallet();
249
+ this.walletCacheTime = Date.now();
245
250
  this.cachedSolanaWallet = { privateKey: w.privateKey, address: w.address };
246
251
  }
247
252
  const wallet = this.cachedSolanaWallet;
@@ -129,7 +129,10 @@ export function timeBasedCleanup(history, lastActivityTimestamp) {
129
129
  if (!lastActivityTimestamp) {
130
130
  return { history, cleaned: false };
131
131
  }
132
- const gapMinutes = (Date.now() - lastActivityTimestamp) / 60_000;
132
+ const gapMs = Date.now() - lastActivityTimestamp;
133
+ if (gapMs < 0)
134
+ return { history, cleaned: false }; // Clock skew protection
135
+ const gapMinutes = gapMs / 60_000;
133
136
  if (gapMinutes < IDLE_GAP_THRESHOLD_MINUTES) {
134
137
  return { history, cleaned: false };
135
138
  }
package/dist/config.js CHANGED
@@ -3,7 +3,7 @@ import os from 'node:os';
3
3
  import fs from 'node:fs';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- let _version = '1.1.0';
6
+ let _version = '2.0.0';
7
7
  try {
8
8
  const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8'));
9
9
  _version = pkg.version || _version;
package/dist/index.js CHANGED
@@ -109,7 +109,7 @@ if (firstArg === 'solana' || firstArg === 'base') {
109
109
  }
110
110
  await startCommand(startOpts);
111
111
  }
112
- else if (!firstArg || (firstArg.startsWith('-') && firstArg !== '-h' && firstArg !== '--help' && firstArg !== '-V' && firstArg !== '--version')) {
112
+ else if (!firstArg || (firstArg.startsWith('-') && !['-h', '--help', '-V', '--version'].includes(firstArg))) {
113
113
  // No subcommand or only flags — treat as 'start' with flags
114
114
  const startOpts = { version };
115
115
  for (let i = 0; i < args.length; i++) {
@@ -394,16 +394,27 @@ export function createProxy(options) {
394
394
  if (done) {
395
395
  // Record stats from streaming response
396
396
  if (isStreaming && fullResponse) {
397
- // Search full response for the last output_tokens value
398
- const allOutputMatches = [...fullResponse.matchAll(/"output_tokens"\s*:\s*(\d+)/g)];
399
- const lastOutputMatch = allOutputMatches[allOutputMatches.length - 1];
400
- const inputMatch = fullResponse.match(/"input_tokens"\s*:\s*(\d+)/);
401
- if (lastOutputMatch) {
402
- const outputTokens = parseInt(lastOutputMatch[1], 10);
397
+ // Extract token usage from SSE stream by parsing message_delta events
398
+ let outputTokens = 0;
399
+ let inputTokens = 0;
400
+ // Find all data: lines and parse JSON to extract usage
401
+ for (const line of fullResponse.split('\n')) {
402
+ if (!line.startsWith('data: '))
403
+ continue;
404
+ const json = line.slice(6).trim();
405
+ if (json === '[DONE]')
406
+ continue;
407
+ try {
408
+ const parsed = JSON.parse(json);
409
+ if (parsed.usage?.output_tokens)
410
+ outputTokens = parsed.usage.output_tokens;
411
+ if (parsed.usage?.input_tokens)
412
+ inputTokens = parsed.usage.input_tokens;
413
+ }
414
+ catch { /* skip malformed */ }
415
+ }
416
+ if (outputTokens > 0) {
403
417
  trackOutputTokens(finalModel, outputTokens);
404
- const inputTokens = inputMatch
405
- ? parseInt(inputMatch[1], 10)
406
- : 0;
407
418
  const latencyMs = Date.now() - requestStartTime;
408
419
  const cost = estimateCost(finalModel, inputTokens, outputTokens);
409
420
  recordUsage(finalModel, inputTokens, outputTokens, cost, latencyMs, usedFallback);
@@ -105,8 +105,12 @@ function runNativeGrep(opts, searchPath, mode, limit) {
105
105
  break;
106
106
  }
107
107
  if (opts.glob) {
108
- // Native grep doesn't support recursive globs like **/*.ts — strip leading **/
109
- const nativeGlob = opts.glob.replace(/^\*\*\//, '');
108
+ // Native grep --include doesn't support ** or path separators
109
+ // Extract file extension pattern for best compatibility
110
+ const nativeGlob = opts.glob
111
+ .replace(/^\*\*\//, '') // Strip leading **/
112
+ .replace(/^.*\//, '') // Strip path prefix (src/ etc.)
113
+ .replace(/\*\*/, '*'); // Convert ** to * for flat matching
110
114
  args.push(`--include=${nativeGlob}`);
111
115
  }
112
116
  args.push('--exclude-dir=node_modules', '--exclude-dir=.git', '--exclude-dir=dist');
@@ -97,6 +97,8 @@ function stripHtml(html) {
97
97
  .replace(/<footer[^>]*>[\s\S]*?<\/footer>/gi, '')
98
98
  .replace(/<aside[^>]*>[\s\S]*?<\/aside>/gi, '')
99
99
  .replace(/<noscript[^>]*>[\s\S]*?<\/noscript>/gi, '')
100
+ .replace(/<svg[^>]*>[\s\S]*?<\/svg>/gi, '')
101
+ .replace(/<form[^>]*>[\s\S]*?<\/form>/gi, '')
100
102
  // Convert block elements to newlines for readability
101
103
  .replace(/<\/?(p|div|h[1-6]|li|br|tr)[^>]*>/gi, '\n')
102
104
  // Strip remaining tags
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/runcode",
3
- "version": "2.2.5",
3
+ "version": "2.2.6",
4
4
  "description": "RunCode — AI coding agent powered by 41+ models. Pay per use with USDC.",
5
5
  "type": "module",
6
6
  "bin": {