@blockrun/cc 0.4.1 → 0.6.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/README.md +73 -6
- package/dist/commands/config.d.ts +9 -0
- package/dist/commands/config.js +92 -0
- package/dist/commands/start.js +11 -2
- package/dist/index.js +10 -3
- package/dist/proxy/server.d.ts +8 -0
- package/dist/proxy/server.js +42 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,8 +33,21 @@ Claude Code --> brcc (local proxy) --> BlockRun API --> Any model
|
|
|
33
33
|
|
|
34
34
|
## Quick Start
|
|
35
35
|
|
|
36
|
+
### One-line install (Linux/macOS)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
curl -fsSL https://raw.githubusercontent.com/BlockRunAI/brcc/main/install.sh | bash
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Installs Node.js (if missing) + Claude Code + brcc + creates wallet.
|
|
43
|
+
|
|
44
|
+
### Manual install
|
|
45
|
+
|
|
36
46
|
```bash
|
|
37
|
-
#
|
|
47
|
+
# Prerequisites: Node.js 20+ and Claude Code
|
|
48
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
49
|
+
|
|
50
|
+
# Install brcc (use sudo on Linux)
|
|
38
51
|
npm install -g @blockrun/cc
|
|
39
52
|
|
|
40
53
|
# Create a wallet (one time)
|
|
@@ -81,14 +94,21 @@ That's it. Claude Code opens with access to 40+ models, no rate limits.
|
|
|
81
94
|
|
|
82
95
|
Creates a wallet and shows the address for funding.
|
|
83
96
|
|
|
97
|
+
```bash
|
|
98
|
+
brcc setup # Default: Base chain
|
|
99
|
+
brcc setup base # Explicit Base (Coinbase L2)
|
|
100
|
+
brcc setup solana # Solana chain
|
|
101
|
+
```
|
|
102
|
+
|
|
84
103
|
```
|
|
85
|
-
$ brcc setup
|
|
104
|
+
$ brcc setup base
|
|
86
105
|
Wallet created!
|
|
87
106
|
Address: 0xCC8c44AD3dc2A58D841c3EB26131E49b22665EF8
|
|
88
107
|
Send USDC on Base to this address to fund your account.
|
|
108
|
+
Chain: base — saved to ~/.blockrun/
|
|
89
109
|
```
|
|
90
110
|
|
|
91
|
-
Your wallet is saved to `~/.blockrun/` and shared with all BlockRun tools.
|
|
111
|
+
Your wallet is saved to `~/.blockrun/` and shared with all BlockRun tools (Python SDK, TS SDK, ClawRouter).
|
|
92
112
|
|
|
93
113
|
### `brcc start`
|
|
94
114
|
|
|
@@ -107,9 +127,56 @@ Starting Claude Code...
|
|
|
107
127
|
|
|
108
128
|
Options:
|
|
109
129
|
```bash
|
|
110
|
-
brcc start
|
|
111
|
-
brcc start --
|
|
112
|
-
brcc start -
|
|
130
|
+
brcc start # Default model (Sonnet 4.6)
|
|
131
|
+
brcc start --model nvidia/gpt-oss-120b # Free model
|
|
132
|
+
brcc start --model openai/gpt-5.4 # GPT-5.4
|
|
133
|
+
brcc start --model deepseek/deepseek-chat # Budget option
|
|
134
|
+
brcc start --no-launch # Proxy only
|
|
135
|
+
brcc start -p 9000 # Custom port
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Inside Claude Code, use `/model` to switch between Sonnet, Opus, and Haiku — each maps to the BlockRun model you configured.
|
|
139
|
+
|
|
140
|
+
### `brcc models`
|
|
141
|
+
|
|
142
|
+
List all available models with pricing.
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
$ brcc models
|
|
146
|
+
Available Models
|
|
147
|
+
|
|
148
|
+
Free Models (no USDC needed)
|
|
149
|
+
──────────────────────────────────────────────────────────────────────
|
|
150
|
+
nvidia/gpt-oss-120b
|
|
151
|
+
nvidia/gpt-oss-20b
|
|
152
|
+
|
|
153
|
+
Paid Models
|
|
154
|
+
──────────────────────────────────────────────────────────────────────
|
|
155
|
+
Model Input Output
|
|
156
|
+
deepseek/deepseek-chat $0.28/M $0.42/M
|
|
157
|
+
anthropic/claude-haiku-4.5 $1.00/M $5.00/M
|
|
158
|
+
openai/gpt-5.4 $2.50/M $15.00/M
|
|
159
|
+
anthropic/claude-sonnet-4.6 $3.00/M $15.00/M
|
|
160
|
+
anthropic/claude-opus-4.6 $5.00/M $25.00/M
|
|
161
|
+
...
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### `brcc config`
|
|
165
|
+
|
|
166
|
+
Customize model mappings for Claude Code's `/model` picker.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Map Claude Code's "Haiku" to a cheap model
|
|
170
|
+
brcc config set haiku-model deepseek/deepseek-chat
|
|
171
|
+
|
|
172
|
+
# Map "Sonnet" to GPT-5.4
|
|
173
|
+
brcc config set sonnet-model openai/gpt-5.4
|
|
174
|
+
|
|
175
|
+
# Set default model
|
|
176
|
+
brcc config set default-model nvidia/gpt-oss-120b
|
|
177
|
+
|
|
178
|
+
# View all settings
|
|
179
|
+
brcc config list
|
|
113
180
|
```
|
|
114
181
|
|
|
115
182
|
### `brcc balance`
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface BrccConfig {
|
|
2
|
+
'default-model'?: string;
|
|
3
|
+
'sonnet-model'?: string;
|
|
4
|
+
'opus-model'?: string;
|
|
5
|
+
'haiku-model'?: string;
|
|
6
|
+
'smart-routing'?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function loadConfig(): BrccConfig;
|
|
9
|
+
export declare function configCommand(action: string, keyOrUndefined?: string, value?: string): void;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { BLOCKRUN_DIR } from '../config.js';
|
|
5
|
+
const CONFIG_FILE = path.join(BLOCKRUN_DIR, 'brcc-config.json');
|
|
6
|
+
const VALID_KEYS = [
|
|
7
|
+
'default-model',
|
|
8
|
+
'sonnet-model',
|
|
9
|
+
'opus-model',
|
|
10
|
+
'haiku-model',
|
|
11
|
+
'smart-routing',
|
|
12
|
+
];
|
|
13
|
+
export function loadConfig() {
|
|
14
|
+
try {
|
|
15
|
+
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
16
|
+
return JSON.parse(content);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function saveConfig(config) {
|
|
23
|
+
fs.mkdirSync(BLOCKRUN_DIR, { recursive: true });
|
|
24
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n', {
|
|
25
|
+
mode: 0o600,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function isValidKey(key) {
|
|
29
|
+
return VALID_KEYS.includes(key);
|
|
30
|
+
}
|
|
31
|
+
export function configCommand(action, keyOrUndefined, value) {
|
|
32
|
+
if (action === 'list') {
|
|
33
|
+
const config = loadConfig();
|
|
34
|
+
const entries = Object.entries(config);
|
|
35
|
+
if (entries.length === 0) {
|
|
36
|
+
console.log(chalk.dim('No config set. Defaults will be used.'));
|
|
37
|
+
console.log(chalk.dim(`\nConfig file: ${CONFIG_FILE}`));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
console.log(chalk.bold('brcc config\n'));
|
|
41
|
+
for (const [k, v] of entries) {
|
|
42
|
+
console.log(` ${chalk.cyan(k)} = ${chalk.green(v)}`);
|
|
43
|
+
}
|
|
44
|
+
console.log(chalk.dim(`\nConfig file: ${CONFIG_FILE}`));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (action === 'get') {
|
|
48
|
+
if (!keyOrUndefined) {
|
|
49
|
+
console.log(chalk.red('Usage: brcc config get <key>'));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const config = loadConfig();
|
|
53
|
+
const val = config[keyOrUndefined];
|
|
54
|
+
if (val !== undefined) {
|
|
55
|
+
console.log(val);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.log(chalk.dim('(not set)'));
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (action === 'set') {
|
|
63
|
+
if (!keyOrUndefined || value === undefined) {
|
|
64
|
+
console.log(chalk.red('Usage: brcc config set <key> <value>'));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
if (!isValidKey(keyOrUndefined)) {
|
|
68
|
+
console.log(chalk.red(`Unknown config key: ${keyOrUndefined}`));
|
|
69
|
+
console.log(`Valid keys: ${VALID_KEYS.map((k) => chalk.cyan(k)).join(', ')}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const config = loadConfig();
|
|
73
|
+
config[keyOrUndefined] = value;
|
|
74
|
+
saveConfig(config);
|
|
75
|
+
console.log(`${chalk.cyan(keyOrUndefined)} = ${chalk.green(value)}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (action === 'unset') {
|
|
79
|
+
if (!keyOrUndefined) {
|
|
80
|
+
console.log(chalk.red('Usage: brcc config unset <key>'));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
const config = loadConfig();
|
|
84
|
+
delete config[keyOrUndefined];
|
|
85
|
+
saveConfig(config);
|
|
86
|
+
console.log(chalk.dim(`Unset ${keyOrUndefined}`));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
console.log(chalk.red(`Unknown action: ${action}`));
|
|
90
|
+
console.log('Usage: brcc config <set|get|unset|list> [key] [value]');
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
package/dist/commands/start.js
CHANGED
|
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import { getOrCreateWallet, getOrCreateSolanaWallet } from '@blockrun/llm';
|
|
4
4
|
import { createProxy } from '../proxy/server.js';
|
|
5
5
|
import { loadChain, API_URLS, DEFAULT_PROXY_PORT } from '../config.js';
|
|
6
|
+
import { loadConfig } from './config.js';
|
|
6
7
|
export async function startCommand(options) {
|
|
7
8
|
const chain = loadChain();
|
|
8
9
|
const apiUrl = API_URLS[chain];
|
|
@@ -24,7 +25,7 @@ export async function startCommand(options) {
|
|
|
24
25
|
console.log(`Model: ${chalk.green(model)}`);
|
|
25
26
|
console.log(`Proxy: ${chalk.cyan(`http://localhost:${port}`)}`);
|
|
26
27
|
console.log(`Backend: ${chalk.dim(apiUrl)}\n`);
|
|
27
|
-
const server = createProxy({ port, apiUrl, chain: 'solana' });
|
|
28
|
+
const server = createProxy({ port, apiUrl, chain: 'solana', modelOverride: model });
|
|
28
29
|
launchServer(server, port, shouldLaunch, model);
|
|
29
30
|
}
|
|
30
31
|
else {
|
|
@@ -45,7 +46,7 @@ export async function startCommand(options) {
|
|
|
45
46
|
console.log(`Model: ${chalk.green(model)}`);
|
|
46
47
|
console.log(`Proxy: ${chalk.cyan(`http://localhost:${port}`)}`);
|
|
47
48
|
console.log(`Backend: ${chalk.dim(apiUrl)}\n`);
|
|
48
|
-
const server = createProxy({ port, apiUrl, chain: 'base' });
|
|
49
|
+
const server = createProxy({ port, apiUrl, chain: 'base', modelOverride: model });
|
|
49
50
|
launchServer(server, port, shouldLaunch, model);
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -57,6 +58,10 @@ function launchServer(server, port, shouldLaunch, model) {
|
|
|
57
58
|
const cleanEnv = { ...process.env };
|
|
58
59
|
delete cleanEnv.CLAUDE_ACCESS_TOKEN;
|
|
59
60
|
delete cleanEnv.CLAUDE_OAUTH_TOKEN;
|
|
61
|
+
const config = loadConfig();
|
|
62
|
+
const sonnetModel = config['sonnet-model'] || 'anthropic/claude-sonnet-4.6';
|
|
63
|
+
const opusModel = config['opus-model'] || 'anthropic/claude-opus-4.6';
|
|
64
|
+
const haikuModel = config['haiku-model'] || 'anthropic/claude-haiku-4.5';
|
|
60
65
|
const claudeArgs = [];
|
|
61
66
|
if (model)
|
|
62
67
|
claudeArgs.push('--model', model);
|
|
@@ -66,6 +71,10 @@ function launchServer(server, port, shouldLaunch, model) {
|
|
|
66
71
|
...cleanEnv,
|
|
67
72
|
ANTHROPIC_BASE_URL: `http://localhost:${port}/api`,
|
|
68
73
|
ANTHROPIC_API_KEY: 'sk-ant-api03-brcc-proxy-00000000000000000000000000000000000000000000-00000000000000',
|
|
74
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: sonnetModel,
|
|
75
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: opusModel,
|
|
76
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: haikuModel,
|
|
77
|
+
...(model ? { ANTHROPIC_MODEL: model } : {}),
|
|
69
78
|
},
|
|
70
79
|
});
|
|
71
80
|
claude.on('error', (err) => {
|
package/dist/index.js
CHANGED
|
@@ -4,11 +4,13 @@ import { setupCommand } from './commands/setup.js';
|
|
|
4
4
|
import { startCommand } from './commands/start.js';
|
|
5
5
|
import { balanceCommand } from './commands/balance.js';
|
|
6
6
|
import { modelsCommand } from './commands/models.js';
|
|
7
|
+
import { configCommand } from './commands/config.js';
|
|
7
8
|
const program = new Command();
|
|
8
9
|
program
|
|
9
10
|
.name('brcc')
|
|
10
|
-
.description('BlockRun Claude Code — run Claude Code with any model, pay with USDC'
|
|
11
|
-
|
|
11
|
+
.description('BlockRun Claude Code — run Claude Code with any model, pay with USDC.\n\n' +
|
|
12
|
+
'Use /model inside Claude Code to switch between models on the fly.')
|
|
13
|
+
.version('0.5.0');
|
|
12
14
|
program
|
|
13
15
|
.command('setup [chain]')
|
|
14
16
|
.description('Create a new wallet for payments (base or solana)')
|
|
@@ -17,7 +19,7 @@ program
|
|
|
17
19
|
.command('start')
|
|
18
20
|
.description('Start proxy and launch Claude Code')
|
|
19
21
|
.option('-p, --port <port>', 'Proxy port', '8402')
|
|
20
|
-
.option('-m, --model <model>', '
|
|
22
|
+
.option('-m, --model <model>', 'Default model (e.g. openai/gpt-5.4, anthropic/claude-sonnet-4.6)')
|
|
21
23
|
.option('--no-launch', 'Start proxy only, do not launch Claude Code')
|
|
22
24
|
.action(startCommand);
|
|
23
25
|
program
|
|
@@ -28,4 +30,9 @@ program
|
|
|
28
30
|
.command('balance')
|
|
29
31
|
.description('Check wallet USDC balance')
|
|
30
32
|
.action(balanceCommand);
|
|
33
|
+
program
|
|
34
|
+
.command('config <action> [key] [value]')
|
|
35
|
+
.description('Manage brcc config (set, get, unset, list)\n' +
|
|
36
|
+
'Keys: default-model, sonnet-model, opus-model, haiku-model, smart-routing')
|
|
37
|
+
.action(configCommand);
|
|
31
38
|
program.parse();
|
package/dist/proxy/server.d.ts
CHANGED
|
@@ -4,5 +4,13 @@ export interface ProxyOptions {
|
|
|
4
4
|
port: number;
|
|
5
5
|
apiUrl: string;
|
|
6
6
|
chain?: Chain;
|
|
7
|
+
modelOverride?: string;
|
|
7
8
|
}
|
|
8
9
|
export declare function createProxy(options: ProxyOptions): http.Server;
|
|
10
|
+
type RequestCategory = 'simple' | 'code' | 'default';
|
|
11
|
+
interface ClassifiedRequest {
|
|
12
|
+
category: RequestCategory;
|
|
13
|
+
suggestedModel?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function classifyRequest(body: string): ClassifiedRequest;
|
|
16
|
+
export {};
|
package/dist/proxy/server.js
CHANGED
|
@@ -29,6 +29,16 @@ export function createProxy(options) {
|
|
|
29
29
|
});
|
|
30
30
|
req.on('end', async () => {
|
|
31
31
|
try {
|
|
32
|
+
if (options.modelOverride && body) {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(body);
|
|
35
|
+
if (parsed.model) {
|
|
36
|
+
parsed.model = options.modelOverride;
|
|
37
|
+
body = JSON.stringify(parsed);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch { /* not JSON, pass through */ }
|
|
41
|
+
}
|
|
32
42
|
const headers = {
|
|
33
43
|
'Content-Type': 'application/json',
|
|
34
44
|
};
|
|
@@ -139,6 +149,38 @@ async function handleSolanaPayment(response, url, method, headers, body, private
|
|
|
139
149
|
body: body || undefined,
|
|
140
150
|
});
|
|
141
151
|
}
|
|
152
|
+
export function classifyRequest(body) {
|
|
153
|
+
try {
|
|
154
|
+
const parsed = JSON.parse(body);
|
|
155
|
+
const messages = parsed.messages;
|
|
156
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
157
|
+
return { category: 'default' };
|
|
158
|
+
}
|
|
159
|
+
const lastMessage = messages[messages.length - 1];
|
|
160
|
+
let content = '';
|
|
161
|
+
if (typeof lastMessage.content === 'string') {
|
|
162
|
+
content = lastMessage.content;
|
|
163
|
+
}
|
|
164
|
+
else if (Array.isArray(lastMessage.content)) {
|
|
165
|
+
content = lastMessage.content
|
|
166
|
+
.filter((b) => b.type === 'text' && b.text)
|
|
167
|
+
.map((b) => b.text)
|
|
168
|
+
.join('\n');
|
|
169
|
+
}
|
|
170
|
+
if (content.includes('```') || content.includes('function ') ||
|
|
171
|
+
content.includes('class ') || content.includes('import ') ||
|
|
172
|
+
content.includes('def ') || content.includes('const ')) {
|
|
173
|
+
return { category: 'code' };
|
|
174
|
+
}
|
|
175
|
+
if (content.length < 100) {
|
|
176
|
+
return { category: 'simple' };
|
|
177
|
+
}
|
|
178
|
+
return { category: 'default' };
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return { category: 'default' };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
142
184
|
// ======================================================================
|
|
143
185
|
// Shared helpers
|
|
144
186
|
// ======================================================================
|