@blockrun/cc 0.2.0 → 0.4.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 +2 -2
- package/dist/commands/balance.js +24 -8
- package/dist/commands/models.d.ts +1 -0
- package/dist/commands/models.js +42 -0
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +36 -14
- package/dist/commands/start.d.ts +1 -0
- package/dist/commands/start.js +57 -19
- package/dist/config.d.ts +6 -1
- package/dist/config.js +29 -1
- package/dist/index.js +9 -3
- package/dist/proxy/server.d.ts +2 -0
- package/dist/proxy/server.js +71 -18
- package/dist/wallet/manager.d.ts +4 -0
- package/dist/wallet/manager.js +11 -3
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Hitting usage limits? Account disabled? Can't verify your phone? brcc fixes all of that.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install -g
|
|
8
|
+
npm install -g @blockrun/cc
|
|
9
9
|
brcc start
|
|
10
10
|
```
|
|
11
11
|
|
|
@@ -35,7 +35,7 @@ Claude Code --> brcc (local proxy) --> BlockRun API --> Any model
|
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
# Install
|
|
38
|
-
npm install -g
|
|
38
|
+
npm install -g @blockrun/cc
|
|
39
39
|
|
|
40
40
|
# Create a wallet (one time)
|
|
41
41
|
brcc setup
|
package/dist/commands/balance.js
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { setupAgentWallet } from '@blockrun/llm';
|
|
2
|
+
import { setupAgentWallet, setupAgentSolanaWallet } from '@blockrun/llm';
|
|
3
|
+
import { loadChain } from '../config.js';
|
|
3
4
|
export async function balanceCommand() {
|
|
5
|
+
const chain = loadChain();
|
|
4
6
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.log(
|
|
7
|
+
if (chain === 'solana') {
|
|
8
|
+
const client = await setupAgentSolanaWallet({ silent: true });
|
|
9
|
+
const address = await client.getWalletAddress();
|
|
10
|
+
const balance = await client.getBalance();
|
|
11
|
+
console.log(`Chain: ${chalk.magenta('solana')}`);
|
|
12
|
+
console.log(`Wallet: ${chalk.cyan(address)}`);
|
|
13
|
+
console.log(`USDC Balance: ${chalk.green(`$${balance.toFixed(2)}`)}`);
|
|
14
|
+
if (balance === 0) {
|
|
15
|
+
console.log(chalk.dim(`\nSend USDC on Solana to ${address} to get started.`));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const client = setupAgentWallet({ silent: true });
|
|
20
|
+
const address = client.getWalletAddress();
|
|
21
|
+
const balance = await client.getBalance();
|
|
22
|
+
console.log(`Chain: ${chalk.magenta('base')}`);
|
|
23
|
+
console.log(`Wallet: ${chalk.cyan(address)}`);
|
|
24
|
+
console.log(`USDC Balance: ${chalk.green(`$${balance.toFixed(2)}`)}`);
|
|
25
|
+
if (balance === 0) {
|
|
26
|
+
console.log(chalk.dim(`\nSend USDC on Base to ${address} to get started.`));
|
|
27
|
+
}
|
|
12
28
|
}
|
|
13
29
|
}
|
|
14
30
|
catch {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function modelsCommand(): Promise<void>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadChain, API_URLS } from '../config.js';
|
|
3
|
+
export async function modelsCommand() {
|
|
4
|
+
const chain = loadChain();
|
|
5
|
+
const apiUrl = API_URLS[chain];
|
|
6
|
+
console.log(chalk.bold('Available Models\n'));
|
|
7
|
+
console.log(`Chain: ${chalk.magenta(chain)} — ${chalk.dim(apiUrl)}\n`);
|
|
8
|
+
try {
|
|
9
|
+
const response = await fetch(`${apiUrl}/v1/models`);
|
|
10
|
+
if (!response.ok) {
|
|
11
|
+
console.log(chalk.red(`Failed to fetch models: ${response.status}`));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const data = (await response.json());
|
|
15
|
+
const models = data.data
|
|
16
|
+
.sort((a, b) => (a.pricing?.input ?? 0) - (b.pricing?.input ?? 0));
|
|
17
|
+
const free = models.filter((m) => m.billing_mode === 'free');
|
|
18
|
+
const paid = models.filter((m) => m.billing_mode !== 'free');
|
|
19
|
+
if (free.length > 0) {
|
|
20
|
+
console.log(chalk.green.bold('Free Models (no USDC needed)'));
|
|
21
|
+
console.log(chalk.dim('─'.repeat(70)));
|
|
22
|
+
for (const m of free) {
|
|
23
|
+
console.log(` ${chalk.cyan(m.id)}`);
|
|
24
|
+
}
|
|
25
|
+
console.log('');
|
|
26
|
+
}
|
|
27
|
+
console.log(chalk.yellow.bold('Paid Models'));
|
|
28
|
+
console.log(chalk.dim('─'.repeat(70)));
|
|
29
|
+
console.log(chalk.dim(` ${'Model'.padEnd(35)} ${'Input'.padEnd(12)} ${'Output'.padEnd(12)} Context`));
|
|
30
|
+
console.log(chalk.dim('─'.repeat(70)));
|
|
31
|
+
for (const m of paid) {
|
|
32
|
+
const input = `$${(m.pricing?.input ?? 0).toFixed(2)}/M`;
|
|
33
|
+
const output = `$${(m.pricing?.output ?? 0).toFixed(2)}/M`;
|
|
34
|
+
const ctx = '';
|
|
35
|
+
console.log(` ${chalk.cyan(m.id.padEnd(35))} ${input.padEnd(12)} ${output.padEnd(12)} ${ctx}`);
|
|
36
|
+
}
|
|
37
|
+
console.log(`\n${chalk.dim(`${models.length} models available. Use:`)} ${chalk.bold('brcc start --model <model-id>')}`);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.log(chalk.red(`Error: ${err instanceof Error ? err.message : 'Failed to fetch models'}`));
|
|
41
|
+
}
|
|
42
|
+
}
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function setupCommand(): Promise<void>;
|
|
1
|
+
export declare function setupCommand(chainArg?: string): Promise<void>;
|
package/dist/commands/setup.js
CHANGED
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { getOrCreateWallet, scanWallets } from '@blockrun/llm';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
import { getOrCreateWallet, scanWallets, getOrCreateSolanaWallet, scanSolanaWallets, } from '@blockrun/llm';
|
|
3
|
+
import { saveChain } from '../config.js';
|
|
4
|
+
export async function setupCommand(chainArg) {
|
|
5
|
+
const chain = chainArg === 'solana' ? 'solana' : 'base';
|
|
6
|
+
if (chain === 'solana') {
|
|
7
|
+
const wallets = scanSolanaWallets();
|
|
8
|
+
if (wallets.length > 0) {
|
|
9
|
+
console.log(chalk.yellow('Solana wallet already exists.'));
|
|
10
|
+
console.log(`Address: ${chalk.cyan(wallets[0].publicKey)}`);
|
|
11
|
+
saveChain('solana');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
console.log('Creating new Solana wallet...\n');
|
|
15
|
+
const { address, isNew } = await getOrCreateSolanaWallet();
|
|
16
|
+
if (isNew) {
|
|
17
|
+
console.log(chalk.green('Solana wallet created!\n'));
|
|
18
|
+
}
|
|
19
|
+
console.log(`Address: ${chalk.cyan(address)}`);
|
|
20
|
+
console.log(`\nSend USDC on Solana to this address to fund your account.`);
|
|
9
21
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
else {
|
|
23
|
+
const wallets = scanWallets();
|
|
24
|
+
if (wallets.length > 0) {
|
|
25
|
+
console.log(chalk.yellow('Wallet already exists.'));
|
|
26
|
+
console.log(`Address: ${chalk.cyan(wallets[0].address)}`);
|
|
27
|
+
saveChain('base');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log('Creating new wallet...\n');
|
|
31
|
+
const { address, isNew } = getOrCreateWallet();
|
|
32
|
+
if (isNew) {
|
|
33
|
+
console.log(chalk.green('Wallet created!\n'));
|
|
34
|
+
}
|
|
35
|
+
console.log(`Address: ${chalk.cyan(address)}`);
|
|
36
|
+
console.log(`\nSend USDC on Base to this address to fund your account.`);
|
|
14
37
|
}
|
|
15
|
-
|
|
16
|
-
console.log(`\nSend USDC on Base to this address to fund your account.`);
|
|
38
|
+
saveChain(chain);
|
|
17
39
|
console.log(`Then run ${chalk.bold('brcc start')} to launch Claude Code.\n`);
|
|
18
|
-
console.log(chalk.dim(
|
|
40
|
+
console.log(chalk.dim(`Chain: ${chain} — saved to ~/.blockrun/`));
|
|
19
41
|
}
|
package/dist/commands/start.d.ts
CHANGED
package/dist/commands/start.js
CHANGED
|
@@ -1,33 +1,71 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { getOrCreateWallet } from '@blockrun/llm';
|
|
3
|
+
import { getOrCreateWallet, getOrCreateSolanaWallet } from '@blockrun/llm';
|
|
4
4
|
import { createProxy } from '../proxy/server.js';
|
|
5
|
-
import {
|
|
5
|
+
import { loadChain, API_URLS, DEFAULT_PROXY_PORT } from '../config.js';
|
|
6
6
|
export async function startCommand(options) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
const chain = loadChain();
|
|
8
|
+
const apiUrl = API_URLS[chain];
|
|
9
|
+
if (chain === 'solana') {
|
|
10
|
+
const wallet = await getOrCreateSolanaWallet();
|
|
11
|
+
if (wallet.isNew) {
|
|
12
|
+
console.log(chalk.yellow('No Solana wallet found — created a new one.'));
|
|
13
|
+
console.log(`Address: ${chalk.cyan(wallet.address)}`);
|
|
14
|
+
console.log(`\nSend USDC on Solana to this address, then run ${chalk.bold('brcc start')} again.\n`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const port = parseInt(options.port || String(DEFAULT_PROXY_PORT));
|
|
18
|
+
const shouldLaunch = options.launch !== false;
|
|
19
|
+
const model = options.model;
|
|
20
|
+
console.log(chalk.bold('brcc — BlockRun Claude Code\n'));
|
|
21
|
+
console.log(`Chain: ${chalk.magenta('solana')}`);
|
|
22
|
+
console.log(`Wallet: ${chalk.cyan(wallet.address)}`);
|
|
23
|
+
if (model)
|
|
24
|
+
console.log(`Model: ${chalk.green(model)}`);
|
|
25
|
+
console.log(`Proxy: ${chalk.cyan(`http://localhost:${port}`)}`);
|
|
26
|
+
console.log(`Backend: ${chalk.dim(apiUrl)}\n`);
|
|
27
|
+
const server = createProxy({ port, apiUrl, chain: 'solana' });
|
|
28
|
+
launchServer(server, port, shouldLaunch, model);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const wallet = getOrCreateWallet();
|
|
32
|
+
if (wallet.isNew) {
|
|
33
|
+
console.log(chalk.yellow('No wallet found — created a new one.'));
|
|
34
|
+
console.log(`Address: ${chalk.cyan(wallet.address)}`);
|
|
35
|
+
console.log(`\nSend USDC on Base to this address, then run ${chalk.bold('brcc start')} again.\n`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const port = parseInt(options.port || String(DEFAULT_PROXY_PORT));
|
|
39
|
+
const shouldLaunch = options.launch !== false;
|
|
40
|
+
const model = options.model;
|
|
41
|
+
console.log(chalk.bold('brcc — BlockRun Claude Code\n'));
|
|
42
|
+
console.log(`Chain: ${chalk.magenta('base')}`);
|
|
43
|
+
console.log(`Wallet: ${chalk.cyan(wallet.address)}`);
|
|
44
|
+
if (model)
|
|
45
|
+
console.log(`Model: ${chalk.green(model)}`);
|
|
46
|
+
console.log(`Proxy: ${chalk.cyan(`http://localhost:${port}`)}`);
|
|
47
|
+
console.log(`Backend: ${chalk.dim(apiUrl)}\n`);
|
|
48
|
+
const server = createProxy({ port, apiUrl, chain: 'base' });
|
|
49
|
+
launchServer(server, port, shouldLaunch, model);
|
|
13
50
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
console.log(chalk.bold('brcc — BlockRun Claude Code\n'));
|
|
17
|
-
console.log(`Wallet: ${chalk.cyan(wallet.address)}`);
|
|
18
|
-
console.log(`Proxy: ${chalk.cyan(`http://localhost:${port}`)}`);
|
|
19
|
-
console.log(`Backend: ${chalk.dim(DEFAULT_API_URL)}\n`);
|
|
20
|
-
const server = createProxy({ port, apiUrl: DEFAULT_API_URL });
|
|
51
|
+
}
|
|
52
|
+
function launchServer(server, port, shouldLaunch, model) {
|
|
21
53
|
server.listen(port, () => {
|
|
22
54
|
console.log(chalk.green(`Proxy running on port ${port}\n`));
|
|
23
55
|
if (shouldLaunch) {
|
|
24
56
|
console.log('Starting Claude Code...\n');
|
|
25
|
-
const
|
|
57
|
+
const cleanEnv = { ...process.env };
|
|
58
|
+
delete cleanEnv.CLAUDE_ACCESS_TOKEN;
|
|
59
|
+
delete cleanEnv.CLAUDE_OAUTH_TOKEN;
|
|
60
|
+
const claudeArgs = ['--api-key-auth'];
|
|
61
|
+
if (model)
|
|
62
|
+
claudeArgs.push('--model', model);
|
|
63
|
+
const claude = spawn('claude', claudeArgs, {
|
|
26
64
|
stdio: 'inherit',
|
|
27
65
|
env: {
|
|
28
|
-
...
|
|
66
|
+
...cleanEnv,
|
|
29
67
|
ANTHROPIC_BASE_URL: `http://localhost:${port}/api`,
|
|
30
|
-
ANTHROPIC_API_KEY: 'brcc',
|
|
68
|
+
ANTHROPIC_API_KEY: 'sk-ant-api03-brcc-proxy-00000000000000000000000000000000000000000000-00000000000000',
|
|
31
69
|
},
|
|
32
70
|
});
|
|
33
71
|
claude.on('error', (err) => {
|
|
@@ -49,7 +87,7 @@ export async function startCommand(options) {
|
|
|
49
87
|
else {
|
|
50
88
|
console.log('Proxy-only mode. Set this in your shell:\n');
|
|
51
89
|
console.log(chalk.bold(` export ANTHROPIC_BASE_URL=http://localhost:${port}/api`));
|
|
52
|
-
console.log(chalk.bold(` export ANTHROPIC_API_KEY=brcc`));
|
|
90
|
+
console.log(chalk.bold(` export ANTHROPIC_API_KEY=sk-ant-api03-brcc-proxy-00000000000000000000000000000000000000000000-00000000000000`));
|
|
53
91
|
console.log(`\nThen run ${chalk.bold('claude')} in another terminal.`);
|
|
54
92
|
}
|
|
55
93
|
});
|
package/dist/config.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type Chain = 'base' | 'solana';
|
|
2
|
+
export declare const BLOCKRUN_DIR: string;
|
|
3
|
+
export declare const CHAIN_FILE: string;
|
|
4
|
+
export declare const API_URLS: Record<Chain, string>;
|
|
2
5
|
export declare const DEFAULT_PROXY_PORT = 8402;
|
|
6
|
+
export declare function saveChain(chain: Chain): void;
|
|
7
|
+
export declare function loadChain(): Chain;
|
package/dist/config.js
CHANGED
|
@@ -1,2 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
export const BLOCKRUN_DIR = path.join(os.homedir(), '.blockrun');
|
|
5
|
+
export const CHAIN_FILE = path.join(BLOCKRUN_DIR, 'payment-chain');
|
|
6
|
+
export const API_URLS = {
|
|
7
|
+
base: 'https://blockrun.ai/api',
|
|
8
|
+
solana: 'https://sol.blockrun.ai/api',
|
|
9
|
+
};
|
|
2
10
|
export const DEFAULT_PROXY_PORT = 8402;
|
|
11
|
+
export function saveChain(chain) {
|
|
12
|
+
fs.mkdirSync(BLOCKRUN_DIR, { recursive: true });
|
|
13
|
+
fs.writeFileSync(CHAIN_FILE, chain + '\n', { mode: 0o600 });
|
|
14
|
+
}
|
|
15
|
+
export function loadChain() {
|
|
16
|
+
const envChain = process.env.BRCC_CHAIN;
|
|
17
|
+
if (envChain === 'solana')
|
|
18
|
+
return 'solana';
|
|
19
|
+
if (envChain === 'base')
|
|
20
|
+
return 'base';
|
|
21
|
+
try {
|
|
22
|
+
const content = fs.readFileSync(CHAIN_FILE, 'utf-8').trim();
|
|
23
|
+
if (content === 'solana')
|
|
24
|
+
return 'solana';
|
|
25
|
+
return 'base';
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return 'base';
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -3,21 +3,27 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { setupCommand } from './commands/setup.js';
|
|
4
4
|
import { startCommand } from './commands/start.js';
|
|
5
5
|
import { balanceCommand } from './commands/balance.js';
|
|
6
|
+
import { modelsCommand } from './commands/models.js';
|
|
6
7
|
const program = new Command();
|
|
7
8
|
program
|
|
8
9
|
.name('brcc')
|
|
9
10
|
.description('BlockRun Claude Code — run Claude Code with any model, pay with USDC')
|
|
10
11
|
.version('0.1.0');
|
|
11
12
|
program
|
|
12
|
-
.command('setup')
|
|
13
|
-
.description('Create a new wallet for payments')
|
|
14
|
-
.action(setupCommand);
|
|
13
|
+
.command('setup [chain]')
|
|
14
|
+
.description('Create a new wallet for payments (base or solana)')
|
|
15
|
+
.action((chain) => setupCommand(chain));
|
|
15
16
|
program
|
|
16
17
|
.command('start')
|
|
17
18
|
.description('Start proxy and launch Claude Code')
|
|
18
19
|
.option('-p, --port <port>', 'Proxy port', '8402')
|
|
20
|
+
.option('-m, --model <model>', 'Model to use (e.g. openai/gpt-5.4, nvidia/gpt-oss-120b)')
|
|
19
21
|
.option('--no-launch', 'Start proxy only, do not launch Claude Code')
|
|
20
22
|
.action(startCommand);
|
|
23
|
+
program
|
|
24
|
+
.command('models')
|
|
25
|
+
.description('List available models and pricing')
|
|
26
|
+
.action(modelsCommand);
|
|
21
27
|
program
|
|
22
28
|
.command('balance')
|
|
23
29
|
.description('Check wallet USDC balance')
|
package/dist/proxy/server.d.ts
CHANGED
package/dist/proxy/server.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
|
-
import { getOrCreateWallet, createPaymentPayload, parsePaymentRequired, extractPaymentDetails, } from '@blockrun/llm';
|
|
2
|
+
import { getOrCreateWallet, getOrCreateSolanaWallet, createPaymentPayload, createSolanaPaymentPayload, parsePaymentRequired, extractPaymentDetails, solanaKeyToBytes, SOLANA_NETWORK, } from '@blockrun/llm';
|
|
3
3
|
export function createProxy(options) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const chain = options.chain || 'base';
|
|
5
|
+
let baseWallet = null;
|
|
6
|
+
let solanaWallet = null;
|
|
7
|
+
if (chain === 'base') {
|
|
8
|
+
const w = getOrCreateWallet();
|
|
9
|
+
baseWallet = { privateKey: w.privateKey, address: w.address };
|
|
10
|
+
}
|
|
11
|
+
const initSolana = async () => {
|
|
12
|
+
if (chain === 'solana' && !solanaWallet) {
|
|
13
|
+
const w = await getOrCreateSolanaWallet();
|
|
14
|
+
solanaWallet = { privateKey: w.privateKey, address: w.address };
|
|
15
|
+
}
|
|
16
|
+
};
|
|
7
17
|
const server = http.createServer(async (req, res) => {
|
|
8
18
|
if (req.method === 'OPTIONS') {
|
|
9
19
|
res.writeHead(200);
|
|
10
20
|
res.end();
|
|
11
21
|
return;
|
|
12
22
|
}
|
|
23
|
+
await initSolana();
|
|
13
24
|
const path = req.url?.replace(/^\/api/, '') || '';
|
|
14
25
|
const targetUrl = `${options.apiUrl}${path}`;
|
|
15
26
|
let body = '';
|
|
@@ -34,7 +45,12 @@ export function createProxy(options) {
|
|
|
34
45
|
body: body || undefined,
|
|
35
46
|
});
|
|
36
47
|
if (response.status === 402) {
|
|
37
|
-
|
|
48
|
+
if (chain === 'solana' && solanaWallet) {
|
|
49
|
+
response = await handleSolanaPayment(response, targetUrl, req.method || 'POST', headers, body, solanaWallet.privateKey, solanaWallet.address);
|
|
50
|
+
}
|
|
51
|
+
else if (baseWallet) {
|
|
52
|
+
response = await handleBasePayment(response, targetUrl, req.method || 'POST', headers, body, baseWallet.privateKey, baseWallet.address);
|
|
53
|
+
}
|
|
38
54
|
}
|
|
39
55
|
const responseHeaders = {};
|
|
40
56
|
response.headers.forEach((v, k) => {
|
|
@@ -71,19 +87,11 @@ export function createProxy(options) {
|
|
|
71
87
|
});
|
|
72
88
|
return server;
|
|
73
89
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (respBody.x402 || respBody.accepts) {
|
|
80
|
-
paymentHeader = btoa(JSON.stringify(respBody));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
// ignore parse errors
|
|
85
|
-
}
|
|
86
|
-
}
|
|
90
|
+
// ======================================================================
|
|
91
|
+
// Base (EIP-712) payment handler
|
|
92
|
+
// ======================================================================
|
|
93
|
+
async function handleBasePayment(response, url, method, headers, body, privateKey, fromAddress) {
|
|
94
|
+
const paymentHeader = await extractPaymentHeader(response);
|
|
87
95
|
if (!paymentHeader) {
|
|
88
96
|
throw new Error('402 response but no payment requirements found');
|
|
89
97
|
}
|
|
@@ -104,3 +112,48 @@ async function handlePayment(response, url, method, headers, body, privateKey, f
|
|
|
104
112
|
body: body || undefined,
|
|
105
113
|
});
|
|
106
114
|
}
|
|
115
|
+
// ======================================================================
|
|
116
|
+
// Solana payment handler
|
|
117
|
+
// ======================================================================
|
|
118
|
+
async function handleSolanaPayment(response, url, method, headers, body, privateKey, fromAddress) {
|
|
119
|
+
const paymentHeader = await extractPaymentHeader(response);
|
|
120
|
+
if (!paymentHeader) {
|
|
121
|
+
throw new Error('402 response but no payment requirements found');
|
|
122
|
+
}
|
|
123
|
+
const paymentRequired = parsePaymentRequired(paymentHeader);
|
|
124
|
+
const details = extractPaymentDetails(paymentRequired, SOLANA_NETWORK);
|
|
125
|
+
const secretKey = await solanaKeyToBytes(privateKey);
|
|
126
|
+
const feePayer = details.extra?.feePayer || details.recipient;
|
|
127
|
+
const paymentPayload = await createSolanaPaymentPayload(secretKey, fromAddress, details.recipient, details.amount, feePayer, {
|
|
128
|
+
resourceUrl: details.resource?.url || url,
|
|
129
|
+
resourceDescription: details.resource?.description || 'BlockRun AI API call',
|
|
130
|
+
maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
|
|
131
|
+
extra: details.extra,
|
|
132
|
+
});
|
|
133
|
+
return fetch(url, {
|
|
134
|
+
method,
|
|
135
|
+
headers: {
|
|
136
|
+
...headers,
|
|
137
|
+
'PAYMENT-SIGNATURE': paymentPayload,
|
|
138
|
+
},
|
|
139
|
+
body: body || undefined,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// ======================================================================
|
|
143
|
+
// Shared helpers
|
|
144
|
+
// ======================================================================
|
|
145
|
+
async function extractPaymentHeader(response) {
|
|
146
|
+
let paymentHeader = response.headers.get('payment-required');
|
|
147
|
+
if (!paymentHeader) {
|
|
148
|
+
try {
|
|
149
|
+
const respBody = (await response.json());
|
|
150
|
+
if (respBody.x402 || respBody.accepts) {
|
|
151
|
+
paymentHeader = btoa(JSON.stringify(respBody));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// ignore parse errors
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return paymentHeader;
|
|
159
|
+
}
|
package/dist/wallet/manager.d.ts
CHANGED
package/dist/wallet/manager.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import { getOrCreateWallet, scanWallets, getWalletAddress, } from '@blockrun/llm';
|
|
1
|
+
import { getOrCreateWallet, scanWallets, getWalletAddress, getOrCreateSolanaWallet, scanSolanaWallets, } from '@blockrun/llm';
|
|
2
|
+
import { loadChain } from '../config.js';
|
|
2
3
|
export function walletExists() {
|
|
3
|
-
const
|
|
4
|
-
|
|
4
|
+
const chain = loadChain();
|
|
5
|
+
if (chain === 'solana') {
|
|
6
|
+
return scanSolanaWallets().length > 0;
|
|
7
|
+
}
|
|
8
|
+
return scanWallets().length > 0;
|
|
5
9
|
}
|
|
6
10
|
export function setupWallet() {
|
|
7
11
|
const { address, isNew } = getOrCreateWallet();
|
|
8
12
|
return { address, isNew };
|
|
9
13
|
}
|
|
14
|
+
export async function setupSolanaWallet() {
|
|
15
|
+
const { address, isNew } = await getOrCreateSolanaWallet();
|
|
16
|
+
return { address, isNew };
|
|
17
|
+
}
|
|
10
18
|
export function getAddress() {
|
|
11
19
|
const addr = getWalletAddress();
|
|
12
20
|
if (!addr)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/cc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Run Claude Code with any model — no rate limits, no account locks, no phone verification. Pay per use with USDC.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@blockrun/llm": "^1.4.2",
|
|
46
|
+
"@solana/spl-token": "^0.4.14",
|
|
47
|
+
"@solana/web3.js": "^1.98.4",
|
|
48
|
+
"bs58": "^6.0.0",
|
|
46
49
|
"chalk": "^5.4.0",
|
|
47
50
|
"commander": "^13.0.0"
|
|
48
51
|
},
|