50c 3.9.1 → 3.9.3

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
@@ -1012,7 +1012,7 @@ const LOCAL_TOOLS = [
1012
1012
 
1013
1013
  // 50c Team utilities - for power users who want granular control
1014
1014
  { name: "team_http", description: "50c Team: HTTP/HTTPS fetch. Bypasses IDE restrictions. FREE.", inputSchema: { type: "object", properties: { url: { type: "string", description: "Full URL to fetch" }, method: { type: "string", description: "HTTP method (default GET)" }, headers: { type: "object", description: "Request headers" }, body: { type: "string", description: "Request body for POST/PUT" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["url"] } },
1015
- { name: "team_ssh", description: "50c Team: SSH remote command. Known servers: nj, zurich, chicago. FREE.", inputSchema: { type: "object", properties: { server: { type: "string", description: "Server alias (nj/zurich/chicago) or {host,user,port}" }, command: { type: "string", description: "Command to execute on remote server" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["server", "command"] } },
1015
+ { name: "team_ssh", description: "50c Team: SSH remote command. Configure servers in ~/.50c/servers.json. FREE.", inputSchema: { type: "object", properties: { server: { type: "string", description: "Server alias (nj/zurich/chicago) or {host,user,port}" }, command: { type: "string", description: "Command to execute on remote server" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["server", "command"] } },
1016
1016
  { name: "team_exec", description: "50c Team: Local command execution. Bypasses shell restrictions. FREE.", inputSchema: { type: "object", properties: { command: { type: "string", description: "Command to execute locally" }, cwd: { type: "string", description: "Working directory" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["command"] } },
1017
1017
  { name: "team_multi", description: "50c Team: Execute multiple tasks in parallel. FREE.", inputSchema: { type: "object", properties: { tasks: { type: "array", description: "Array of task objects [{type, ...params}]", items: { type: "object" } } }, required: ["tasks"] } },
1018
1018
  { name: "team_servers", description: "50c Team: List known SSH servers. FREE.", inputSchema: { type: "object", properties: {} } },
@@ -1020,6 +1020,9 @@ const LOCAL_TOOLS = [
1020
1020
  { name: "team_chain", description: "50c Team: Chain multiple tools together. Output flows via $prev placeholder.", inputSchema: { type: "object", properties: { steps: { type: "array", description: "Array of {tool, args} - use $prev for previous result", items: { type: "object" } }, input: { type: "string", description: "Initial input (optional)" } }, required: ["steps"] } },
1021
1021
  { name: "pre_publish", description: "Pre-publish verification. Thorough checks before npm/github/arxiv/medical publish. Profiles: npm, github, arxiv, medical, science, math. Uses AI tools (bCalc, genius+, web_search) for academic verification. FREE.", inputSchema: { type: "object", properties: { profile: { type: "string", description: "Verification profile: npm, github, arxiv, medical, science, math", enum: ["npm", "github", "arxiv", "medical", "science", "math"] }, cwd: { type: "string", description: "Directory to check (default: current)" }, save_receipt: { type: "boolean", description: "Save receipt as markdown file" } } } },
1022
1022
 
1023
+ // Security audit tool - runs locally, FREE
1024
+ { name: "backdoor_check", description: "Security audit: check for backdoors, unauthorized RDP/SSH, persistence, suspicious connections. Geo-IP tags foreign IPs. Cross-platform. FREE.", inputSchema: { type: "object", properties: { skipGeo: { type: "boolean", description: "Skip geo-IP lookups (faster, offline)" } } } },
1025
+
1023
1026
  // ENTERPRISE PRESET - Auto-Invent Swarm Pipeline ($2.00)
1024
1027
  { name: "auto_invent", description: "ENTERPRISE ($2.00): Full invention pipeline. Chains mind_opener → idea_fold → bcalc → genius_plus → compute → cvi_verify. Produces provable, verified solutions. Requires Enterprise tier.", inputSchema: { type: "object", properties: { problem: { type: "string", description: "Problem or hypothesis to solve/prove" }, constraints: { type: "array", items: { type: "string" }, description: "Hard constraints the solution must satisfy" }, domain: { type: "string", description: "Domain hint: math, physics, engineering, business, code", enum: ["math", "physics", "engineering", "business", "code"] }, rigor: { type: "string", description: "Rigor level: fast, standard, deep, exhaustive (all $2.00)", enum: ["fast", "standard", "deep", "exhaustive"], default: "deep" } }, required: ["problem"] } },
1025
1028
 
@@ -1796,6 +1799,17 @@ async function handleLocalTools(request) {
1796
1799
  if (name === 'team_servers') {
1797
1800
  return mcpResult(id, { ok: true, servers: KNOWN_SERVERS });
1798
1801
  }
1802
+
1803
+ if (name === 'backdoor_check') {
1804
+ try {
1805
+ const { runAudit } = require('../lib/backdoor-checker.js');
1806
+ const result = await runAudit({ skipGeo: args.skipGeo });
1807
+ return mcpResult(id, result);
1808
+ } catch (e) {
1809
+ return mcpResult(id, { ok: false, error: e.message });
1810
+ }
1811
+ }
1812
+
1799
1813
 
1800
1814
  // Pre-publish verification - thorough checks before npm/arxiv/github/medical publish
1801
1815
  if (name === 'pre_publish') {
@@ -0,0 +1,736 @@
1
+ /**
2
+ * 50c Backdoor Checker - Local security audit tool
3
+ * Runs entirely on the user's machine. FREE.
4
+ *
5
+ * Detects:
6
+ * - Unauthorized RDP/SSH logins (with geo-IP)
7
+ * - Persistence mechanisms (scheduled tasks, startup, registry, services)
8
+ * - Suspicious network connections to foreign IPs
9
+ * - Rogue certificates and proxy settings
10
+ * - IDE artifacts (Verdant, etc.)
11
+ * - Unauthorized user accounts
12
+ * - SSH authorized_keys anomalies
13
+ *
14
+ * Cross-platform: Windows (PowerShell), Linux (bash), macOS (bash)
15
+ */
16
+
17
+ const { exec, execSync } = require('child_process');
18
+ const os = require('os');
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ const PLATFORM = os.platform(); // win32, linux, darwin
23
+ const HOME = os.homedir();
24
+
25
+ // Suspicious country indicators in IP geolocation
26
+ const SUSPICIOUS_GEOS = ['CN', 'RU', 'KP', 'IR'];
27
+
28
+ // Known suspicious IDE directories
29
+ const SUSPICIOUS_IDE_DIRS = [
30
+ '.verdent', '.verdant', '.verd',
31
+ '.trae', // ByteDance IDE
32
+ ];
33
+
34
+ // Known suspicious process names
35
+ const SUSPICIOUS_PROCESSES = [
36
+ 'cryptominer', 'xmrig', 'minerd', 'cgminer',
37
+ 'nc.exe', 'ncat', 'netcat',
38
+ 'mimikatz', 'lazagne', 'procdump',
39
+ 'psexec', 'wmic', // lateral movement
40
+ ];
41
+
42
+ /**
43
+ * Run a command and return stdout (or error message)
44
+ */
45
+ function run(cmd, timeout = 30000) {
46
+ try {
47
+ return execSync(cmd, {
48
+ timeout,
49
+ encoding: 'utf8',
50
+ stdio: ['pipe', 'pipe', 'pipe'],
51
+ windowsHide: true,
52
+ maxBuffer: 10 * 1024 * 1024
53
+ }).trim();
54
+ } catch (e) {
55
+ return `[ERROR] ${e.message}`;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Run PowerShell command (Windows)
61
+ */
62
+ function ps(script, timeout = 30000) {
63
+ const escaped = script.replace(/"/g, '\\"');
64
+ return run(`powershell -NoProfile -ExecutionPolicy Bypass -Command "${escaped}"`, timeout);
65
+ }
66
+
67
+ /**
68
+ * Geo-IP lookup using free API (ip-api.com)
69
+ * Returns { country, countryCode, city, isp, org }
70
+ */
71
+ async function geoIP(ip) {
72
+ try {
73
+ const http = require('http');
74
+ return new Promise((resolve, reject) => {
75
+ const req = http.get(`http://ip-api.com/json/${ip}?fields=country,countryCode,city,isp,org`, { timeout: 5000 }, (res) => {
76
+ let data = '';
77
+ res.on('data', c => data += c);
78
+ res.on('end', () => {
79
+ try { resolve(JSON.parse(data)); }
80
+ catch { resolve({ country: 'unknown', countryCode: '??' }); }
81
+ });
82
+ });
83
+ req.on('error', () => resolve({ country: 'unknown', countryCode: '??' }));
84
+ req.on('timeout', () => { req.destroy(); resolve({ country: 'unknown', countryCode: '??' }); });
85
+ });
86
+ } catch {
87
+ return { country: 'unknown', countryCode: '??' };
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Batch geo-IP lookup (ip-api.com supports batch of up to 100)
93
+ */
94
+ async function batchGeoIP(ips) {
95
+ if (ips.length === 0) return {};
96
+ const uniqueIPs = [...new Set(ips)].slice(0, 100);
97
+
98
+ try {
99
+ const http = require('http');
100
+ const body = JSON.stringify(uniqueIPs.map(ip => ({ query: ip, fields: 'query,country,countryCode,city,isp,org' })));
101
+
102
+ return new Promise((resolve) => {
103
+ const req = http.request({
104
+ hostname: 'ip-api.com',
105
+ path: '/batch',
106
+ method: 'POST',
107
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
108
+ timeout: 10000
109
+ }, (res) => {
110
+ let data = '';
111
+ res.on('data', c => data += c);
112
+ res.on('end', () => {
113
+ try {
114
+ const results = JSON.parse(data);
115
+ const map = {};
116
+ for (const r of results) { map[r.query] = r; }
117
+ resolve(map);
118
+ } catch { resolve({}); }
119
+ });
120
+ });
121
+ req.on('error', () => resolve({}));
122
+ req.on('timeout', () => { req.destroy(); resolve({}); });
123
+ req.write(body);
124
+ req.end();
125
+ });
126
+ } catch {
127
+ return {};
128
+ }
129
+ }
130
+
131
+ // ============================================
132
+ // WINDOWS CHECKS
133
+ // ============================================
134
+
135
+ function windowsChecks() {
136
+ const findings = [];
137
+
138
+ // 1. RDP Login History (Event ID 4624 Type 10 = RemoteInteractive)
139
+ findings.push({ check: 'RDP Login History', data: getRDPLogins() });
140
+
141
+ // 2. Failed RDP attempts (Event ID 4625)
142
+ findings.push({ check: 'Failed Login Attempts', data: getFailedLogins() });
143
+
144
+ // 3. Local user accounts
145
+ findings.push({ check: 'Local User Accounts', data: getLocalUsers() });
146
+
147
+ // 4. Scheduled tasks (persistence)
148
+ findings.push({ check: 'Scheduled Tasks', data: getScheduledTasks() });
149
+
150
+ // 5. Startup programs (registry Run keys)
151
+ findings.push({ check: 'Startup Registry Keys', data: getStartupKeys() });
152
+
153
+ // 6. Running services (unusual ones)
154
+ findings.push({ check: 'Services', data: getServices() });
155
+
156
+ // 7. Active network connections
157
+ findings.push({ check: 'Active Network Connections', data: getNetConnections() });
158
+
159
+ // 8. Firewall rules
160
+ findings.push({ check: 'Firewall Rules', data: getFirewallRules() });
161
+
162
+ // 9. Certificates
163
+ findings.push({ check: 'Root Certificates', data: getCertificates() });
164
+
165
+ // 10. Proxy settings
166
+ findings.push({ check: 'Proxy Settings', data: getProxySettings() });
167
+
168
+ // 11. WinRM / Remote Management
169
+ findings.push({ check: 'Remote Management (WinRM)', data: getWinRM() });
170
+
171
+ // 12. SSH authorized_keys
172
+ findings.push({ check: 'SSH Authorized Keys', data: getSSHKeys() });
173
+
174
+ // 13. Suspicious IDE artifacts
175
+ findings.push({ check: 'Suspicious IDE Artifacts', data: getIDEArtifacts() });
176
+
177
+ // 14. PowerShell history (may reveal attacker commands)
178
+ findings.push({ check: 'PowerShell History', data: getPSHistory() });
179
+
180
+ // 15. DNS cache (suspicious domains)
181
+ findings.push({ check: 'DNS Cache', data: getDNSCache() });
182
+
183
+ // 16. Recently installed programs
184
+ findings.push({ check: 'Recently Installed Programs', data: getRecentInstalls() });
185
+
186
+ // 17. Hidden/suspicious processes
187
+ findings.push({ check: 'Suspicious Processes', data: getSuspiciousProcesses() });
188
+
189
+ return findings;
190
+ }
191
+
192
+ function getRDPLogins() {
193
+ const result = ps(`
194
+ try {
195
+ $events = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4624} -MaxEvents 200 -ErrorAction Stop |
196
+ Where-Object { $_.Properties[8].Value -eq 10 } |
197
+ Select-Object -First 50 |
198
+ ForEach-Object {
199
+ $ip = $_.Properties[18].Value
200
+ $user = $_.Properties[5].Value
201
+ $domain = $_.Properties[6].Value
202
+ $time = $_.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
203
+ "$time | $domain\\\\$user | $ip"
204
+ }
205
+ if ($events) { $events -join '\\n' } else { 'No RDP logins found in Security log' }
206
+ } catch { 'Access denied or Security log unavailable - run as Administrator' }
207
+ `, 60000);
208
+ return result;
209
+ }
210
+
211
+ function getFailedLogins() {
212
+ const result = ps(`
213
+ try {
214
+ $events = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625} -MaxEvents 100 -ErrorAction Stop |
215
+ Select-Object -First 30 |
216
+ ForEach-Object {
217
+ $ip = $_.Properties[19].Value
218
+ $user = $_.Properties[5].Value
219
+ $time = $_.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
220
+ "$time | $user | $ip"
221
+ }
222
+ if ($events) { $events -join '\\n' } else { 'No failed login attempts found' }
223
+ } catch { 'Access denied or Security log unavailable - run as Administrator' }
224
+ `, 60000);
225
+ return result;
226
+ }
227
+
228
+ function getLocalUsers() {
229
+ return ps(`
230
+ Get-LocalUser | ForEach-Object {
231
+ $name = $_.Name
232
+ $enabled = $_.Enabled
233
+ $lastLogon = $_.LastLogon
234
+ $desc = $_.Description
235
+ "$name | Enabled=$enabled | LastLogon=$lastLogon | $desc"
236
+ } | Out-String
237
+ `);
238
+ }
239
+
240
+ function getScheduledTasks() {
241
+ return ps(`
242
+ Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' -and $_.TaskPath -notlike '\\\\Microsoft\\\\*' } |
243
+ ForEach-Object {
244
+ $name = $_.TaskName
245
+ $path = $_.TaskPath
246
+ $actions = ($_.Actions | ForEach-Object { $_.Execute + ' ' + $_.Arguments }) -join '; '
247
+ "$path$name | $actions"
248
+ } | Out-String
249
+ `, 60000);
250
+ }
251
+
252
+ function getStartupKeys() {
253
+ const keys = [
254
+ 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run',
255
+ 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce',
256
+ 'HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run',
257
+ 'HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce',
258
+ 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run',
259
+ ];
260
+
261
+ return ps(`
262
+ $keys = @(${keys.map(k => `'${k}'`).join(',')})
263
+ foreach ($key in $keys) {
264
+ if (Test-Path $key) {
265
+ "=== $key ==="
266
+ Get-ItemProperty $key -ErrorAction SilentlyContinue |
267
+ ForEach-Object { $_.PSObject.Properties | Where-Object { $_.Name -notlike 'PS*' } |
268
+ ForEach-Object { " $($_.Name) = $($_.Value)" }
269
+ }
270
+ }
271
+ }
272
+ `);
273
+ }
274
+
275
+ function getServices() {
276
+ return ps(`
277
+ Get-Service | Where-Object { $_.Status -eq 'Running' -and $_.StartType -eq 'Automatic' } |
278
+ Where-Object { $_.DisplayName -notlike 'Windows*' -and $_.DisplayName -notlike 'Microsoft*' -and $_.DisplayName -notlike 'DCOM*' -and $_.DisplayName -notlike 'Plug*' } |
279
+ Select-Object Name, DisplayName, StartType |
280
+ Format-Table -AutoSize | Out-String
281
+ `);
282
+ }
283
+
284
+ function getNetConnections() {
285
+ return ps(`
286
+ Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue |
287
+ Where-Object { $_.RemoteAddress -ne '127.0.0.1' -and $_.RemoteAddress -ne '::1' -and $_.RemoteAddress -ne '0.0.0.0' } |
288
+ Select-Object LocalPort, RemoteAddress, RemotePort, OwningProcess,
289
+ @{N='Process';E={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName}} |
290
+ Sort-Object RemoteAddress |
291
+ Format-Table -AutoSize | Out-String
292
+ `);
293
+ }
294
+
295
+ function getFirewallRules() {
296
+ return ps(`
297
+ Get-NetFirewallRule -Enabled True -Direction Inbound -Action Allow -ErrorAction SilentlyContinue |
298
+ Where-Object { $_.DisplayName -notlike 'Core Networking*' -and $_.DisplayName -notlike 'Windows*' } |
299
+ Select-Object -First 30 DisplayName, Profile, Direction |
300
+ Format-Table -AutoSize | Out-String
301
+ `);
302
+ }
303
+
304
+ function getCertificates() {
305
+ return ps(`
306
+ Get-ChildItem Cert:\\LocalMachine\\Root |
307
+ Where-Object { $_.NotAfter -gt (Get-Date) } |
308
+ Select-Object Subject, Issuer, NotAfter, Thumbprint |
309
+ Format-Table -AutoSize -Wrap | Out-String
310
+ `);
311
+ }
312
+
313
+ function getProxySettings() {
314
+ return ps(`
315
+ $proxy = Get-ItemProperty 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' -ErrorAction SilentlyContinue
316
+ "ProxyEnable: $($proxy.ProxyEnable)"
317
+ "ProxyServer: $($proxy.ProxyServer)"
318
+ "ProxyOverride: $($proxy.ProxyOverride)"
319
+ "AutoConfigURL: $($proxy.AutoConfigURL)"
320
+ ""
321
+ "Environment:"
322
+ "HTTP_PROXY: $env:HTTP_PROXY"
323
+ "HTTPS_PROXY: $env:HTTPS_PROXY"
324
+ "ALL_PROXY: $env:ALL_PROXY"
325
+ `);
326
+ }
327
+
328
+ function getWinRM() {
329
+ return ps(`
330
+ try {
331
+ $status = Get-Service WinRM -ErrorAction Stop
332
+ "WinRM Status: $($status.Status)"
333
+ if ($status.Status -eq 'Running') {
334
+ "WARNING: WinRM is running - remote management is enabled"
335
+ winrm get winrm/config/client 2>$null
336
+ }
337
+ } catch {
338
+ "WinRM: Not found or access denied"
339
+ }
340
+ `);
341
+ }
342
+
343
+ function getSSHKeys() {
344
+ const results = [];
345
+ const sshDir = path.join(HOME, '.ssh');
346
+
347
+ if (fs.existsSync(sshDir)) {
348
+ const authKeys = path.join(sshDir, 'authorized_keys');
349
+ if (fs.existsSync(authKeys)) {
350
+ results.push('=== authorized_keys ===');
351
+ const content = fs.readFileSync(authKeys, 'utf8');
352
+ const keys = content.split('\n').filter(l => l.trim() && !l.startsWith('#'));
353
+ for (const key of keys) {
354
+ const parts = key.trim().split(/\s+/);
355
+ const comment = parts.length >= 3 ? parts.slice(2).join(' ') : 'no-comment';
356
+ const type = parts[0];
357
+ results.push(` ${type} ... ${comment}`);
358
+ }
359
+ } else {
360
+ results.push('No authorized_keys file found');
361
+ }
362
+
363
+ // Check for unusual files in .ssh
364
+ const files = fs.readdirSync(sshDir);
365
+ const unusual = files.filter(f => !['authorized_keys', 'known_hosts', 'config', 'id_rsa', 'id_rsa.pub', 'id_ed25519', 'id_ed25519.pub', 'id_ecdsa', 'id_ecdsa.pub', 'id_ed25519_automation', 'id_ed25519_automation.pub'].includes(f));
366
+ if (unusual.length > 0) {
367
+ results.push(`\nUnusual files in .ssh/: ${unusual.join(', ')}`);
368
+ }
369
+ } else {
370
+ results.push('No .ssh directory found');
371
+ }
372
+
373
+ return results.join('\n');
374
+ }
375
+
376
+ function getIDEArtifacts() {
377
+ const results = [];
378
+
379
+ for (const dir of SUSPICIOUS_IDE_DIRS) {
380
+ const fullPath = path.join(HOME, dir);
381
+ if (fs.existsSync(fullPath)) {
382
+ results.push(`[!] FOUND: ${fullPath}`);
383
+ try {
384
+ const files = fs.readdirSync(fullPath);
385
+ results.push(` Contents: ${files.slice(0, 20).join(', ')}`);
386
+
387
+ // Check for MCP config with embedded secrets
388
+ const mcpPath = path.join(fullPath, 'mcp.json');
389
+ if (fs.existsSync(mcpPath)) {
390
+ results.push(` [!!] MCP config found: ${mcpPath}`);
391
+ try {
392
+ const mcp = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
393
+ const servers = Object.keys(mcp.mcpServers || {});
394
+ results.push(` MCP servers configured: ${servers.join(', ')}`);
395
+ // Check for embedded API keys
396
+ for (const [name, srv] of Object.entries(mcp.mcpServers || {})) {
397
+ if (srv.env) {
398
+ const envKeys = Object.keys(srv.env);
399
+ results.push(` [!] ${name} has env vars: ${envKeys.join(', ')}`);
400
+ }
401
+ }
402
+ } catch {}
403
+ }
404
+ } catch {}
405
+ }
406
+ }
407
+
408
+ // Also check AppData for Verdant
409
+ const appDataPaths = [
410
+ path.join(process.env.APPDATA || '', 'Verdent'),
411
+ path.join(process.env.APPDATA || '', 'Verdant'),
412
+ path.join(process.env.LOCALAPPDATA || '', 'Verdent'),
413
+ path.join(process.env.LOCALAPPDATA || '', 'Verdant'),
414
+ ];
415
+
416
+ for (const p of appDataPaths) {
417
+ if (p && fs.existsSync(p)) {
418
+ results.push(`[!] FOUND AppData: ${p}`);
419
+ try {
420
+ const files = fs.readdirSync(p);
421
+ results.push(` Contents: ${files.slice(0, 20).join(', ')}`);
422
+ } catch {}
423
+ }
424
+ }
425
+
426
+ if (results.length === 0) {
427
+ results.push('No suspicious IDE artifacts found');
428
+ }
429
+
430
+ return results.join('\n');
431
+ }
432
+
433
+ function getPSHistory() {
434
+ const histPath = path.join(HOME, 'AppData', 'Roaming', 'Microsoft', 'Windows', 'PowerShell', 'PSReadLine', 'ConsoleHost_history.txt');
435
+ if (fs.existsSync(histPath)) {
436
+ try {
437
+ const content = fs.readFileSync(histPath, 'utf8');
438
+ const lines = content.split('\n').slice(-100); // last 100 commands
439
+ const suspicious = lines.filter(l => {
440
+ const lower = l.toLowerCase();
441
+ return lower.includes('invoke-webrequest') || lower.includes('downloadstring') ||
442
+ lower.includes('downloadfile') || lower.includes('net.webclient') ||
443
+ lower.includes('iex') || lower.includes('invoke-expression') ||
444
+ lower.includes('bypass') || lower.includes('hidden') ||
445
+ lower.includes('-enc ') || lower.includes('encodedcommand') ||
446
+ lower.includes('certutil') || lower.includes('bitsadmin');
447
+ });
448
+ if (suspicious.length > 0) {
449
+ return `[!] Suspicious PowerShell commands found:\n${suspicious.join('\n')}`;
450
+ }
451
+ return `Last 100 commands checked - no suspicious patterns found (${lines.length} total lines in history)`;
452
+ } catch {
453
+ return 'Could not read PowerShell history';
454
+ }
455
+ }
456
+ return 'No PowerShell history file found';
457
+ }
458
+
459
+ function getDNSCache() {
460
+ return ps(`
461
+ $cache = Get-DnsClientCache -ErrorAction SilentlyContinue |
462
+ Select-Object -First 100 Entry, Data |
463
+ Where-Object { $_.Entry -match '\\.(cn|ru|ir|kp)$' -or $_.Entry -match '(baidu|qq|163|aliyun|tencent|weibo|bytedance|douyin)' }
464
+ if ($cache) {
465
+ "Suspicious DNS entries found:"
466
+ $cache | Format-Table -AutoSize | Out-String
467
+ } else {
468
+ "No suspicious DNS entries (.cn/.ru/.ir/.kp or known Chinese services)"
469
+ }
470
+ `);
471
+ }
472
+
473
+ function getRecentInstalls() {
474
+ return ps(`
475
+ Get-ItemProperty HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* |
476
+ Where-Object { $_.InstallDate -and $_.InstallDate -gt (Get-Date).AddDays(-90).ToString('yyyyMMdd') } |
477
+ Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
478
+ Sort-Object InstallDate -Descending |
479
+ Format-Table -AutoSize | Out-String
480
+ `);
481
+ }
482
+
483
+ function getSuspiciousProcesses() {
484
+ return ps(`
485
+ $procs = Get-Process -ErrorAction SilentlyContinue |
486
+ Select-Object Name, Id, Path, Company |
487
+ Where-Object {
488
+ $suspicious = @(${SUSPICIOUS_PROCESSES.map(p => `'${p}'`).join(',')})
489
+ $_.Name -in $suspicious -or
490
+ ($_.Path -and ($_.Path -match '\\\\Temp\\\\' -or $_.Path -match '\\\\tmp\\\\' -or $_.Path -match '\\\\Downloads\\\\'))
491
+ }
492
+ if ($procs) {
493
+ "[!] Suspicious processes found:"
494
+ $procs | Format-Table -AutoSize | Out-String
495
+ } else {
496
+ "No known suspicious processes found"
497
+ }
498
+ `);
499
+ }
500
+
501
+ // ============================================
502
+ // LINUX CHECKS
503
+ // ============================================
504
+
505
+ function linuxChecks() {
506
+ const findings = [];
507
+
508
+ findings.push({ check: 'SSH Login History', data: run('last -50 2>/dev/null || echo "last command not available"') });
509
+ findings.push({ check: 'Failed SSH Attempts', data: run('grep "Failed password" /var/log/auth.log 2>/dev/null | tail -30 || grep "Failed password" /var/log/secure 2>/dev/null | tail -30 || journalctl -u sshd --no-pager -n 30 2>/dev/null | grep -i "failed" || echo "No auth logs accessible"') });
510
+ findings.push({ check: 'User Accounts', data: run('cat /etc/passwd | grep -v nologin | grep -v /false') });
511
+ findings.push({ check: 'Sudoers', data: run('cat /etc/sudoers 2>/dev/null; ls -la /etc/sudoers.d/ 2>/dev/null') });
512
+ findings.push({ check: 'Crontabs', data: run('for user in $(cut -f1 -d: /etc/passwd); do crontab -l -u $user 2>/dev/null && echo "=== $user ==="; done; cat /etc/crontab 2>/dev/null; ls -la /etc/cron.d/ 2>/dev/null') });
513
+ findings.push({ check: 'SSH Authorized Keys', data: getSSHKeys() });
514
+ findings.push({ check: 'Active Connections', data: run('ss -tunp 2>/dev/null || netstat -tunp 2>/dev/null') });
515
+ findings.push({ check: 'Listening Ports', data: run('ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null') });
516
+ findings.push({ check: 'Running Services', data: run('systemctl list-units --type=service --state=running 2>/dev/null | head -40') });
517
+ findings.push({ check: 'Suspicious IDE Artifacts', data: getIDEArtifacts() });
518
+ findings.push({ check: 'Unusual SUID Binaries', data: run('find / -perm -4000 -type f 2>/dev/null | grep -v -E "(sudo|passwd|ping|mount|su|chsh|chfn|newgrp|gpasswd|pkexec)" | head -20') });
519
+ findings.push({ check: 'Startup Scripts', data: run('ls -la /etc/init.d/ 2>/dev/null; ls -la /etc/rc.local 2>/dev/null; systemctl list-unit-files --state=enabled 2>/dev/null | head -30') });
520
+ findings.push({ check: 'Bash History (suspicious)', data: run('grep -E "curl.*\\|.*sh|wget.*\\|.*sh|python.*-c.*import|nc\\s+-|/dev/tcp|base64.*-d" ~/.bash_history 2>/dev/null | tail -20 || echo "No suspicious bash history patterns"') });
521
+
522
+ return findings;
523
+ }
524
+
525
+ // ============================================
526
+ // macOS CHECKS
527
+ // ============================================
528
+
529
+ function macChecks() {
530
+ const findings = [];
531
+
532
+ // 1. Login history (last works on macOS)
533
+ findings.push({ check: 'Login History', data: run('last -50 2>/dev/null || echo "last command not available"') });
534
+
535
+ // 2. Failed SSH/login attempts via unified log
536
+ findings.push({ check: 'Failed Login Attempts', data: run('log show --predicate \'eventMessage contains "failed" AND eventMessage contains "ssh"\' --style syslog --last 7d 2>/dev/null | tail -30 || echo "No failed SSH in unified log"', 60000) });
537
+
538
+ // 3. Remote login (SSH) status
539
+ findings.push({ check: 'Remote Login (SSH) Status', data: run('systemsetup -getremotelogin 2>/dev/null || echo "Cannot check remote login status"') });
540
+
541
+ // 4. Screen sharing / VNC / ARD
542
+ findings.push({ check: 'Screen Sharing / VNC / ARD', data: run('launchctl list 2>/dev/null | grep -iE "vnc|screensharing|ARD|remote" || echo "No screen sharing services found"') });
543
+
544
+ // 5. User accounts
545
+ findings.push({ check: 'User Accounts', data: run('dscl . list /Users | grep -v "^_" | grep -v daemon | grep -v nobody') });
546
+
547
+ // 6. Admin users
548
+ findings.push({ check: 'Admin Users', data: run('dscl . -read /Groups/admin GroupMembership 2>/dev/null || echo "Cannot read admin group"') });
549
+
550
+ // 7. LaunchAgents (user-level persistence - PRIMARY backdoor vector on macOS)
551
+ findings.push({ check: 'User LaunchAgents', data: getMacLaunchItems(path.join(HOME, 'Library', 'LaunchAgents')) });
552
+
553
+ // 8. System LaunchAgents
554
+ findings.push({ check: 'System LaunchAgents', data: getMacLaunchItems('/Library/LaunchAgents') });
555
+
556
+ // 9. LaunchDaemons (root-level persistence)
557
+ findings.push({ check: 'LaunchDaemons', data: getMacLaunchItems('/Library/LaunchDaemons') });
558
+
559
+ // 10. Login Items (GUI persistence)
560
+ findings.push({ check: 'Login Items', data: run('osascript -e \'tell application "System Events" to get the name of every login item\' 2>/dev/null || echo "Cannot read login items"') });
561
+
562
+ // 11. Crontabs
563
+ findings.push({ check: 'Crontabs', data: run('crontab -l 2>/dev/null || echo "No user crontab"; echo "---"; cat /etc/crontab 2>/dev/null || echo "No /etc/crontab"') });
564
+
565
+ // 12. SSH authorized_keys
566
+ findings.push({ check: 'SSH Authorized Keys', data: getSSHKeys() });
567
+
568
+ // 13. Active connections
569
+ findings.push({ check: 'Active Connections', data: run('netstat -an 2>/dev/null | grep ESTABLISHED | head -40 || lsof -i -P -n 2>/dev/null | grep ESTABLISHED | head -40') });
570
+
571
+ // 14. Listening ports
572
+ findings.push({ check: 'Listening Ports', data: run('lsof -i -P -n 2>/dev/null | grep LISTEN | head -30') });
573
+
574
+ // 15. Profiles (MDM / configuration profiles - can enforce proxy, certs, etc.)
575
+ findings.push({ check: 'Configuration Profiles (MDM)', data: run('profiles list 2>/dev/null || profiles -L 2>/dev/null || echo "No profiles command or no profiles installed"') });
576
+
577
+ // 16. Keychain - look for suspicious root certs
578
+ findings.push({ check: 'Custom Root Certificates', data: run('security find-certificate -a -p /Library/Keychains/System.keychain 2>/dev/null | openssl x509 -noout -subject -issuer 2>/dev/null | head -20 || echo "Cannot enumerate system keychain"') });
579
+
580
+ // 17. Proxy settings
581
+ findings.push({ check: 'Proxy Settings', data: run('networksetup -getwebproxy Wi-Fi 2>/dev/null; networksetup -getsecurewebproxy Wi-Fi 2>/dev/null; networksetup -getautoproxyurl Wi-Fi 2>/dev/null; echo "HTTP_PROXY=$HTTP_PROXY"; echo "HTTPS_PROXY=$HTTPS_PROXY"') });
582
+
583
+ // 18. Firewall status
584
+ findings.push({ check: 'Firewall Status', data: run('/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate 2>/dev/null || echo "Cannot check firewall"') });
585
+
586
+ // 19. Suspicious IDE artifacts
587
+ findings.push({ check: 'Suspicious IDE Artifacts', data: getIDEArtifacts() });
588
+
589
+ // 20. Shell history (suspicious patterns)
590
+ findings.push({ check: 'Shell History (suspicious)', data: run('grep -hE "curl.*\\|.*sh|wget.*\\|.*sh|python.*-c.*import|nc\\s+-|/dev/tcp|base64.*-d|osascript.*-e" ~/.bash_history ~/.zsh_history 2>/dev/null | tail -20 || echo "No suspicious shell history patterns"') });
591
+
592
+ // 21. Gatekeeper and SIP status
593
+ findings.push({ check: 'Gatekeeper & SIP Status', data: run('spctl --status 2>/dev/null; csrutil status 2>/dev/null') });
594
+
595
+ // 22. TCC (privacy permissions) - what apps have accessibility/screen recording/etc.
596
+ findings.push({ check: 'Privacy Permissions (TCC)', data: run('sqlite3 "$HOME/Library/Application Support/com.apple.TCC/TCC.db" "SELECT client,service,auth_value FROM access WHERE auth_value=2" 2>/dev/null || echo "Cannot read TCC database (may need Full Disk Access)"') });
597
+
598
+ return findings;
599
+ }
600
+
601
+ /**
602
+ * Read macOS LaunchAgent/LaunchDaemon plist files for suspicious entries
603
+ */
604
+ function getMacLaunchItems(dirPath) {
605
+ const results = [];
606
+ try {
607
+ if (!fs.existsSync(dirPath)) {
608
+ return `Directory not found: ${dirPath}`;
609
+ }
610
+ const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.plist'));
611
+ if (files.length === 0) {
612
+ return `No plist files in ${dirPath}`;
613
+ }
614
+
615
+ for (const file of files) {
616
+ const fullPath = path.join(dirPath, file);
617
+ // Use plutil to convert plist to readable format
618
+ const content = run(`plutil -p "${fullPath}" 2>/dev/null`);
619
+
620
+ // Flag non-Apple items
621
+ const isApple = file.startsWith('com.apple.') || file.startsWith('com.openssh.');
622
+ const label = isApple ? ' ' : '[!] ';
623
+
624
+ // Extract program/command from plist output
625
+ const programMatch = content.match(/"Program(?:Arguments)?" => (?:\[[\s\S]*?\]|"[^"]*")/);
626
+ const program = programMatch ? programMatch[0].substring(0, 120) : '';
627
+
628
+ results.push(`${label}${file}`);
629
+ if (program) results.push(` ${program}`);
630
+ }
631
+ } catch (e) {
632
+ results.push(`[ERROR] Cannot read ${dirPath}: ${e.message}`);
633
+ }
634
+ return results.join('\n');
635
+ }
636
+
637
+ // ============================================
638
+ // MAIN AUDIT FUNCTION
639
+ // ============================================
640
+
641
+ async function runAudit(options = {}) {
642
+ const startTime = Date.now();
643
+ const result = {
644
+ ok: true,
645
+ platform: PLATFORM,
646
+ hostname: os.hostname(),
647
+ timestamp: new Date().toISOString(),
648
+ user: os.userInfo().username,
649
+ findings: [],
650
+ alerts: [],
651
+ summary: {}
652
+ };
653
+
654
+ // Run platform-specific checks
655
+ if (PLATFORM === 'win32') {
656
+ result.findings = windowsChecks();
657
+ } else if (PLATFORM === 'darwin') {
658
+ result.findings = macChecks();
659
+ } else {
660
+ result.findings = linuxChecks();
661
+ }
662
+
663
+ // Extract unique IPs from findings for geo-lookup
664
+ const allIPs = new Set();
665
+ for (const f of result.findings) {
666
+ if (typeof f.data === 'string') {
667
+ const ipMatches = f.data.match(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g) || [];
668
+ for (const ip of ipMatches) {
669
+ if (!ip.startsWith('127.') && !ip.startsWith('0.') && !ip.startsWith('10.') &&
670
+ !ip.startsWith('192.168.') && !ip.startsWith('169.254.') &&
671
+ !ip.match(/^172\.(1[6-9]|2\d|3[01])\./)) {
672
+ allIPs.add(ip);
673
+ }
674
+ }
675
+ }
676
+ }
677
+
678
+ // Batch geo-IP lookup
679
+ if (allIPs.size > 0 && !options.skipGeo) {
680
+ const geoResults = await batchGeoIP([...allIPs]);
681
+ result.geoIP = {};
682
+
683
+ for (const [ip, geo] of Object.entries(geoResults)) {
684
+ result.geoIP[ip] = {
685
+ country: geo.country,
686
+ countryCode: geo.countryCode,
687
+ city: geo.city,
688
+ isp: geo.isp,
689
+ org: geo.org
690
+ };
691
+
692
+ // Flag suspicious countries
693
+ if (SUSPICIOUS_GEOS.includes(geo.countryCode)) {
694
+ result.alerts.push({
695
+ severity: 'HIGH',
696
+ message: `Connection from suspicious country: ${ip} → ${geo.country} (${geo.city}) [${geo.isp}]`,
697
+ ip,
698
+ geo
699
+ });
700
+ }
701
+ }
702
+ }
703
+
704
+ // Analyze findings for alerts
705
+ for (const f of result.findings) {
706
+ if (typeof f.data === 'string') {
707
+ if (f.data.includes('[!]') || f.data.includes('[!!]')) {
708
+ result.alerts.push({
709
+ severity: f.data.includes('[!!]') ? 'HIGH' : 'MEDIUM',
710
+ message: `${f.check}: ${f.data.split('\n').find(l => l.includes('[!]')) || f.data.substring(0, 200)}`
711
+ });
712
+ }
713
+ }
714
+ }
715
+
716
+ // Summary
717
+ result.summary = {
718
+ totalChecks: result.findings.length,
719
+ alerts: result.alerts.length,
720
+ highSeverity: result.alerts.filter(a => a.severity === 'HIGH').length,
721
+ mediumSeverity: result.alerts.filter(a => a.severity === 'MEDIUM').length,
722
+ uniqueExternalIPs: allIPs.size,
723
+ suspiciousGeoIPs: result.alerts.filter(a => a.ip).length,
724
+ duration: Date.now() - startTime
725
+ };
726
+
727
+ result.verdict = result.summary.highSeverity > 0
728
+ ? 'INVESTIGATE - High severity alerts found'
729
+ : result.summary.mediumSeverity > 0
730
+ ? 'REVIEW - Medium severity items found'
731
+ : 'CLEAN - No suspicious findings';
732
+
733
+ return result;
734
+ }
735
+
736
+ module.exports = { runAudit, windowsChecks, linuxChecks, macChecks, batchGeoIP, geoIP };
package/lib/subagent.js CHANGED
@@ -16,12 +16,18 @@ const http = require('http');
16
16
  const fs = require('fs');
17
17
  const path = require('path');
18
18
 
19
- // Known servers (can be extended via config)
20
- const KNOWN_SERVERS = {
21
- 'nj': { host: '172.93.101.193', user: 'root', name: 'NJ Production' },
22
- 'zurich': { host: '89.21.66.24', user: 'root', name: 'Zurich' },
23
- 'chicago': { host: 'chicago.50c.ai', user: 'root', name: 'Chicago' }
24
- };
19
+ // User-configured servers loaded from ~/.50c/servers.json
20
+ // Run: 50c servers add <alias> <host> <user> to configure
21
+ function loadUserServers() {
22
+ try {
23
+ const serversPath = path.join(require('os').homedir(), '.50c', 'servers.json');
24
+ if (fs.existsSync(serversPath)) {
25
+ return JSON.parse(fs.readFileSync(serversPath, 'utf8'));
26
+ }
27
+ } catch (e) {}
28
+ return {};
29
+ }
30
+ const KNOWN_SERVERS = loadUserServers();
25
31
 
26
32
  /**
27
33
  * HTTP/HTTPS fetch - no shell, pure Node
@@ -43,7 +49,7 @@ async function httpFetch(url, options = {}) {
43
49
  ...(bodyData ? { 'Content-Length': Buffer.byteLength(bodyData) } : {})
44
50
  },
45
51
  timeout: options.timeout || 30000,
46
- rejectUnauthorized: false // Allow self-signed certs
52
+ // TLS certificate validation enabled for security
47
53
  };
48
54
 
49
55
  const req = lib.request(reqOptions, (res) => {
@@ -76,6 +76,45 @@ const FALLBACK_TOOLS = [
76
76
  { slug: 'magnum_number_theory', name: 'magnum_number_theory', description: 'Number theory specialist with mandatory numerical verification. Fixed hallucination problem.', price: 0.35, tier: 'enterprise', category: 'math_verification' },
77
77
  { slug: 'magnum_find_theorem', name: 'magnum_find_theorem', description: 'Find relevant theorems and literature with citations.', price: 0.30, tier: 'enterprise', category: 'math_verification' },
78
78
  { slug: 'magnum_test_conjecture', name: 'magnum_test_conjecture', description: 'Adversarial conjecture testing. Finds counterexamples or estimates plausibility.', price: 0.35, tier: 'enterprise', category: 'math_verification' },
79
+
80
+ // Titan Deep Solvers (Foundation/Enterprise)
81
+ { slug: 'titan_1', name: 'titan_1', description: 'Deep orchestrated solver. 7 phases: Diverge, Ground-truth, Explore, Attack, Resolve, Repair, Verify.', price: 10.00, tier: 'enterprise', category: 'deep_ai' },
82
+ { slug: 'titan_2', name: 'titan_2', description: 'Compute-anchored deep solver. Ground-truth, Analysis, Synthesis, Scoring.', price: 5.00, tier: 'enterprise', category: 'deep_ai' },
83
+ { slug: 'titan_3', name: 'titan_3', description: 'Proof-audited deep solver. 6 phases: understand, construct, verify, audit, fix, synthesize.', price: 10.00, tier: 'enterprise', category: 'deep_ai' },
84
+ { slug: 'titan_4', name: 'titan_4', description: 'Compute-before-assert deep solver. 5 phases: blast, verify, fix, synthesize, claim_audit.', price: 2.00, tier: 'enterprise', category: 'deep_ai' },
85
+
86
+ // Clarity Mode
87
+ { slug: 'clarity', name: 'clarity', description: 'Clarity Mode: tools self-activate based on input relevance. Modes: on/off/auto.', price: 0, tier: 'free', category: 'deep_ai' },
88
+
89
+ // Skeptic POV - adversarial verification
90
+ { slug: 'skeptic_pov', name: 'skeptic_pov', description: '5-lens adversarial verification (Factual, Logical, Computational, Constructive, STEM).', price: 0, tier: 'free', category: 'deep_ai' },
91
+
92
+ // Solve - iterative deep solver
93
+ { slug: 'solve', name: 'solve', description: 'Deep iterative solver. Researches, audits, verifies computationally, and self-corrects.', price: 0, tier: 'free', category: 'deep_ai' },
94
+
95
+ // Justice - ethical reasoning
96
+ { slug: 'justice', name: 'justice', description: 'Multi-framework ethical reasoning engine. 5+ frameworks with internal objections.', price: 0.10, tier: 'pro', category: 'deep_ai' },
97
+
98
+ // Artist Mode - creative construction
99
+ { slug: 'artist_mode', name: 'artist_mode', description: 'Divergent creative construction engine. Novel artifacts under constraints.', price: 0.10, tier: 'pro', category: 'deep_ai' },
100
+
101
+ // Self Modeling - epistemic analysis
102
+ { slug: 'self_modeling', name: 'self_modeling', description: 'Epistemic self-analysis. Calibrates confidence, distinguishes retrieval vs computation.', price: 0.08, tier: 'pro', category: 'deep_ai' },
103
+
104
+ // Magnum Advanced Tools
105
+ { slug: 'magnum_dependency_dag', name: 'magnum_dependency_dag', description: 'Build dependency DAG for proofs. Track theorem/axiom dependencies, find circular refs.', price: 0.25, tier: 'enterprise', category: 'math_verification' },
106
+ { slug: 'magnum_symbolic_computational_hybrid', name: 'magnum_symbolic_computational_hybrid', description: 'Hybrid solver bridging symbolic and computational verification.', price: 0.40, tier: 'enterprise', category: 'math_verification' },
107
+ { slug: 'mcp_conductor', name: 'mcp_conductor', description: 'Meta-orchestrator ensuring Magnum tools consistency. Resolves conflicts.', price: 0.50, tier: 'enterprise', category: 'math_verification' },
108
+
109
+ // Caz dedup
110
+ { slug: 'caz_dedup', name: 'caz_dedup', description: 'Semantic deduplication of items.', price: 0.03, tier: 'starter', category: 'context' },
111
+
112
+ // SEO Tools
113
+ { slug: 'seo_audit', name: 'seo_audit', description: 'Quick SEO audit of a URL.', price: 0.10, tier: 'pro', category: 'web_seo' },
114
+ { slug: 'traffic_optimizer', name: 'traffic_optimizer', description: 'Traffic optimization strategies.', price: 0.10, tier: 'pro', category: 'web_seo' },
115
+ { slug: 'schema_generator', name: 'schema_generator', description: 'Generate JSON-LD structured data.', price: 0.05, tier: 'pro', category: 'web_seo' },
116
+ { slug: 'faq_generator', name: 'faq_generator', description: 'Generate FAQ content with schema.', price: 0.05, tier: 'pro', category: 'web_seo' },
117
+
79
118
  { slug: 'magnum_design_computation', name: 'magnum_design_computation', description: 'Design computational verification experiments with runnable code.', price: 0.30, tier: 'enterprise', category: 'math_verification' },
80
119
  ];
81
120
 
package/package.json CHANGED
@@ -1,39 +1,39 @@
1
- {
2
- "name": "50c",
3
- "version": "3.9.1",
4
- "description": "AI developer tools via MCP. Pay-per-use from $0.01. No subscriptions.",
5
- "bin": {
6
- "50c": "./bin/50c.js"
7
- },
8
- "scripts": {
9
- "postinstall": "node -e \"console.log('\\n50c Hub installed. Run: 50c install\\n')\""
10
- },
11
- "keywords": [
12
- "mcp",
13
- "ai",
14
- "llm",
15
- "cli",
16
- "agent",
17
- "50c",
18
- "hints",
19
- "genius",
20
- "beacon",
21
- "developer-tools",
22
- "context",
23
- "compression",
24
- "caz",
25
- "file-memory"
26
- ],
27
- "author": "genxis",
28
- "license": "SEE LICENSE IN LICENSE",
29
- "homepage": "https://50c.ai",
30
- "engines": {
31
- "node": ">=18.0.0"
32
- },
33
- "files": [
34
- "bin/",
35
- "lib/",
36
- "README.md",
37
- "LICENSE"
38
- ]
39
- }
1
+ {
2
+ "name": "50c",
3
+ "version": "3.9.3",
4
+ "description": "AI developer tools via MCP. Pay-per-use from $0.01. No subscriptions.",
5
+ "bin": {
6
+ "50c": "./bin/50c.js"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node -e \"console.log('\\n50c Hub installed. Run: 50c install\\n')\""
10
+ },
11
+ "keywords": [
12
+ "mcp",
13
+ "ai",
14
+ "llm",
15
+ "cli",
16
+ "agent",
17
+ "50c",
18
+ "hints",
19
+ "genius",
20
+ "beacon",
21
+ "developer-tools",
22
+ "context",
23
+ "compression",
24
+ "caz",
25
+ "file-memory"
26
+ ],
27
+ "author": "genxis",
28
+ "license": "SEE LICENSE IN LICENSE",
29
+ "homepage": "https://50c.ai",
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "files": [
34
+ "bin/",
35
+ "lib/",
36
+ "README.md",
37
+ "LICENSE"
38
+ ]
39
+ }