50c 2.11.0 → 2.15.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 +7 -0
- package/lib/index.js +80 -2
- package/lib/packs/grabr.js +5 -5
- package/lib/vault.js +37 -4
- package/package.json +2 -2
package/bin/50c.js
CHANGED
|
@@ -464,6 +464,13 @@ async function main() {
|
|
|
464
464
|
// MCP mode: --mcp flag or no args AND piped input
|
|
465
465
|
const isMCPMode = args[0] === '--mcp' || (args.length === 0 && !process.stdin.isTTY);
|
|
466
466
|
|
|
467
|
+
// Version check (non-blocking, only warns)
|
|
468
|
+
if (!isMCPMode && args[0] !== 'help' && args[0] !== '--help') {
|
|
469
|
+
lib.checkVersion().then(v => {
|
|
470
|
+
if (v.message) console.error(`\n Warning: ${v.message}\n`);
|
|
471
|
+
}).catch(() => {});
|
|
472
|
+
}
|
|
473
|
+
|
|
467
474
|
if (isMCPMode) {
|
|
468
475
|
await runMCP();
|
|
469
476
|
} else if (args.length === 0) {
|
package/lib/index.js
CHANGED
|
@@ -25,9 +25,83 @@ const ux = require('./packs/ux');
|
|
|
25
25
|
const promptEngine = require('./packs/prompt_engine');
|
|
26
26
|
const grabr = require('./packs/grabr');
|
|
27
27
|
|
|
28
|
+
// ═══════════════════════════════════════════════════════════════
|
|
29
|
+
// MANIFEST - SINGLE SOURCE OF TRUTH
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════
|
|
31
|
+
// genxis.one/v1/manifest is the canonical tool registry
|
|
32
|
+
// CLI bootstraps from it, caches locally, validates versions
|
|
33
|
+
|
|
34
|
+
const MANIFEST_URL = 'https://api.50c.ai/v1/manifest';
|
|
35
|
+
const MANIFEST_CACHE_TTL = 3600000; // 1 hour
|
|
36
|
+
let manifestCache = null;
|
|
37
|
+
let manifestCacheTime = 0;
|
|
38
|
+
|
|
39
|
+
async function fetchManifest(staging = false) {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
if (manifestCache && (now - manifestCacheTime) < MANIFEST_CACHE_TTL) {
|
|
42
|
+
return manifestCache;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const https = require('https');
|
|
47
|
+
const headers = staging ? { 'X-GenXis-Env': 'staging' } : {};
|
|
48
|
+
|
|
49
|
+
const data = await new Promise((resolve, reject) => {
|
|
50
|
+
const req = https.get(MANIFEST_URL, { headers, timeout: 10000 }, (res) => {
|
|
51
|
+
let body = '';
|
|
52
|
+
res.on('data', chunk => body += chunk);
|
|
53
|
+
res.on('end', () => {
|
|
54
|
+
try {
|
|
55
|
+
resolve(JSON.parse(body));
|
|
56
|
+
} catch (e) {
|
|
57
|
+
reject(new Error('Invalid manifest JSON'));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
req.on('error', reject);
|
|
62
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Manifest timeout')); });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
manifestCache = data;
|
|
66
|
+
manifestCacheTime = now;
|
|
67
|
+
return data;
|
|
68
|
+
} catch (e) {
|
|
69
|
+
// Fallback to local tools if manifest unavailable
|
|
70
|
+
console.error('Manifest fetch failed, using local tools:', e.message);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function checkVersion() {
|
|
76
|
+
try {
|
|
77
|
+
const manifest = await fetchManifest();
|
|
78
|
+
if (!manifest) return { upToDate: true };
|
|
79
|
+
|
|
80
|
+
const pkg = require('../package.json');
|
|
81
|
+
const minVersion = manifest.meta?.min_client_version || '0.0.0';
|
|
82
|
+
const currentVersion = pkg.version;
|
|
83
|
+
|
|
84
|
+
const [minMajor, minMinor] = minVersion.split('.').map(Number);
|
|
85
|
+
const [curMajor, curMinor] = currentVersion.split('.').map(Number);
|
|
86
|
+
|
|
87
|
+
const upToDate = curMajor > minMajor || (curMajor === minMajor && curMinor >= minMinor);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
upToDate,
|
|
91
|
+
current: currentVersion,
|
|
92
|
+
minimum: minVersion,
|
|
93
|
+
latest: manifest.meta?.api_version,
|
|
94
|
+
message: upToDate ? null : `Update available: npx 50c@latest`
|
|
95
|
+
};
|
|
96
|
+
} catch (e) {
|
|
97
|
+
return { upToDate: true, error: e.message };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
28
101
|
// Tool name mappings by pack
|
|
29
102
|
const TOOL_PACKS = {
|
|
30
|
-
beacon: ['hints', 'hints_plus', 'roast', 'quick_vibe', 'one_liner', 'name_it', 'price_it', 'compute', 'ide_conversation', 'learning_stats'
|
|
103
|
+
beacon: ['hints', 'hints_plus', 'roast', 'quick_vibe', 'one_liner', 'name_it', 'price_it', 'compute', 'ide_conversation', 'learning_stats',
|
|
104
|
+
'beacon_scan', 'beacon_compress', 'beacon_extract', 'beacon_focus', 'beacon_rank'],
|
|
31
105
|
labs: ['genius', 'mind_opener', 'idea_fold', 'agent_autopsy', 'prompt_fortress', 'context_health', 'context_compress', 'context_extract', 'context_reposition'],
|
|
32
106
|
labs_plus: ['bcalc', 'genius_plus', 'bcalc_why', 'discovery_collision', 'cvi_loop', 'cvi_verify', 'chaos_fingerprint', 'resonance', 'prime_residue', 'echo_sequence', 'conversation_diagnostic', 'handoff'],
|
|
33
107
|
prompt_engine: ['prompt_extract', 'prompt_phases', 'prompt_refine', 'prompt_expand', 'prompt_categorize'],
|
|
@@ -195,5 +269,9 @@ module.exports = {
|
|
|
195
269
|
labs,
|
|
196
270
|
labsPlus,
|
|
197
271
|
promptEngine,
|
|
198
|
-
grabr
|
|
272
|
+
grabr,
|
|
273
|
+
// Manifest - Single Source of Truth
|
|
274
|
+
fetchManifest,
|
|
275
|
+
checkVersion,
|
|
276
|
+
MANIFEST_URL
|
|
199
277
|
};
|
package/lib/packs/grabr.js
CHANGED
|
@@ -110,7 +110,7 @@ async function grabrScrape(url, depth = 1) {
|
|
|
110
110
|
|
|
111
111
|
try {
|
|
112
112
|
// Use 50c page_fetch via API
|
|
113
|
-
const result = await apiRequest('page_fetch', { url });
|
|
113
|
+
const result = await apiRequest('POST', '/tools/page_fetch', { url });
|
|
114
114
|
if (result.error) return { error: result.error };
|
|
115
115
|
|
|
116
116
|
const html = result.content || result.text || '';
|
|
@@ -130,7 +130,7 @@ async function grabrScrape(url, depth = 1) {
|
|
|
130
130
|
if (href) {
|
|
131
131
|
try {
|
|
132
132
|
const fullUrl = href.startsWith('http') ? href : new URL(href, url).href;
|
|
133
|
-
const subResult = await apiRequest('page_fetch', { url: fullUrl });
|
|
133
|
+
const subResult = await apiRequest('POST', '/tools/page_fetch', { url: fullUrl });
|
|
134
134
|
if (subResult.content) {
|
|
135
135
|
contacts.emails.push(...extractEmails(subResult.content));
|
|
136
136
|
contacts.phones.push(...extractPhones(subResult.content));
|
|
@@ -237,15 +237,15 @@ async function grabrSitemap(url) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
try {
|
|
240
|
-
const result = await apiRequest('page_fetch', { url: sitemapUrl });
|
|
240
|
+
const result = await apiRequest('POST', '/tools/page_fetch', { url: sitemapUrl });
|
|
241
241
|
if (result.error) {
|
|
242
242
|
// Try robots.txt fallback
|
|
243
243
|
const robotsUrl = url.replace(/\/$/, '') + '/robots.txt';
|
|
244
|
-
const robotsResult = await apiRequest('page_fetch', { url: robotsUrl });
|
|
244
|
+
const robotsResult = await apiRequest('POST', '/tools/page_fetch', { url: robotsUrl });
|
|
245
245
|
if (robotsResult.content) {
|
|
246
246
|
const sitemapMatch = robotsResult.content.match(/Sitemap:\s*(\S+)/i);
|
|
247
247
|
if (sitemapMatch) {
|
|
248
|
-
const altResult = await apiRequest('page_fetch', { url: sitemapMatch[1] });
|
|
248
|
+
const altResult = await apiRequest('POST', '/tools/page_fetch', { url: sitemapMatch[1] });
|
|
249
249
|
if (altResult.content) {
|
|
250
250
|
result.content = altResult.content;
|
|
251
251
|
}
|
package/lib/vault.js
CHANGED
|
@@ -15,6 +15,9 @@ const SALT_LENGTH = 32;
|
|
|
15
15
|
const IV_LENGTH = 16;
|
|
16
16
|
const AUTH_TAG_LENGTH = 16;
|
|
17
17
|
|
|
18
|
+
// Security: Auto-lock after 5 minutes of inactivity (300 seconds)
|
|
19
|
+
const AUTO_LOCK_INACTIVITY_MS = 5 * 60 * 1000;
|
|
20
|
+
|
|
18
21
|
// Local file paths
|
|
19
22
|
const MASTER_KEY_FILE = path.join(VAULT_DIR, 'master.key.enc');
|
|
20
23
|
const VAULT_DB_FILE = path.join(VAULT_DIR, 'vault.db');
|
|
@@ -54,10 +57,25 @@ function loadLocalSession() {
|
|
|
54
57
|
try {
|
|
55
58
|
if (fs.existsSync(SESSION_FILE)) {
|
|
56
59
|
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf8'));
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
|
|
62
|
+
// Check TTL expiry
|
|
63
|
+
if (session.expires_at <= now) {
|
|
64
|
+
fs.unlinkSync(SESSION_FILE);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check inactivity auto-lock (5 minutes)
|
|
69
|
+
if (session.last_access && (now - session.last_access) > AUTO_LOCK_INACTIVITY_MS) {
|
|
70
|
+
fs.unlinkSync(SESSION_FILE);
|
|
71
|
+
return null;
|
|
59
72
|
}
|
|
60
|
-
|
|
73
|
+
|
|
74
|
+
// Update last_access timestamp
|
|
75
|
+
session.last_access = now;
|
|
76
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session), { mode: 0o600 });
|
|
77
|
+
|
|
78
|
+
return session;
|
|
61
79
|
}
|
|
62
80
|
} catch {}
|
|
63
81
|
return null;
|
|
@@ -291,11 +309,26 @@ async function remove(name) {
|
|
|
291
309
|
}
|
|
292
310
|
|
|
293
311
|
async function status() {
|
|
312
|
+
const session = MODE === 'local' ? loadLocalSession() : null;
|
|
313
|
+
const now = Date.now();
|
|
314
|
+
|
|
315
|
+
let autoLockIn = null;
|
|
316
|
+
if (session && session.last_access) {
|
|
317
|
+
const remaining = AUTO_LOCK_INACTIVITY_MS - (now - session.last_access);
|
|
318
|
+
autoLockIn = remaining > 0 ? Math.ceil(remaining / 1000) : 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
294
321
|
return {
|
|
295
322
|
mode: MODE,
|
|
296
323
|
initialized: await isInitialized(),
|
|
297
324
|
locked: !(await isUnlocked()),
|
|
298
|
-
path: MODE === 'local' ? VAULT_DIR : 'cloud'
|
|
325
|
+
path: MODE === 'local' ? VAULT_DIR : 'cloud',
|
|
326
|
+
auto_lock_seconds: autoLockIn,
|
|
327
|
+
security: {
|
|
328
|
+
auto_lock_inactivity: '5 minutes',
|
|
329
|
+
encryption: 'AES-256-GCM',
|
|
330
|
+
pbkdf2_iterations: PBKDF2_ITERATIONS
|
|
331
|
+
}
|
|
299
332
|
};
|
|
300
333
|
}
|
|
301
334
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "50c",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "AI toolkit
|
|
3
|
+
"version": "2.15.0",
|
|
4
|
+
"description": "AI toolkit. Added beacon_* tools to TOOL_PACKS routing.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"50c": "./bin/50c.js"
|