50c 1.4.1 → 2.0.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/bin/50c.js CHANGED
@@ -1,187 +1,241 @@
1
1
  #!/usr/bin/env node
2
- const https = require('https');
3
- const readline = require('readline');
4
-
5
- const API_ENDPOINT = process.env.FIFTYC_ENDPOINT || 'https://50c.ai';
6
- const MCP_ENDPOINT = API_ENDPOINT + '/mcp';
7
- const API_KEY = process.env.FIFTYC_API_KEY;
8
-
9
- // Version
10
- if (process.argv.includes('--version') || process.argv.includes('-v')) {
11
- console.log(require('../package.json').version);
12
- process.exit(0);
13
- }
14
-
15
- // Help
16
- if (process.argv.includes('--help') || process.argv.includes('-h')) {
17
- showHelp();
18
- process.exit(0);
19
- }
20
-
21
- function showHelp() {
22
- console.log(`
23
- 50c - AI Augmentation CLI & MCP Server
24
-
25
- USAGE:
26
- 50c <command> <input> Direct CLI mode
27
- 50c MCP mode (JSON-RPC via stdin)
28
-
29
- COMMANDS:
30
- genius <problem> Deep problem solving ($0.50)
31
- hints <topic> 5 brutal 2-word hints ($0.05)
32
- hints+ <topic> 10 expanded hints ($0.10)
33
- vibe <working_on> 3 unconventional ideas ($0.05)
34
- one-liner <product> Elevator pitch in 8 words ($0.02)
35
- roast <code> Brutal code review ($0.05)
36
- name-it <does> 5 names + domain check ($0.03)
37
- price-it <product> SaaS pricing strategy ($0.05)
38
- compute <code> Execute Python code ($0.02)
39
-
40
- EXAMPLES:
41
- 50c genius "How do I scale PostgreSQL?"
42
- 50c hints "api design"
43
- 50c one-liner "habit tracker with friends"
44
- 50c roast "const x = localStorage.getItem('jwt')"
45
- 50c name-it "AI that writes emails"
46
- 50c price-it "email automation SaaS"
47
-
48
- ENVIRONMENT:
49
- FIFTYC_API_KEY Your API key (required)
50
- FIFTYC_ENDPOINT Custom endpoint (default: https://50c.ai)
51
-
52
- MCP MODE:
53
- Run without arguments for IDE integration (Claude Desktop, Cursor, etc.)
54
- `);
55
- }
56
-
57
- // Check API key
58
- if (!API_KEY) {
59
- console.error('Error: FIFTYC_API_KEY environment variable required');
60
- console.error('Get your key at: https://50c.ai');
61
- process.exit(1);
62
- }
63
-
64
- // CLI commands
65
- const COMMANDS = ['genius', 'hints', 'hints+', 'vibe', 'compute', 'one-liner', 'roast', 'name-it', 'price-it', 'mind-opener'];
66
- const command = process.argv[2];
67
- const input = process.argv.slice(3).join(' ');
2
+ /**
3
+ * 50c CLI & MCP Server
4
+ * The AI toolkit - one package, all tools, works everywhere
5
+ */
68
6
 
69
- if (command && COMMANDS.includes(command)) {
70
- if (!input) {
71
- console.error(`Error: ${command} requires input`);
72
- process.exit(1);
73
- }
74
-
75
- runCLI(command, input).then(result => {
76
- console.log(result);
77
- process.exit(0);
78
- }).catch(err => {
79
- console.error('Error:', err.message);
80
- process.exit(1);
81
- });
82
- } else if (command) {
83
- startMCPMode();
84
- } else {
85
- startMCPMode();
86
- }
7
+ const readline = require('readline');
8
+ const lib = require('../lib');
87
9
 
