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/README.md +49 -226
- package/bin/50c.js +215 -161
- package/lib/config.js +185 -0
- package/lib/core/tools.js +107 -0
- package/lib/index.js +166 -0
- package/lib/packs/beacon.js +224 -0
- package/lib/packs/cf.js +156 -0
- package/lib/packs/labs.js +188 -0
- package/lib/packs/labs_plus.js +246 -0
- package/lib/packs/ux.js +76 -0
- package/lib/packs/whm.js +228 -0
- package/lib/packs/wp.js +82 -0
- package/lib/packs.js +406 -0
- package/lib/vault.js +354 -0
- package/package.json +25 -11
- package/LICENSE +0 -31
package/bin/50c.js
CHANGED
|
@@ -1,187 +1,241 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
30
|
+
if (method === 'tools/list') {
|
|
31
|
+
const tools = await lib.getTools();
|
|
32
|
+
return { jsonrpc: '2.0', id, result: { tools } };
|
|
33
|
+
}
|
|
117
34
|
|
|
118
|
-
if (
|
|
119
|
-
|
|
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
|
|
46
|
+
return {
|
|
47
|
+
jsonrpc: '2.0',
|
|
48
|
+
id,
|
|
49
|
+
error: { code: -32601, message: 'Method not found' }
|
|
50
|
+
};
|
|
123
51
|
}
|
|
124
52
|
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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: -
|
|
140
|
-
})
|
|
70
|
+
error: { code: -32700, message: 'Parse error' }
|
|
71
|
+
}));
|
|
141
72
|
}
|
|
142
73
|
});
|
|
143
74
|
}
|
|
144
75
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
151
|
+
console.log(await lib.vault.get(name));
|
|
169
152
|
} catch (e) {
|
|
170
|
-
|
|
153
|
+
console.error(e.message);
|
|
154
|
+
process.exit(1);
|
|
171
155
|
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
187
|
-
|
|
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
|
+
};
|