50c 4.0.0 → 4.1.1

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/package.json CHANGED
@@ -1,40 +1,26 @@
1
1
  {
2
2
  "name": "50c",
3
- "version": "4.0.0",
4
- "description": "AI developer tools via MCP. 150+ tools, pay-per-use from $0.01, no subscriptions. Drift-watch + smoke-tested + plan-aware.",
5
- "scripts_comment": "Run `node test-mcp.js` for smoke tests before publish.",
3
+ "version": "4.1.1",
4
+ "description": "Remote shell for GenXis 50c / 50c-PREVIEW",
6
5
  "bin": {
7
- "50c": "./bin/50c.js"
6
+ "50c": "bin/50c.js"
8
7
  },
9
- "scripts": {
10
- "postinstall": "node -e \"console.log('\\n50c Hub installed. Run: 50c install\\n')\""
8
+ "files": [
9
+ "bin/50c.js",
10
+ "README.md"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
11
14
  },
15
+ "license": "UNLICENSED",
12
16
  "keywords": [
13
- "mcp",
14
- "ai",
15
- "llm",
16
- "cli",
17
- "agent",
18
17
  "50c",
19
- "hints",
20
- "genius",
21
- "beacon",
22
- "developer-tools",
23
- "context",
24
- "compression",
25
- "caz",
26
- "file-memory"
18
+ "50c-preview",
19
+ "cli",
20
+ "mcp"
27
21
  ],
28
- "author": "genxis",
29
- "license": "SEE LICENSE IN LICENSE",
30
- "homepage": "https://50c.ai",
31
- "engines": {
32
- "node": ">=18.0.0"
33
- },
34
- "files": [
35
- "bin/",
36
- "lib/",
37
- "README.md",
38
- "LICENSE"
39
- ]
22
+ "homepage": "https://c.50c.ai",
23
+ "publishConfig": {
24
+ "access": "public"
25
+ }
40
26
  }
package/LICENSE DELETED
@@ -1,31 +0,0 @@
1
- PROPRIETARY SOFTWARE LICENSE
2
-
3
- Copyright (c) 2026 genxis. All Rights Reserved.
4
-
5
- This software and associated documentation files (the "Software") are the
6
- proprietary property of genxis.
7
-
8
- USAGE TERMS:
9
- 1. You may install and use this Software only with a valid API key.
10
- 2. Each use of the Software requires credits at published rates.
11
- 3. You may NOT copy, modify, merge, publish, distribute, sublicense, or sell
12
- copies of the Software.
13
- 4. You may NOT reverse engineer, decompile, or disassemble the Software.
14
- 5. You may NOT remove or bypass the payment/credit system.
15
- 6. You may NOT create derivative works based on the Software.
16
-
17
- RESTRICTIONS:
18
- - This is a commercial product. No free usage rights are granted.
19
- - Source code is provided for transparency only, not for modification.
20
- - Unauthorized use, reproduction, or distribution is strictly prohibited.
21
-
22
- WARRANTY DISCLAIMER:
23
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26
-
27
- LIMITATION OF LIABILITY:
28
- IN NO EVENT SHALL GENXIS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
- LIABILITY ARISING FROM THE USE OF THE SOFTWARE.
30
-
31
- Contact: https://genxis.one
@@ -1,461 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 50c-context - Thin client for CAZ context compression
4
- *
5
- * LOCAL (FREE): Basic indexing, hash lookup, line retrieval
6
- * API (PRO): CAZ dedup, SimHash, BM25 ranking, fog detection
7
- *
8
- * User sees this. Secret sauce stays on server.
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
- const crypto = require('crypto');
14
- const os = require('os');
15
-
16
- const INDEX_DIR = path.join(os.homedir(), '.50c', 'context');
17
- const API_BASE = process.env.FIFTYC_API_URL || 'https://api.50c.ai';
18
-
19
- if (!fs.existsSync(INDEX_DIR)) {
20
- fs.mkdirSync(INDEX_DIR, { recursive: true });
21
- }
22
-
23
- function hash(content) {
24
- return crypto.createHash('blake2b512').update(content).digest('hex').slice(0, 32);
25
- }
26
-
27
- function hashPath(filepath) {
28
- return crypto.createHash('md5').update(filepath).digest('hex').slice(0, 12);
29
- }
30
-
31
- function getIndexPath(filepath) {
32
- return path.join(INDEX_DIR, `${hashPath(filepath)}.json`);
33
- }
34
-
35
- function simpleChunk(content, targetSize = 1024) {
36
- const chunks = [];
37
- let start = 0;
38
- while (start < content.length) {
39
- let end = Math.min(start + targetSize, content.length);
40
- if (end < content.length) {
41
- const newline = content.lastIndexOf('\n', end);
42
- if (newline > start + targetSize / 2) end = newline + 1;
43
- }
44
- chunks.push({
45
- content: content.slice(start, end),
46
- start,
47
- end,
48
- hash: hash(content.slice(start, end))
49
- });
50
- start = end;
51
- }
52
- return chunks;
53
- }
54
-
55
- function extractSymbols(lines, ext) {
56
- const symbols = [];
57
- const isPython = ['.py'].includes(ext);
58
- const isJS = ['.js', '.ts', '.jsx', '.tsx', '.mjs'].includes(ext);
59
-
60
- lines.forEach((line, i) => {
61
- const trimmed = line.trim();
62
- const lineNum = i + 1;
63
-
64
- if (isPython) {
65
- if (trimmed.startsWith('def ')) {
66
- const name = trimmed.slice(4).split('(')[0].trim();
67
- symbols.push({ name, type: 'function', line: lineNum });
68
- }
69
- else if (trimmed.startsWith('async def ')) {
70
- const name = trimmed.slice(10).split('(')[0].trim();
71
- symbols.push({ name, type: 'async_function', line: lineNum });
72
- }
73
- else if (trimmed.startsWith('class ')) {
74
- const name = trimmed.slice(6).split('(')[0].split(':')[0].trim();
75
- symbols.push({ name, type: 'class', line: lineNum });
76
- }
77
- }
78
-
79
- if (isJS) {
80
- if (trimmed.match(/^(export\s+)?(async\s+)?function\s+\w+/)) {
81
- const match = trimmed.match(/function\s+(\w+)/);
82
- if (match) symbols.push({ name: match[1], type: 'function', line: lineNum });
83
- }
84
- else if (trimmed.match(/^(export\s+)?class\s+\w+/)) {
85
- const match = trimmed.match(/class\s+(\w+)/);
86
- if (match) symbols.push({ name: match[1], type: 'class', line: lineNum });
87
- }
88
- else if (trimmed.match(/^(export\s+)?(const|let|var)\s+\w+\s*=\s*(async\s+)?(\(|function)/)) {
89
- const match = trimmed.match(/(const|let|var)\s+(\w+)/);
90
- if (match) symbols.push({ name: match[2], type: 'arrow_fn', line: lineNum });
91
- }
92
- }
93
- });
94
-
95
- return symbols;
96
- }
97
-
98
- function indexFile(filepath) {
99
- filepath = path.resolve(filepath);
100
- if (!fs.existsSync(filepath)) return { error: `File not found: ${filepath}` };
101
-
102
- const content = fs.readFileSync(filepath, 'utf8');
103
- const lines = content.split('\n');
104
- const ext = path.extname(filepath).toLowerCase();
105
- const symbols = extractSymbols(lines, ext);
106
- const chunks = simpleChunk(content);
107
- const fileHash = hash(content);
108
-
109
- const indexPath = getIndexPath(filepath);
110
- if (fs.existsSync(indexPath)) {
111
- try {
112
- const existing = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
113
- if (existing.hash === fileHash) {
114
- return { status: 'current', filepath, lines: lines.length };
115
- }
116
- } catch (e) {}
117
- }
118
-
119
- const blockHashes = {};
120
- chunks.forEach(c => {
121
- if (!blockHashes[c.hash]) blockHashes[c.hash] = [];
122
- blockHashes[c.hash].push({ start: c.start, end: c.end });
123
- });
124
-
125
- const index = {
126
- filepath,
127
- filename: path.basename(filepath),
128
- hash: fileHash,
129
- totalLines: lines.length,
130
- indexed: new Date().toISOString(),
131
- symbols,
132
- chunks: chunks.map(c => ({ hash: c.hash, start: c.start, end: c.end })),
133
- uniqueBlocks: Object.keys(blockHashes).length,
134
- totalBlocks: chunks.length,
135
- dedupRatio: chunks.length / Object.keys(blockHashes).length
136
- };
137
-
138
- fs.writeFileSync(indexPath, JSON.stringify(index, null, 2));
139
-
140
- return {
141
- status: 'indexed',
142
- filepath,
143
- lines: lines.length,
144
- symbols: symbols.length,
145
- uniqueBlocks: index.uniqueBlocks,
146
- totalBlocks: index.totalBlocks,
147
- dedupRatio: index.dedupRatio.toFixed(2)
148
- };
149
- }
150
-
151
- function findSymbol(name, filepath = null) {
152
- const indexFiles = filepath
153
- ? [getIndexPath(path.resolve(filepath))]
154
- : fs.readdirSync(INDEX_DIR).filter(f => f.endsWith('.json')).map(f => path.join(INDEX_DIR, f));
155
-
156
- const results = [];
157
- const nameLower = name.toLowerCase();
158
-
159
- for (const indexFile of indexFiles) {
160
- if (!fs.existsSync(indexFile)) continue;
161
- try {
162
- const index = JSON.parse(fs.readFileSync(indexFile, 'utf8'));
163
- const matches = index.symbols.filter(s =>
164
- s.name.toLowerCase().includes(nameLower)
165
- );
166
- matches.forEach(m => results.push({
167
- name: m.name, type: m.type, line: m.line,
168
- file: index.filename, filepath: index.filepath
169
- }));
170
- } catch (e) {}
171
- }
172
-
173
- return results.sort((a, b) => {
174
- const aExact = a.name.toLowerCase() === nameLower ? 0 : 1;
175
- const bExact = b.name.toLowerCase() === nameLower ? 0 : 1;
176
- return aExact - bExact || a.line - b.line;
177
- });
178
- }
179
-
180
- function getLines(filepath, start, end) {
181
- filepath = path.resolve(filepath);
182
- if (!fs.existsSync(filepath)) return { error: `File not found: ${filepath}` };
183
-
184
- const lines = fs.readFileSync(filepath, 'utf8').split('\n');
185
- start = Math.max(1, start);
186
- end = Math.min(lines.length, end);
187
-
188
- const result = [];
189
- for (let i = start - 1; i < end; i++) {
190
- result.push(`${(i + 1).toString().padStart(5)}| ${lines[i]}`);
191
- }
192
- return result.join('\n');
193
- }
194
-
195
- function getBlocksByQuery(filepath, query) {
196
- filepath = path.resolve(filepath);
197
- const indexPath = getIndexPath(filepath);
198
-
199
- if (!fs.existsSync(indexPath)) indexFile(filepath);
200
- if (!fs.existsSync(indexPath)) return { error: `Could not index: ${filepath}` };
201
-
202
- const index = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
203
- const content = fs.readFileSync(filepath, 'utf8');
204
- const queryLower = query.toLowerCase();
205
-
206
- const relevantSymbols = index.symbols.filter(s =>
207
- s.name.toLowerCase().includes(queryLower)
208
- );
209
-
210
- const relevantChunks = [];
211
- const lines = content.split('\n');
212
-
213
- for (const sym of relevantSymbols) {
214
- const startLine = sym.line;
215
- let endLine = startLine + 50;
216
- for (let i = startLine; i < Math.min(startLine + 100, lines.length); i++) {
217
- const line = lines[i];
218
- if (line && !line.startsWith(' ') && !line.startsWith('\t') && line.trim() && i > startLine) {
219
- endLine = i;
220
- break;
221
- }
222
- }
223
- relevantChunks.push({
224
- symbol: sym.name,
225
- startLine,
226
- endLine,
227
- content: lines.slice(startLine - 1, endLine).join('\n')
228
- });
229
- }
230
-
231
- return {
232
- query,
233
- file: index.filename,
234
- matchedSymbols: relevantSymbols.length,
235
- chunks: relevantChunks.slice(0, 10),
236
- totalLines: index.totalLines,
237
- sentLines: relevantChunks.reduce((sum, c) => sum + (c.endLine - c.startLine), 0),
238
- reduction: ((1 - relevantChunks.reduce((sum, c) => sum + (c.endLine - c.startLine), 0) / index.totalLines) * 100).toFixed(1) + '%'
239
- };
240
- }
241
-
242
- async function apiCall(endpoint, data, apiKey) {
243
- const key = apiKey || process.env.FIFTYC_API_KEY;
244
- if (!key) return { error: 'No API key. Set FIFTYC_API_KEY' };
245
-
246
- try {
247
- const fetch = globalThis.fetch || require('node-fetch');
248
- const res = await fetch(`${API_BASE}${endpoint}`, {
249
- method: 'POST',
250
- headers: {
251
- 'Content-Type': 'application/json',
252
- 'X-API-Key': key
253
- },
254
- body: JSON.stringify(data)
255
- });
256
- return await res.json();
257
- } catch (e) {
258
- return { error: e.message };
259
- }
260
- }
261
-
262
- async function cazDedup(filepath, apiKey) {
263
- filepath = path.resolve(filepath);
264
- if (!fs.existsSync(filepath)) return { error: `File not found: ${filepath}` };
265
-
266
- const content = fs.readFileSync(filepath, 'utf8');
267
- const localIndex = indexFile(filepath);
268
-
269
- return await apiCall('/tools/caz_dedup', {
270
- filepath: path.basename(filepath),
271
- content,
272
- localStats: localIndex
273
- }, apiKey);
274
- }
275
-
276
- async function contextCompress(filepath, query, apiKey) {
277
- filepath = path.resolve(filepath);
278
- if (!fs.existsSync(filepath)) return { error: `File not found: ${filepath}` };
279
-
280
- const content = fs.readFileSync(filepath, 'utf8');
281
- const localBlocks = getBlocksByQuery(filepath, query);
282
-
283
- return await apiCall('/tools/context_compress', {
284
- filepath: path.basename(filepath),
285
- query,
286
- localBlocks,
287
- content
288
- }, apiKey);
289
- }
290
-
291
- async function fogCheck(messages, apiKey) {
292
- return await apiCall('/tools/fog_check', { messages }, apiKey);
293
- }
294
-
295
- async function fogClear(messages, mode, apiKey) {
296
- return await apiCall('/tools/fog_clear', { messages, mode }, apiKey);
297
- }
298
-
299
- function listIndexed() {
300
- const files = fs.readdirSync(INDEX_DIR).filter(f => f.endsWith('.json'));
301
- const results = [];
302
-
303
- for (const file of files) {
304
- try {
305
- const index = JSON.parse(fs.readFileSync(path.join(INDEX_DIR, file), 'utf8'));
306
- results.push({
307
- filename: index.filename,
308
- filepath: index.filepath,
309
- lines: index.totalLines,
310
- symbols: index.symbols.length,
311
- dedupRatio: index.dedupRatio?.toFixed(2) || 'N/A'
312
- });
313
- } catch (e) {}
314
- }
315
-
316
- return results.sort((a, b) => b.lines - a.lines);
317
- }
318
-
319
- function stats() {
320
- const files = listIndexed();
321
- const totalLines = files.reduce((sum, f) => sum + f.lines, 0);
322
- const totalSymbols = files.reduce((sum, f) => sum + f.symbols, 0);
323
-
324
- return {
325
- indexedFiles: files.length,
326
- totalLines,
327
- totalSymbols,
328
- indexDir: INDEX_DIR
329
- };
330
- }
331
-
332
- const CONTEXT_TOOLS = {
333
- ctx_index: {
334
- description: "Index file for CAZ context compression. FREE.",
335
- handler: (args) => indexFile(args.filepath)
336
- },
337
- ctx_find: {
338
- description: "Find symbol across indexed files. FREE.",
339
- handler: (args) => findSymbol(args.name, args.filepath)
340
- },
341
- ctx_lines: {
342
- description: "Get specific line range. FREE.",
343
- handler: (args) => getLines(args.filepath, args.start, args.end)
344
- },
345
- ctx_blocks: {
346
- description: "Get relevant blocks for query. FREE.",
347
- handler: (args) => getBlocksByQuery(args.filepath, args.query)
348
- },
349
- ctx_list: {
350
- description: "List indexed files. FREE.",
351
- handler: () => listIndexed()
352
- },
353
- ctx_stats: {
354
- description: "Get indexing stats. FREE.",
355
- handler: () => stats()
356
- },
357
- caz_dedup: {
358
- description: "Full CAZ deduplication. PRO $0.02.",
359
- handler: async (args) => await cazDedup(args.filepath, args.apiKey)
360
- },
361
- context_compress: {
362
- description: "Compress context for query. PRO $0.03.",
363
- handler: async (args) => await contextCompress(args.filepath, args.query, args.apiKey)
364
- },
365
- fog_check: {
366
- description: "Check fog level. FREE.",
367
- handler: async (args) => await fogCheck(args.messages, args.apiKey)
368
- },
369
- fog_clear: {
370
- description: "Clear context fog. PRO $0.03.",
371
- handler: async (args) => await fogClear(args.messages, args.mode, args.apiKey)
372
- }
373
- };
374
-
375
- module.exports = {
376
- CONTEXT_TOOLS,
377
- indexFile,
378
- findSymbol,
379
- getLines,
380
- getBlocksByQuery,
381
- listIndexed,
382
- stats,
383
- cazDedup,
384
- contextCompress,
385
- fogCheck,
386
- fogClear
387
- };
388
-
389
- if (require.main === module) {
390
- const args = process.argv.slice(2);
391
- const cmd = args[0];
392
-
393
- if (!cmd || cmd === 'help') {
394
- console.log(`
395
- 50c-context - CAZ Context Compression
396
-
397
- LOCAL (FREE):
398
- ctx_index <file> Index file for dedup
399
- ctx_find <name> [file] Find symbol
400
- ctx_lines <file> <s> <e> Get line range
401
- ctx_blocks <file> <query> Get relevant blocks
402
- ctx_list List indexed files
403
- ctx_stats Show stats
404
-
405
- API (PRO):
406
- caz_dedup <file> Full CAZ deduplication
407
- context_compress <f> <q> Compress for query
408
- fog_check Check fog level
409
- fog_clear Clear context fog
410
-
411
- Test:
412
- node caz-context.js test
413
- `);
414
- return;
415
- }
416
-
417
- if (cmd === 'test') {
418
- const testFile = 'C:\\Users\\Administrator\\Desktop\\50c\\nj-deploy\\api-unified-v8-FINAL-ALL.py';
419
-
420
- console.log('='.repeat(60));
421
- console.log('50c-context TEST');
422
- console.log('='.repeat(60));
423
-
424
- console.log('\n[1] Indexing...');
425
- const idx = indexFile(testFile);
426
- console.log(idx);
427
-
428
- console.log('\n[2] Find fog_check...');
429
- console.log(findSymbol('fog_check'));
430
-
431
- console.log('\n[3] Get blocks for "fog"...');
432
- const blocks = getBlocksByQuery(testFile, 'fog');
433
- console.log(`Matched: ${blocks.matchedSymbols} symbols`);
434
- console.log(`Sent: ${blocks.sentLines} lines (${blocks.reduction} reduction)`);
435
- console.log(`First chunk: ${blocks.chunks[0]?.symbol} at line ${blocks.chunks[0]?.startLine}`);
436
-
437
- console.log('\n[4] Stats...');
438
- console.log(stats());
439
-
440
- console.log('\n[5] List indexed...');
441
- console.log(listIndexed());
442
- }
443
- else if (cmd === 'index') {
444
- console.log(indexFile(args[1]));
445
- }
446
- else if (cmd === 'find') {
447
- console.log(JSON.stringify(findSymbol(args[1], args[2]), null, 2));
448
- }
449
- else if (cmd === 'lines') {
450
- console.log(getLines(args[1], parseInt(args[2]), parseInt(args[3])));
451
- }
452
- else if (cmd === 'blocks') {
453
- console.log(JSON.stringify(getBlocksByQuery(args[1], args[2]), null, 2));
454
- }
455
- else if (cmd === 'list') {
456
- console.log(JSON.stringify(listIndexed(), null, 2));
457
- }
458
- else if (cmd === 'stats') {
459
- console.log(JSON.stringify(stats(), null, 2));
460
- }
461
- }