88
- async function runCLI(cmd, query) {
89
- // Map CLI commands to MCP tool names
90
- const toolMap = {
91
- 'genius': { name: 'genius', arg: 'problem' },
92
- 'hints': { name: 'hints', arg: 'query' },
93
- 'hints+': { name: 'hints_plus', arg: 'query' },
94
- 'vibe': { name: 'quick_vibe', arg: 'working_on' },
95
- 'compute': { name: 'compute', arg: 'code' },
96
- 'one-liner': { name: 'one_liner', arg: 'product' },
97
- 'roast': { name: 'roast', arg: 'code' },
98
- 'name-it': { name: 'name_it', arg: 'does' },
99
- 'price-it': { name: 'price_it', arg: 'product' },
100
- 'mind-opener': { name: 'mind_opener', arg: 'problem' }
101
- };
10
+ // MCP Protocol Handler
11
+ async function handleMCP(req) {
12
+ const { id, method, params } = req;
102
13
 
103
- const tool = toolMap[cmd];
104
- if (!tool) throw new Error(`Unknown command: ${cmd}`);
14
+ if (method === 'initialize') {
15
+ return {
16
+ jsonrpc: '2.0',
17
+ id,
18
+ result: {
19
+ protocolVersion: '2024-11-05',
20
+ capabilities: { tools: { listChanged: true } },
21
+ serverInfo: { name: '50c', version: '2.0.0' }
22
+ }
23
+ };
24
+ }
105
25
 
106
- const request = {
107
- jsonrpc: '2.0',
108
- id: 1,
109
- method: 'tools/call',
110
- params: {
111
- name: tool.name,
112
- arguments: { [tool.arg]: query }
113
- }
114
- };
26
+ if (method === 'notifications/initialized') {
27
+ return null;
28
+ }
115
29
 
116
- const response = await callRemoteMCP(request);
30
+ if (method === 'tools/list') {
31
+ const tools = await lib.getTools();
32
+ return { jsonrpc: '2.0', id, result: { tools } };
33
+ }
117
34
 
118
- if (response.error) {
119
- throw new Error(response.error.message || JSON.stringify(response.error));
35
+ if (method === 'tools/call') {
36
+ const result = await lib.handleTool(params?.name, params?.arguments || {});
37
+ return {
38
+ jsonrpc: '2.0',
39
+ id,
40
+ result: {
41
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
42
+ }
43
+ };
120
44
  }
121
45
 
122
- return response.result?.content?.[0]?.text || 'No output';
46
+ return {
47
+ jsonrpc: '2.0',
48
+ id,
49
+ error: { code: -32601, message: 'Method not found' }
50
+ };
123
51
  }
124
52
 
125
- function startMCPMode() {
126
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
53
+ // MCP Server Mode
54
+ async function runMCP() {
55
+ const rl = readline.createInterface({
56
+ input: process.stdin,
57
+ output: process.stdout,
58
+ terminal: false
59
+ });
127
60
 
128
61
  rl.on('line', async (line) => {
129
62
  try {
130
- const clean = line.trim();
131
- if (!clean) return;
132
- const request = JSON.parse(clean);
133
- const response = await callRemoteMCP(request);
134
- process.stdout.write(JSON.stringify(response) + '\n');
135
- } catch (e) {
136
- process.stdout.write(JSON.stringify({
63
+ const req = JSON.parse(line);
64
+ const res = await handleMCP(req);
65
+ if (res) console.log(JSON.stringify(res));
66
+ } catch (err) {
67
+ console.log(JSON.stringify({
137
68
  jsonrpc: '2.0',
138
69
  id: null,
139
- error: { code: -32603, message: e.message }
140
- }) + '\n');
70
+ error: { code: -32700, message: 'Parse error' }
71
+ }));
141
72
  }
142
73
  });
143
74
  }
144
75
 
145
- function callRemoteMCP(request) {
146
- return new Promise((resolve, reject) => {
147
- const url = new URL(MCP_ENDPOINT);
148
- const postData = JSON.stringify(request);
149
-
150
- const options = {
151
- hostname: url.hostname,
152
- port: url.port || 443,
153
- path: url.pathname,
154
- method: 'POST',
155
- headers: {
156
- 'Content-Type': 'application/json',
157
- 'Content-Length': Buffer.byteLength(postData),
158
- 'Authorization': `Bearer ${API_KEY}`,
159
- 'User-Agent': '50c-cli/1.4.0'
76
+ // CLI Mode
77
+ async function runCLI(args) {
78
+ const cmd = args[0];
79
+ const cmdArgs = args.slice(1);
80
+
81
+ switch (cmd) {
82
+ case 'status':
83
+ console.log(JSON.stringify(await lib.getStatus(), null, 2));
84
+ break;
85
+
86
+ case 'discover':
87
+ console.log(JSON.stringify(await lib.packs.discover(), null, 2));
88
+ break;
89
+
90
+ case 'enable':
91
+ console.log(JSON.stringify(await lib.packs.enablePack(cmdArgs[0]), null, 2));
92
+ break;
93
+
94
+ case 'disable':
95
+ console.log(JSON.stringify(await lib.packs.disablePack(cmdArgs[0]), null, 2));
96
+ break;
97
+
98
+ case 'packs':
99
+ console.log(JSON.stringify(await lib.packs.listPacks(), null, 2));
100
+ break;
101
+
102
+ // Vault commands
103
+ case 'vault':
104
+ const vaultCmd = cmdArgs[0];
105
+ const vaultArgs = cmdArgs.slice(1);
106
+
107
+ if (vaultCmd === 'init') {
108
+ const passphrase = process.env.VAULT_PASSPHRASE || vaultArgs[0];
109
+ if (!passphrase) {
110
+ console.error('Usage: 50c vault init <passphrase>');
111
+ console.error('Or set VAULT_PASSPHRASE env var');
112
+ process.exit(1);
113
+ }
114
+ console.log(JSON.stringify(await lib.vault.init(passphrase), null, 2));
160
115
  }
161
- };
162
-
163
- const req = https.request(options, (res) => {
164
- let data = '';
165
- res.on('data', chunk => data += chunk);
166
- res.on('end', () => {
116
+ else if (vaultCmd === 'unlock') {
117
+ const passphrase = process.env.VAULT_PASSPHRASE || vaultArgs[0];
118
+ if (!passphrase) {
119
+ console.error('Usage: 50c vault unlock <passphrase>');
120
+ process.exit(1);
121
+ }
122
+ console.log(JSON.stringify(await lib.vault.unlock(passphrase), null, 2));
123
+ }
124
+ else if (vaultCmd === 'lock') {
125
+ console.log(JSON.stringify(lib.vault.lock(), null, 2));
126
+ }
127
+ else if (vaultCmd === 'yolo') {
128
+ const passphrase = process.env.VAULT_PASSPHRASE || vaultArgs[0];
129
+ if (!passphrase) {
130
+ console.error('Usage: 50c vault yolo <passphrase>');
131
+ process.exit(1);
132
+ }
133
+ console.log(JSON.stringify(await lib.vault.yolo(passphrase), null, 2));
134
+ }
135
+ else if (vaultCmd === 'add') {
136
+ const [name, ...valueParts] = vaultArgs;
137
+ const value = valueParts.join(' ');
138
+ if (!name || !value) {
139
+ console.error('Usage: 50c vault add <name> <value>');
140
+ process.exit(1);
141
+ }
142
+ console.log(JSON.stringify(await lib.vault.add(name, value), null, 2));
143
+ }
144
+ else if (vaultCmd === 'get') {
145
+ const name = vaultArgs[0];
146
+ if (!name) {
147
+ console.error('Usage: 50c vault get <name>');
148
+ process.exit(1);
149
+ }
167
150
  try {
168
- resolve(JSON.parse(data));
151
+ console.log(await lib.vault.get(name));
169
152
  } catch (e) {
170
- reject(new Error(`Invalid response: ${data.substring(0, 200)}`));
153
+ console.error(e.message);
154
+ process.exit(1);
171
155
  }
172
- });
173
- });
174
-
175
- req.setTimeout(120000, () => {
176
- req.destroy();
177
- reject(new Error('Request timeout'));
178
- });
179
-
180
- req.on('error', reject);
181
- req.write(postData);
182
- req.end();
183
- });
156
+ }
157
+ else if (vaultCmd === 'list') {
158
+ const namespace = vaultArgs[0];
159
+ const list = await lib.vault.list(namespace);
160
+ list.forEach(n => console.log(n));
161
+ }
162
+ else if (vaultCmd === 'delete' || vaultCmd === 'rm') {
163
+ const name = vaultArgs[0];
164
+ if (!name) {
165
+ console.error('Usage: 50c vault delete <name>');
166
+ process.exit(1);
167
+ }
168
+ console.log(JSON.stringify(await lib.vault.remove(name), null, 2));
169
+ }
170
+ else if (vaultCmd === 'status') {
171
+ console.log(JSON.stringify(await lib.vault.status(), null, 2));
172
+ }
173
+ else {
174
+ console.log(`50c vault commands:
175
+ init <passphrase> Initialize vault
176
+ unlock <passphrase> Unlock for session
177
+ lock Lock immediately
178
+ yolo <passphrase> Stay unlocked (dev mode)
179
+ add <name> <value> Add credential
180
+ get <name> Get credential
181
+ list [namespace] List credentials
182
+ delete <name> Delete credential
183
+ status Check vault status`);
184
+ }
185
+ break;
186
+
187
+ case 'help':
188
+ default:
189
+ console.log(`50c - The AI Toolkit
190
+
191
+ Usage:
192
+ 50c Start MCP server (for AI tools)
193
+ 50c status Show status
194
+ 50c discover Show available packs
195
+ 50c enable <pack> Enable a pack
196
+ 50c disable <pack> Disable a pack
197
+ 50c packs List packs
198
+ 50c vault <cmd> Vault commands
199
+
200
+ Packs:
201
+ vault Secure credentials (always on)
202
+ whm WHM/cPanel/SSH (39 tools)
203
+ cf Cloudflare (34 tools)
204
+ wp WordPress (14 tools)
205
+ ux UI/UX toolkit (17 tools)
206
+
207
+ MCP Config:
208
+ {
209
+ "mcpServers": {
210
+ "50c": {
211
+ "command": "50c",
212
+ "env": { "FIFTY_CENT_API_KEY": "cv_xxx" }
213
+ }
214
+ }
215
+ }
216
+
217
+ More info: https://50c.ai/docs`);
218
+ }
219
+ }
220
+
221
+ // Main
222
+ async function main() {
223
+ const args = process.argv.slice(2);
224
+
225
+ // MCP mode: --mcp flag or no args AND piped input
226
+ const isMCPMode = args[0] === '--mcp' || (args.length === 0 && !process.stdin.isTTY);
227
+
228
+ if (isMCPMode) {
229
+ await runMCP();
230
+ } else if (args.length === 0) {
231
+ // No args, show help
232
+ await runCLI(['help']);
233
+ } else {
234
+ await runCLI(args);
235
+ }
184
236
  }
185
237
 
186
- process.on('SIGINT', () => process.exit(130));
187
- process.on('SIGTERM', () => process.exit(143));
238
+ main().catch(err => {
239
+ console.error('Error:', err.message);
240
+ process.exit(1);
241
+ });
package/lib/config.js ADDED
@@ -0,0 +1,185 @@
1
+ /**
2
+ * 50c Config & Mode Detection
3
+ * Handles local vs cloud mode automatically
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+ const https = require('https');
10
+
11
+ // Storage locations by OS
12
+ function getLocalDir() {
13
+ if (process.platform === 'win32') {
14
+ return path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), '50c');
15
+ } else if (process.platform === 'darwin') {
16
+ return path.join(os.homedir(), 'Library', 'Application Support', '50c');
17
+ } else {
18
+ return path.join(os.homedir(), '.local', 'share', '50c');
19
+ }
20
+ }
21
+
22
+ const LOCAL_DIR = getLocalDir();
23
+ const CONFIG_FILE = path.join(LOCAL_DIR, 'config.json');
24
+ const VAULT_DIR = path.join(LOCAL_DIR, 'vault');
25
+ const API_URL = process.env.FIFTY_CENT_API_URL || 'https://api.50c.ai';
26
+
27
+ // Default config
28
+ const DEFAULT_CONFIG = {
29
+ api_key: '',
30
+ packs: {
31
+ vault: true, // Always on
32
+ whm: false,
33
+ cf: false,
34
+ wp: false,
35
+ ux: false
36
+ },
37
+ vault: {
38
+ yolo_mode: false,
39
+ session_ttl: 3600,
40
+ idle_timeout: 1800
41
+ }
42
+ };
43
+
44
+ // Detect mode: local or cloud
45
+ function detectMode() {
46
+ // Explicit override
47
+ if (process.env.FIFTY_CENT_MODE === 'cloud') return 'cloud';
48
+ if (process.env.FIFTY_CENT_MODE === 'local') return 'local';
49
+
50
+ // Cloud indicators
51
+ if (process.env.REPL_ID) return 'cloud'; // Replit
52
+ if (process.env.LOVABLE_PROJECT) return 'cloud'; // Lovable
53
+ if (process.env.CODESPACE_NAME) return 'cloud'; // GitHub Codespaces
54
+ if (process.env.CODESANDBOX_SSE) return 'cloud'; // CodeSandbox
55
+ if (process.env.GITPOD_WORKSPACE_ID) return 'cloud'; // Gitpod
56
+
57
+ // Check if we can write locally
58
+ try {
59
+ if (!fs.existsSync(LOCAL_DIR)) {
60
+ fs.mkdirSync(LOCAL_DIR, { recursive: true, mode: 0o700 });
61
+ }
62
+ const testFile = path.join(LOCAL_DIR, '.write-test');
63
+ fs.writeFileSync(testFile, 'test', { mode: 0o600 });
64
+ fs.unlinkSync(testFile);
65
+ return 'local';
66
+ } catch {
67
+ return 'cloud';
68
+ }
69
+ }
70
+
71
+ const MODE = detectMode();
72
+
73
+ // Ensure local directory exists
74
+ function ensureLocalDir() {
75
+ if (MODE !== 'local') return;
76
+ if (!fs.existsSync(LOCAL_DIR)) {
77
+ fs.mkdirSync(LOCAL_DIR, { recursive: true, mode: 0o700 });
78
+ }
79
+ if (!fs.existsSync(VAULT_DIR)) {
80
+ fs.mkdirSync(VAULT_DIR, { recursive: true, mode: 0o700 });
81
+ }
82
+ }
83
+
84
+ // Load config (local or cloud)
85
+ async function loadConfig() {
86
+ if (MODE === 'local') {
87
+ return loadConfigLocal();
88
+ } else {
89
+ return loadConfigCloud();
90
+ }
91
+ }
92
+
93
+ function loadConfigLocal() {
94
+ try {
95
+ if (fs.existsSync(CONFIG_FILE)) {
96
+ const saved = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
97
+ return { ...DEFAULT_CONFIG, ...saved, packs: { ...DEFAULT_CONFIG.packs, ...saved.packs } };
98
+ }
99
+ } catch {}
100
+ return { ...DEFAULT_CONFIG };
101
+ }
102
+
103
+ async function loadConfigCloud() {
104
+ const apiKey = process.env.FIFTY_CENT_API_KEY;
105
+ if (!apiKey) return { ...DEFAULT_CONFIG };
106
+
107
+ try {
108
+ const response = await apiRequest('GET', '/user/config');
109
+ if (response && response.packs) {
110
+ return { ...DEFAULT_CONFIG, ...response, packs: { ...DEFAULT_CONFIG.packs, ...response.packs } };
111
+ }
112
+ } catch {}
113
+ return { ...DEFAULT_CONFIG };
114
+ }
115
+
116
+ // Save config (local or cloud)
117
+ async function saveConfig(config) {
118
+ if (MODE === 'local') {
119
+ return saveConfigLocal(config);
120
+ } else {
121
+ return saveConfigCloud(config);
122
+ }
123
+ }
124
+
125
+ function saveConfigLocal(config) {
126
+ ensureLocalDir();
127
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
128
+ }
129
+
130
+ async function saveConfigCloud(config) {
131
+ const apiKey = process.env.FIFTY_CENT_API_KEY;
132
+ if (!apiKey) throw new Error('API key required for cloud mode');
133
+
134
+ await apiRequest('PUT', '/user/config', { packs: config.packs, vault: config.vault });
135
+ }
136
+
137
+ // API request helper
138
+ function apiRequest(method, endpoint, body = null) {
139
+ return new Promise((resolve, reject) => {
140
+ const apiKey = process.env.FIFTY_CENT_API_KEY || '';
141
+ const url = new URL(endpoint, API_URL);
142
+
143
+ const options = {
144
+ hostname: url.hostname,
145
+ port: url.port || 443,
146
+ path: url.pathname,
147
+ method: method,
148
+ headers: {
149
+ 'Content-Type': 'application/json',
150
+ 'Authorization': `Bearer ${apiKey}`,
151
+ 'User-Agent': '50c/2.0.0'
152
+ }
153
+ };
154
+
155
+ const req = https.request(options, (res) => {
156
+ let data = '';
157
+ res.on('data', chunk => data += chunk);
158
+ res.on('end', () => {
159
+ try {
160
+ resolve(JSON.parse(data));
161
+ } catch {
162
+ resolve({ raw: data });
163
+ }
164
+ });
165
+ });
166
+
167
+ req.on('error', reject);
168
+ if (body) req.write(JSON.stringify(body));
169
+ req.end();
170
+ });
171
+ }
172
+
173
+ module.exports = {
174
+ MODE,
175
+ LOCAL_DIR,
176
+ VAULT_DIR,
177
+ CONFIG_FILE,
178
+ API_URL,
179
+ DEFAULT_CONFIG,
180
+ detectMode,
181
+ ensureLocalDir,
182
+ loadConfig,
183
+ saveConfig,
184
+ apiRequest
185
+ };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * 50c Core Tools - Always Free
3
+ * search, fetch, domain_check, balance
4
+ */
5
+
6
+ const https = require('https');
7
+ const http = require('http');
8
+ const { apiRequest } = require('../config');
9
+
10
+ // Web Search
11
+ async function webSearch(query, options = {}) {
12
+ return apiRequest('POST', '/tools/web_search', {
13
+ query,
14
+ max_results: options.max_results || 5
15
+ });
16
+ }
17
+
18
+ // Page Fetch
19
+ async function pageFetch(url, query = '') {
20
+ return apiRequest('POST', '/tools/page_fetch', { url, query });
21
+ }
22
+
23
+ // Domain Check
24
+ async function domainCheck(domain) {
25
+ return apiRequest('POST', '/tools/domain_check', { domain });
26
+ }
27
+
28
+ // Check Balance
29
+ async function checkBalance() {
30
+ return apiRequest('GET', '/user/balance');
31
+ }
32
+
33
+ // Tool definitions
34
+ const CORE_TOOLS = [
35
+ {
36
+ name: 'web_search',
37
+ description: 'Search the internet. FREE.',
38
+ inputSchema: {
39
+ type: 'object',
40
+ properties: {
41
+ query: { type: 'string', description: 'Search query' },
42
+ max_results: { type: 'number', default: 5 }
43
+ },
44
+ required: ['query']
45
+ }
46
+ },
47
+ {
48
+ name: 'page_fetch',
49
+ description: 'Fetch and extract content from URL. FREE.',
50
+ inputSchema: {
51
+ type: 'object',
52
+ properties: {
53
+ url: { type: 'string', description: 'URL to fetch' },
54
+ query: { type: 'string', description: 'Optional: question about the page' }
55
+ },
56
+ required: ['url']
57
+ }
58
+ },
59
+ {
60
+ name: 'domain_check',
61
+ description: 'Check domain availability. FREE.',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ domain: { type: 'string', description: 'Domain to check (e.g., example.com)' }
66
+ },
67
+ required: ['domain']
68
+ }
69
+ },
70
+ {
71
+ name: 'check_balance',
72
+ description: 'Check your credit balance. FREE.',
73
+ inputSchema: {
74
+ type: 'object',
75
+ properties: {}
76
+ }
77
+ }
78
+ ];
79
+
80
+ // Handle core tool calls
81
+ async function handleTool(name, args) {
82
+ try {
83
+ switch (name) {
84
+ case 'web_search':
85
+ return await webSearch(args.query, args);
86
+ case 'page_fetch':
87
+ return await pageFetch(args.url, args.query);
88
+ case 'domain_check':
89
+ return await domainCheck(args.domain);
90
+ case 'check_balance':
91
+ return await checkBalance();
92
+ default:
93
+ return { error: `Unknown core tool: ${name}` };
94
+ }
95
+ } catch (e) {
96
+ return { error: e.message };
97
+ }
98
+ }
99
+
100
+ module.exports = {
101
+ CORE_TOOLS,
102
+ handleTool,
103
+ webSearch,
104
+ pageFetch,
105
+ domainCheck,
106
+ checkBalance
107
+ };