@blockrun/cc 0.9.1 → 0.9.2
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/commands/balance.js +8 -2
- package/dist/commands/models.js +8 -1
- package/dist/commands/start.js +10 -5
- package/dist/index.js +11 -1
- package/dist/proxy/server.js +53 -7
- package/dist/router/index.js +7 -0
- package/package.json +1 -1
package/dist/commands/balance.js
CHANGED
|
@@ -27,8 +27,14 @@ export async function balanceCommand() {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
catch {
|
|
31
|
-
|
|
30
|
+
catch (err) {
|
|
31
|
+
const msg = err instanceof Error ? err.message : '';
|
|
32
|
+
if (msg.includes('ENOENT') || msg.includes('wallet') || msg.includes('key')) {
|
|
33
|
+
console.log(chalk.red('No wallet found. Run `brcc setup` first.'));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log(chalk.red(`Error checking balance: ${msg || 'unknown error'}`));
|
|
37
|
+
}
|
|
32
38
|
process.exit(1);
|
|
33
39
|
}
|
|
34
40
|
}
|
package/dist/commands/models.js
CHANGED
|
@@ -37,6 +37,13 @@ export async function modelsCommand() {
|
|
|
37
37
|
console.log(`\n${chalk.dim(`${models.length} models available. Use:`)} ${chalk.bold('brcc start --model <model-id>')}`);
|
|
38
38
|
}
|
|
39
39
|
catch (err) {
|
|
40
|
-
|
|
40
|
+
const msg = err instanceof Error ? err.message : 'unknown error';
|
|
41
|
+
if (msg.includes('fetch') || msg.includes('ECONNREFUSED') || msg.includes('ENOTFOUND')) {
|
|
42
|
+
console.log(chalk.red(`Cannot reach BlockRun API at ${apiUrl}`));
|
|
43
|
+
console.log(chalk.dim('Check your internet connection or try again later.'));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.log(chalk.red(`Error: ${msg}`));
|
|
47
|
+
}
|
|
41
48
|
}
|
|
42
49
|
}
|
package/dist/commands/start.js
CHANGED
|
@@ -8,6 +8,11 @@ export async function startCommand(options) {
|
|
|
8
8
|
const chain = loadChain();
|
|
9
9
|
const apiUrl = API_URLS[chain];
|
|
10
10
|
const fallbackEnabled = options.fallback !== false; // Default true
|
|
11
|
+
const port = parseInt(options.port || String(DEFAULT_PROXY_PORT));
|
|
12
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
13
|
+
console.log(chalk.red(`Invalid port: ${options.port}. Must be 1-65535.`));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
11
16
|
if (chain === 'solana') {
|
|
12
17
|
const wallet = await getOrCreateSolanaWallet();
|
|
13
18
|
if (wallet.isNew) {
|
|
@@ -16,7 +21,6 @@ export async function startCommand(options) {
|
|
|
16
21
|
console.log(`\nSend USDC on Solana to this address, then run ${chalk.bold('brcc start')} again.\n`);
|
|
17
22
|
return;
|
|
18
23
|
}
|
|
19
|
-
const port = parseInt(options.port || String(DEFAULT_PROXY_PORT));
|
|
20
24
|
const shouldLaunch = options.launch !== false;
|
|
21
25
|
const model = options.model;
|
|
22
26
|
console.log(chalk.bold('brcc — BlockRun Claude Code\n'));
|
|
@@ -35,7 +39,7 @@ export async function startCommand(options) {
|
|
|
35
39
|
debug: options.debug,
|
|
36
40
|
fallbackEnabled,
|
|
37
41
|
});
|
|
38
|
-
launchServer(server, port, shouldLaunch, model);
|
|
42
|
+
launchServer(server, port, shouldLaunch, model, options.debug);
|
|
39
43
|
}
|
|
40
44
|
else {
|
|
41
45
|
const wallet = getOrCreateWallet();
|
|
@@ -45,7 +49,6 @@ export async function startCommand(options) {
|
|
|
45
49
|
console.log(`\nSend USDC on Base to this address, then run ${chalk.bold('brcc start')} again.\n`);
|
|
46
50
|
return;
|
|
47
51
|
}
|
|
48
|
-
const port = parseInt(options.port || String(DEFAULT_PROXY_PORT));
|
|
49
52
|
const shouldLaunch = options.launch !== false;
|
|
50
53
|
const model = options.model;
|
|
51
54
|
console.log(chalk.bold('brcc — BlockRun Claude Code\n'));
|
|
@@ -64,13 +67,15 @@ export async function startCommand(options) {
|
|
|
64
67
|
debug: options.debug,
|
|
65
68
|
fallbackEnabled,
|
|
66
69
|
});
|
|
67
|
-
launchServer(server, port, shouldLaunch, model);
|
|
70
|
+
launchServer(server, port, shouldLaunch, model, options.debug);
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
|
-
function launchServer(server, port, shouldLaunch, model) {
|
|
73
|
+
function launchServer(server, port, shouldLaunch, model, debug) {
|
|
71
74
|
server.listen(port, () => {
|
|
72
75
|
console.log(chalk.green(`✓ Proxy running on port ${port}`));
|
|
73
76
|
console.log(chalk.dim(` Usage tracking: ~/.blockrun/brcc-stats.json`));
|
|
77
|
+
if (debug)
|
|
78
|
+
console.log(chalk.dim(` Debug log: ~/.blockrun/brcc-debug.log`));
|
|
74
79
|
console.log(chalk.dim(` Run 'brcc stats' to view statistics\n`));
|
|
75
80
|
if (shouldLaunch) {
|
|
76
81
|
console.log('Starting Claude Code...\n');
|
package/dist/index.js
CHANGED
|
@@ -6,12 +6,22 @@ import { balanceCommand } from './commands/balance.js';
|
|
|
6
6
|
import { modelsCommand } from './commands/models.js';
|
|
7
7
|
import { configCommand } from './commands/config.js';
|
|
8
8
|
import { statsCommand } from './commands/stats.js';
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
let version = '0.9.0';
|
|
14
|
+
try {
|
|
15
|
+
const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8'));
|
|
16
|
+
version = pkg.version || version;
|
|
17
|
+
}
|
|
18
|
+
catch { /* use default */ }
|
|
9
19
|
const program = new Command();
|
|
10
20
|
program
|
|
11
21
|
.name('brcc')
|
|
12
22
|
.description('BlockRun Claude Code — run Claude Code with any model, pay with USDC.\n\n' +
|
|
13
23
|
'Use /model inside Claude Code to switch between models on the fly.')
|
|
14
|
-
.version(
|
|
24
|
+
.version(version);
|
|
15
25
|
program
|
|
16
26
|
.command('setup [chain]')
|
|
17
27
|
.description('Create a new wallet for payments (base or solana)')
|
package/dist/proxy/server.js
CHANGED
|
@@ -75,11 +75,18 @@ const MODEL_PRICING = {
|
|
|
75
75
|
'google/gemini-2.5-pro': { input: 1.25, output: 10.0 },
|
|
76
76
|
'google/gemini-2.5-flash': { input: 0.3, output: 2.5 },
|
|
77
77
|
'deepseek/deepseek-chat': { input: 0.28, output: 0.42 },
|
|
78
|
+
'deepseek/deepseek-reasoner': { input: 0.55, output: 2.19 },
|
|
78
79
|
'xai/grok-3': { input: 3.0, output: 15.0 },
|
|
79
80
|
'xai/grok-4-fast': { input: 0.2, output: 0.5 },
|
|
81
|
+
'xai/grok-4-1-fast-reasoning': { input: 0.2, output: 0.5 },
|
|
80
82
|
'nvidia/gpt-oss-120b': { input: 0, output: 0 },
|
|
81
83
|
'zai/glm-5': { input: 1.0, output: 3.2 },
|
|
82
84
|
'moonshot/kimi-k2.5': { input: 0.6, output: 3.0 },
|
|
85
|
+
'openai/gpt-5.3-codex': { input: 2.5, output: 10.0 },
|
|
86
|
+
'openai/o3': { input: 2.0, output: 8.0 },
|
|
87
|
+
'openai/o4-mini': { input: 1.1, output: 4.4 },
|
|
88
|
+
'google/gemini-2.5-flash-lite': { input: 0.08, output: 0.3 },
|
|
89
|
+
'google/gemini-3.1-pro': { input: 1.25, output: 10.0 },
|
|
83
90
|
};
|
|
84
91
|
function estimateCost(model, inputTokens, outputTokens) {
|
|
85
92
|
const pricing = MODEL_PRICING[model] || { input: 2.0, output: 10.0 };
|
|
@@ -301,23 +308,63 @@ export function createProxy(options) {
|
|
|
301
308
|
response.headers.forEach((v, k) => {
|
|
302
309
|
responseHeaders[k] = v;
|
|
303
310
|
});
|
|
311
|
+
// Intercept error responses and ensure Anthropic-format errors
|
|
312
|
+
// so Claude Code doesn't fall back to showing a login page
|
|
313
|
+
if (response.status >= 400 && !responseHeaders['content-type']?.includes('text/event-stream')) {
|
|
314
|
+
let errorBody;
|
|
315
|
+
try {
|
|
316
|
+
const rawText = await response.text();
|
|
317
|
+
const parsed = JSON.parse(rawText);
|
|
318
|
+
// Already has Anthropic error shape? Pass through
|
|
319
|
+
if (parsed.type === 'error' && parsed.error) {
|
|
320
|
+
errorBody = rawText;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
// Wrap in Anthropic error format
|
|
324
|
+
const errorMsg = parsed.error?.message || parsed.message || rawText.slice(0, 500);
|
|
325
|
+
errorBody = JSON.stringify({
|
|
326
|
+
type: 'error',
|
|
327
|
+
error: {
|
|
328
|
+
type: response.status === 401 ? 'authentication_error'
|
|
329
|
+
: response.status === 402 ? 'invalid_request_error'
|
|
330
|
+
: response.status === 429 ? 'rate_limit_error'
|
|
331
|
+
: response.status === 400 ? 'invalid_request_error'
|
|
332
|
+
: 'api_error',
|
|
333
|
+
message: `[${finalModel}] ${errorMsg}`,
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
errorBody = JSON.stringify({
|
|
340
|
+
type: 'error',
|
|
341
|
+
error: { type: 'api_error', message: `Backend returned ${response.status}` },
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
res.writeHead(response.status, { 'Content-Type': 'application/json' });
|
|
345
|
+
res.end(errorBody);
|
|
346
|
+
log(`⚠️ ${response.status} from backend for ${finalModel}`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
304
349
|
res.writeHead(response.status, responseHeaders);
|
|
305
350
|
const isStreaming = responseHeaders['content-type']?.includes('text/event-stream');
|
|
306
351
|
if (response.body) {
|
|
307
352
|
const reader = response.body.getReader();
|
|
308
353
|
const decoder = new TextDecoder();
|
|
309
|
-
let lastChunkText = '';
|
|
310
354
|
let fullResponse = '';
|
|
355
|
+
const STREAM_CAP = 5_000_000; // 5MB cap on accumulated stream
|
|
311
356
|
const pump = async () => {
|
|
312
357
|
while (true) {
|
|
313
358
|
const { done, value } = await reader.read();
|
|
314
359
|
if (done) {
|
|
315
360
|
// Record stats from streaming response
|
|
316
|
-
if (isStreaming &&
|
|
317
|
-
|
|
361
|
+
if (isStreaming && fullResponse) {
|
|
362
|
+
// Search full response for the last output_tokens value
|
|
363
|
+
const allOutputMatches = [...fullResponse.matchAll(/"output_tokens"\s*:\s*(\d+)/g)];
|
|
364
|
+
const lastOutputMatch = allOutputMatches[allOutputMatches.length - 1];
|
|
318
365
|
const inputMatch = fullResponse.match(/"input_tokens"\s*:\s*(\d+)/);
|
|
319
|
-
if (
|
|
320
|
-
lastOutputTokens = parseInt(
|
|
366
|
+
if (lastOutputMatch) {
|
|
367
|
+
lastOutputTokens = parseInt(lastOutputMatch[1], 10);
|
|
321
368
|
const inputTokens = inputMatch
|
|
322
369
|
? parseInt(inputMatch[1], 10)
|
|
323
370
|
: 0;
|
|
@@ -330,9 +377,8 @@ export function createProxy(options) {
|
|
|
330
377
|
res.end();
|
|
331
378
|
break;
|
|
332
379
|
}
|
|
333
|
-
if (isStreaming) {
|
|
380
|
+
if (isStreaming && fullResponse.length < STREAM_CAP) {
|
|
334
381
|
const chunk = decoder.decode(value, { stream: true });
|
|
335
|
-
lastChunkText = chunk;
|
|
336
382
|
fullResponse += chunk;
|
|
337
383
|
}
|
|
338
384
|
res.write(value);
|
package/dist/router/index.js
CHANGED
|
@@ -221,10 +221,17 @@ export function routeRequest(prompt, profile = 'auto') {
|
|
|
221
221
|
'google/gemini-2.5-flash': 0.001,
|
|
222
222
|
'google/gemini-2.5-flash-lite': 0.0003,
|
|
223
223
|
'deepseek/deepseek-chat': 0.0004,
|
|
224
|
+
'deepseek/deepseek-reasoner': 0.003,
|
|
224
225
|
'moonshot/kimi-k2.5': 0.002,
|
|
226
|
+
'google/gemini-2.5-pro': 0.006,
|
|
225
227
|
'google/gemini-3.1-pro': 0.007,
|
|
228
|
+
'anthropic/claude-haiku-4.5': 0.003,
|
|
226
229
|
'anthropic/claude-sonnet-4.6': 0.009,
|
|
227
230
|
'anthropic/claude-opus-4.6': 0.015,
|
|
231
|
+
'openai/gpt-5.3-codex': 0.008,
|
|
232
|
+
'openai/gpt-5.4': 0.009,
|
|
233
|
+
'openai/o3': 0.012,
|
|
234
|
+
'openai/o4-mini': 0.006,
|
|
228
235
|
'xai/grok-4-1-fast-reasoning': 0.0004,
|
|
229
236
|
};
|
|
230
237
|
const modelCost = modelCosts[model] ?? 0.005;
|