clawculator 2.9.0 → 2.9.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/bin/clawculator.js +1 -2
- package/package.json +3 -3
- package/skills/clawculator/snapshotCard.js +3 -151
- package/src/snapshotCard.js +3 -151
package/bin/clawculator.js
CHANGED
|
@@ -172,8 +172,7 @@ async function main() {
|
|
|
172
172
|
const { generateSnapshotCard } = require('../src/snapshotCard');
|
|
173
173
|
const result = generateSnapshotCard(analysis, outDir);
|
|
174
174
|
const { exec } = require('child_process');
|
|
175
|
-
|
|
176
|
-
exec(`open "${openFile}" 2>/dev/null || xdg-open "${openFile}" 2>/dev/null`, () => {
|
|
175
|
+
exec(`open "${result.htmlPath}" 2>/dev/null || xdg-open "${result.htmlPath}" 2>/dev/null`, () => {
|
|
177
176
|
process.exit(0);
|
|
178
177
|
});
|
|
179
178
|
setTimeout(() => process.exit(0), 2000);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawculator",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "AI cost forensics for OpenClaw and multi-model setups. Your friendly penny pincher. 100% offline. Zero AI. Pure deterministic logic.",
|
|
5
5
|
"main": "src/analyzer.js",
|
|
6
6
|
"bin": {
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"engines": {
|
|
31
31
|
"node": ">=18.0.0"
|
|
32
32
|
},
|
|
33
|
+
"dependencies": {},
|
|
33
34
|
"optionalDependencies": {
|
|
34
|
-
"better-sqlite3": "^11.0.0"
|
|
35
|
-
"canvas": "^3.2.1"
|
|
35
|
+
"better-sqlite3": "^11.0.0"
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -349,149 +349,6 @@ function generateSnapshotCard(analysis, outputDir) {
|
|
|
349
349
|
const htmlPath = path.join(outputDir, 'clawculator-snapshot.html');
|
|
350
350
|
fs.writeFileSync(htmlPath, html, 'utf8');
|
|
351
351
|
|
|
352
|
-
// ── Generate PNG card ──────────────────────────────────
|
|
353
|
-
let pngPath = null;
|
|
354
|
-
try {
|
|
355
|
-
const { createCanvas } = require('canvas');
|
|
356
|
-
const W = 1080, H = 1080;
|
|
357
|
-
const canvas = createCanvas(W, H);
|
|
358
|
-
const ctx = canvas.getContext('2d');
|
|
359
|
-
|
|
360
|
-
// Background
|
|
361
|
-
ctx.fillStyle = '#05080f';
|
|
362
|
-
ctx.fillRect(0, 0, W, H);
|
|
363
|
-
|
|
364
|
-
// Grid lines
|
|
365
|
-
ctx.strokeStyle = 'rgba(34,211,238,0.03)';
|
|
366
|
-
ctx.lineWidth = 1;
|
|
367
|
-
for (let x = 0; x < W; x += 54) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke(); }
|
|
368
|
-
for (let y = 0; y < H; y += 54) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke(); }
|
|
369
|
-
|
|
370
|
-
// Border
|
|
371
|
-
ctx.strokeStyle = '#1a2744';
|
|
372
|
-
ctx.lineWidth = 2;
|
|
373
|
-
ctx.roundRect(24, 24, W - 48, H - 48, 24);
|
|
374
|
-
ctx.stroke();
|
|
375
|
-
|
|
376
|
-
// ── Header
|
|
377
|
-
ctx.font = 'bold 36px sans-serif';
|
|
378
|
-
ctx.fillStyle = '#22d3ee';
|
|
379
|
-
ctx.textAlign = 'center';
|
|
380
|
-
ctx.fillText('CLAWCULATOR', W / 2 + 20, 100);
|
|
381
|
-
|
|
382
|
-
// Lobster emoji substitute
|
|
383
|
-
ctx.font = '44px sans-serif';
|
|
384
|
-
ctx.fillText('🦞', W / 2 - 160, 102);
|
|
385
|
-
|
|
386
|
-
// ── Grade circle
|
|
387
|
-
const cx = W / 2, cy = 280, r = 100;
|
|
388
|
-
// Outer glow
|
|
389
|
-
ctx.beginPath(); ctx.arc(cx, cy, r + 8, 0, Math.PI * 2);
|
|
390
|
-
ctx.fillStyle = gradeGlow; ctx.fill();
|
|
391
|
-
// Ring
|
|
392
|
-
ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
|
393
|
-
ctx.strokeStyle = gradeColor + '66'; ctx.lineWidth = 4; ctx.stroke();
|
|
394
|
-
ctx.beginPath(); ctx.arc(cx, cy, r - 3, 0, Math.PI * 2);
|
|
395
|
-
ctx.strokeStyle = gradeColor + 'aa'; ctx.lineWidth = 2; ctx.stroke();
|
|
396
|
-
// Grade letter
|
|
397
|
-
ctx.font = 'bold 72px sans-serif';
|
|
398
|
-
ctx.fillStyle = gradeColor;
|
|
399
|
-
ctx.textAlign = 'center';
|
|
400
|
-
ctx.textBaseline = 'middle';
|
|
401
|
-
ctx.fillText(grade, cx, cy);
|
|
402
|
-
ctx.textBaseline = 'alphabetic';
|
|
403
|
-
// Label
|
|
404
|
-
ctx.font = '16px sans-serif';
|
|
405
|
-
ctx.fillStyle = '#64748b';
|
|
406
|
-
ctx.fillText('COST HEALTH GRADE', cx, cy + r + 30);
|
|
407
|
-
|
|
408
|
-
// ── Cost range
|
|
409
|
-
ctx.font = 'bold 36px sans-serif';
|
|
410
|
-
ctx.fillStyle = '#e2e8f0';
|
|
411
|
-
ctx.fillText(costEmoji + ' ' + costRange, cx, 460);
|
|
412
|
-
|
|
413
|
-
// ── Divider
|
|
414
|
-
ctx.beginPath();
|
|
415
|
-
ctx.moveTo(W / 2 - 60, 500);
|
|
416
|
-
ctx.lineTo(W / 2 + 60, 500);
|
|
417
|
-
ctx.strokeStyle = '#1e3a5f'; ctx.lineWidth = 1; ctx.stroke();
|
|
418
|
-
|
|
419
|
-
// ── Pills
|
|
420
|
-
ctx.font = '20px sans-serif';
|
|
421
|
-
ctx.fillStyle = '#94a3b8';
|
|
422
|
-
const pillLines = [];
|
|
423
|
-
let currentLine = '';
|
|
424
|
-
for (const p of pills) {
|
|
425
|
-
const item = p.icon + ' ' + p.label;
|
|
426
|
-
const test = currentLine ? currentLine + ' ' + item : item;
|
|
427
|
-
if (test.length > 45 && currentLine) {
|
|
428
|
-
pillLines.push(currentLine);
|
|
429
|
-
currentLine = item;
|
|
430
|
-
} else {
|
|
431
|
-
currentLine = test;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
if (currentLine) pillLines.push(currentLine);
|
|
435
|
-
|
|
436
|
-
let pillY = 545;
|
|
437
|
-
for (const line of pillLines) {
|
|
438
|
-
ctx.fillText(line, cx, pillY);
|
|
439
|
-
pillY += 32;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// ── Findings
|
|
443
|
-
const findY = pillY + 30;
|
|
444
|
-
if (findingSummary.length > 0) {
|
|
445
|
-
ctx.font = 'bold 22px sans-serif';
|
|
446
|
-
findingSummary.forEach((f, i) => {
|
|
447
|
-
const c = f.startsWith('🔴') ? '#ef4444' : f.startsWith('🟠') ? '#f97316' : '#eab308';
|
|
448
|
-
ctx.fillStyle = c;
|
|
449
|
-
ctx.fillText(f, cx, findY + i * 32);
|
|
450
|
-
});
|
|
451
|
-
} else {
|
|
452
|
-
ctx.font = 'bold 22px sans-serif';
|
|
453
|
-
ctx.fillStyle = '#22c55e';
|
|
454
|
-
ctx.fillText('✅ No issues found', cx, findY);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// ── Divider 2
|
|
458
|
-
const divY2 = findY + (findingSummary.length > 0 ? findingSummary.length * 32 + 20 : 50);
|
|
459
|
-
ctx.beginPath();
|
|
460
|
-
ctx.moveTo(80, divY2);
|
|
461
|
-
ctx.lineTo(W - 80, divY2);
|
|
462
|
-
ctx.strokeStyle = '#1a2744'; ctx.lineWidth = 1; ctx.stroke();
|
|
463
|
-
|
|
464
|
-
// ── CTA
|
|
465
|
-
ctx.font = '18px sans-serif';
|
|
466
|
-
ctx.fillStyle = '#64748b';
|
|
467
|
-
ctx.fillText('Get your OpenClaw cost grade', cx, divY2 + 40);
|
|
468
|
-
|
|
469
|
-
// Command box
|
|
470
|
-
const cmdY = divY2 + 70;
|
|
471
|
-
const cmdW = 380, cmdH = 44;
|
|
472
|
-
ctx.fillStyle = '#0b1120';
|
|
473
|
-
ctx.strokeStyle = '#1a2744';
|
|
474
|
-
ctx.lineWidth = 1;
|
|
475
|
-
ctx.roundRect(cx - cmdW / 2, cmdY - cmdH / 2, cmdW, cmdH, 8);
|
|
476
|
-
ctx.fill(); ctx.stroke();
|
|
477
|
-
|
|
478
|
-
ctx.font = 'bold 20px monospace';
|
|
479
|
-
ctx.fillStyle = '#22d3ee';
|
|
480
|
-
ctx.fillText('npx clawculator --snapshot', cx, cmdY + 6);
|
|
481
|
-
|
|
482
|
-
// Subtitle
|
|
483
|
-
ctx.font = '13px sans-serif';
|
|
484
|
-
ctx.fillStyle = '#334155';
|
|
485
|
-
ctx.fillText('100% offline · your data never leaves your machine', cx, cmdY + 38);
|
|
486
|
-
|
|
487
|
-
// Write PNG
|
|
488
|
-
pngPath = path.join(outputDir, 'clawculator-snapshot.png');
|
|
489
|
-
fs.writeFileSync(pngPath, canvas.toBuffer('image/png'));
|
|
490
|
-
} catch (e) {
|
|
491
|
-
// canvas not installed — PNG generation skipped, HTML still works
|
|
492
|
-
pngPath = null;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
352
|
// ── Terminal card (screenshot-ready) ───────────────────
|
|
496
353
|
const C = '\x1b[36m'; // cyan
|
|
497
354
|
const G = gradeColor === '#22c55e' ? '\x1b[32m' : gradeColor === '#f59e0b' ? '\x1b[33m' : gradeColor === '#f97316' ? '\x1b[33m' : '\x1b[31m';
|
|
@@ -535,15 +392,10 @@ function generateSnapshotCard(analysis, outputDir) {
|
|
|
535
392
|
|
|
536
393
|
console.log(lines.join('\n'));
|
|
537
394
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
console.log(` \x1b[90mDrag into Twitter, Discord, or any social feed!\x1b[0m\n`);
|
|
541
|
-
} else {
|
|
542
|
-
console.log(` \x1b[90mHTML card saved: ${htmlPath}\x1b[0m`);
|
|
543
|
-
console.log(` \x1b[90mTip: npm install canvas — to auto-generate a PNG for sharing\x1b[0m\n`);
|
|
544
|
-
}
|
|
395
|
+
console.log(` \x1b[32m✓\x1b[0m Card saved: ${htmlPath}`);
|
|
396
|
+
console.log(` \x1b[90mOpen it → screenshot → drag into any tweet or post\x1b[0m\n`);
|
|
545
397
|
|
|
546
|
-
return { htmlPath,
|
|
398
|
+
return { htmlPath, grade, costRange };
|
|
547
399
|
}
|
|
548
400
|
|
|
549
401
|
module.exports = { generateSnapshotCard };
|
package/src/snapshotCard.js
CHANGED
|
@@ -349,149 +349,6 @@ function generateSnapshotCard(analysis, outputDir) {
|
|
|
349
349
|
const htmlPath = path.join(outputDir, 'clawculator-snapshot.html');
|
|
350
350
|
fs.writeFileSync(htmlPath, html, 'utf8');
|
|
351
351
|
|
|
352
|
-
// ── Generate PNG card ──────────────────────────────────
|
|
353
|
-
let pngPath = null;
|
|
354
|
-
try {
|
|
355
|
-
const { createCanvas } = require('canvas');
|
|
356
|
-
const W = 1080, H = 1080;
|
|
357
|
-
const canvas = createCanvas(W, H);
|
|
358
|
-
const ctx = canvas.getContext('2d');
|
|
359
|
-
|
|
360
|
-
// Background
|
|
361
|
-
ctx.fillStyle = '#05080f';
|
|
362
|
-
ctx.fillRect(0, 0, W, H);
|
|
363
|
-
|
|
364
|
-
// Grid lines
|
|
365
|
-
ctx.strokeStyle = 'rgba(34,211,238,0.03)';
|
|
366
|
-
ctx.lineWidth = 1;
|
|
367
|
-
for (let x = 0; x < W; x += 54) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke(); }
|
|
368
|
-
for (let y = 0; y < H; y += 54) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke(); }
|
|
369
|
-
|
|
370
|
-
// Border
|
|
371
|
-
ctx.strokeStyle = '#1a2744';
|
|
372
|
-
ctx.lineWidth = 2;
|
|
373
|
-
ctx.roundRect(24, 24, W - 48, H - 48, 24);
|
|
374
|
-
ctx.stroke();
|
|
375
|
-
|
|
376
|
-
// ── Header
|
|
377
|
-
ctx.font = 'bold 36px sans-serif';
|
|
378
|
-
ctx.fillStyle = '#22d3ee';
|
|
379
|
-
ctx.textAlign = 'center';
|
|
380
|
-
ctx.fillText('CLAWCULATOR', W / 2 + 20, 100);
|
|
381
|
-
|
|
382
|
-
// Lobster emoji substitute
|
|
383
|
-
ctx.font = '44px sans-serif';
|
|
384
|
-
ctx.fillText('🦞', W / 2 - 160, 102);
|
|
385
|
-
|
|
386
|
-
// ── Grade circle
|
|
387
|
-
const cx = W / 2, cy = 280, r = 100;
|
|
388
|
-
// Outer glow
|
|
389
|
-
ctx.beginPath(); ctx.arc(cx, cy, r + 8, 0, Math.PI * 2);
|
|
390
|
-
ctx.fillStyle = gradeGlow; ctx.fill();
|
|
391
|
-
// Ring
|
|
392
|
-
ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
|
393
|
-
ctx.strokeStyle = gradeColor + '66'; ctx.lineWidth = 4; ctx.stroke();
|
|
394
|
-
ctx.beginPath(); ctx.arc(cx, cy, r - 3, 0, Math.PI * 2);
|
|
395
|
-
ctx.strokeStyle = gradeColor + 'aa'; ctx.lineWidth = 2; ctx.stroke();
|
|
396
|
-
// Grade letter
|
|
397
|
-
ctx.font = 'bold 72px sans-serif';
|
|
398
|
-
ctx.fillStyle = gradeColor;
|
|
399
|
-
ctx.textAlign = 'center';
|
|
400
|
-
ctx.textBaseline = 'middle';
|
|
401
|
-
ctx.fillText(grade, cx, cy);
|
|
402
|
-
ctx.textBaseline = 'alphabetic';
|
|
403
|
-
// Label
|
|
404
|
-
ctx.font = '16px sans-serif';
|
|
405
|
-
ctx.fillStyle = '#64748b';
|
|
406
|
-
ctx.fillText('COST HEALTH GRADE', cx, cy + r + 30);
|
|
407
|
-
|
|
408
|
-
// ── Cost range
|
|
409
|
-
ctx.font = 'bold 36px sans-serif';
|
|
410
|
-
ctx.fillStyle = '#e2e8f0';
|
|
411
|
-
ctx.fillText(costEmoji + ' ' + costRange, cx, 460);
|
|
412
|
-
|
|
413
|
-
// ── Divider
|
|
414
|
-
ctx.beginPath();
|
|
415
|
-
ctx.moveTo(W / 2 - 60, 500);
|
|
416
|
-
ctx.lineTo(W / 2 + 60, 500);
|
|
417
|
-
ctx.strokeStyle = '#1e3a5f'; ctx.lineWidth = 1; ctx.stroke();
|
|
418
|
-
|
|
419
|
-
// ── Pills
|
|
420
|
-
ctx.font = '20px sans-serif';
|
|
421
|
-
ctx.fillStyle = '#94a3b8';
|
|
422
|
-
const pillLines = [];
|
|
423
|
-
let currentLine = '';
|
|
424
|
-
for (const p of pills) {
|
|
425
|
-
const item = p.icon + ' ' + p.label;
|
|
426
|
-
const test = currentLine ? currentLine + ' ' + item : item;
|
|
427
|
-
if (test.length > 45 && currentLine) {
|
|
428
|
-
pillLines.push(currentLine);
|
|
429
|
-
currentLine = item;
|
|
430
|
-
} else {
|
|
431
|
-
currentLine = test;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
if (currentLine) pillLines.push(currentLine);
|
|
435
|
-
|
|
436
|
-
let pillY = 545;
|
|
437
|
-
for (const line of pillLines) {
|
|
438
|
-
ctx.fillText(line, cx, pillY);
|
|
439
|
-
pillY += 32;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// ── Findings
|
|
443
|
-
const findY = pillY + 30;
|
|
444
|
-
if (findingSummary.length > 0) {
|
|
445
|
-
ctx.font = 'bold 22px sans-serif';
|
|
446
|
-
findingSummary.forEach((f, i) => {
|
|
447
|
-
const c = f.startsWith('🔴') ? '#ef4444' : f.startsWith('🟠') ? '#f97316' : '#eab308';
|
|
448
|
-
ctx.fillStyle = c;
|
|
449
|
-
ctx.fillText(f, cx, findY + i * 32);
|
|
450
|
-
});
|
|
451
|
-
} else {
|
|
452
|
-
ctx.font = 'bold 22px sans-serif';
|
|
453
|
-
ctx.fillStyle = '#22c55e';
|
|
454
|
-
ctx.fillText('✅ No issues found', cx, findY);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// ── Divider 2
|
|
458
|
-
const divY2 = findY + (findingSummary.length > 0 ? findingSummary.length * 32 + 20 : 50);
|
|
459
|
-
ctx.beginPath();
|
|
460
|
-
ctx.moveTo(80, divY2);
|
|
461
|
-
ctx.lineTo(W - 80, divY2);
|
|
462
|
-
ctx.strokeStyle = '#1a2744'; ctx.lineWidth = 1; ctx.stroke();
|
|
463
|
-
|
|
464
|
-
// ── CTA
|
|
465
|
-
ctx.font = '18px sans-serif';
|
|
466
|
-
ctx.fillStyle = '#64748b';
|
|
467
|
-
ctx.fillText('Get your OpenClaw cost grade', cx, divY2 + 40);
|
|
468
|
-
|
|
469
|
-
// Command box
|
|
470
|
-
const cmdY = divY2 + 70;
|
|
471
|
-
const cmdW = 380, cmdH = 44;
|
|
472
|
-
ctx.fillStyle = '#0b1120';
|
|
473
|
-
ctx.strokeStyle = '#1a2744';
|
|
474
|
-
ctx.lineWidth = 1;
|
|
475
|
-
ctx.roundRect(cx - cmdW / 2, cmdY - cmdH / 2, cmdW, cmdH, 8);
|
|
476
|
-
ctx.fill(); ctx.stroke();
|
|
477
|
-
|
|
478
|
-
ctx.font = 'bold 20px monospace';
|
|
479
|
-
ctx.fillStyle = '#22d3ee';
|
|
480
|
-
ctx.fillText('npx clawculator --snapshot', cx, cmdY + 6);
|
|
481
|
-
|
|
482
|
-
// Subtitle
|
|
483
|
-
ctx.font = '13px sans-serif';
|
|
484
|
-
ctx.fillStyle = '#334155';
|
|
485
|
-
ctx.fillText('100% offline · your data never leaves your machine', cx, cmdY + 38);
|
|
486
|
-
|
|
487
|
-
// Write PNG
|
|
488
|
-
pngPath = path.join(outputDir, 'clawculator-snapshot.png');
|
|
489
|
-
fs.writeFileSync(pngPath, canvas.toBuffer('image/png'));
|
|
490
|
-
} catch (e) {
|
|
491
|
-
// canvas not installed — PNG generation skipped, HTML still works
|
|
492
|
-
pngPath = null;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
352
|
// ── Terminal card (screenshot-ready) ───────────────────
|
|
496
353
|
const C = '\x1b[36m'; // cyan
|
|
497
354
|
const G = gradeColor === '#22c55e' ? '\x1b[32m' : gradeColor === '#f59e0b' ? '\x1b[33m' : gradeColor === '#f97316' ? '\x1b[33m' : '\x1b[31m';
|
|
@@ -535,15 +392,10 @@ function generateSnapshotCard(analysis, outputDir) {
|
|
|
535
392
|
|
|
536
393
|
console.log(lines.join('\n'));
|
|
537
394
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
console.log(` \x1b[90mDrag into Twitter, Discord, or any social feed!\x1b[0m\n`);
|
|
541
|
-
} else {
|
|
542
|
-
console.log(` \x1b[90mHTML card saved: ${htmlPath}\x1b[0m`);
|
|
543
|
-
console.log(` \x1b[90mTip: npm install canvas — to auto-generate a PNG for sharing\x1b[0m\n`);
|
|
544
|
-
}
|
|
395
|
+
console.log(` \x1b[32m✓\x1b[0m Card saved: ${htmlPath}`);
|
|
396
|
+
console.log(` \x1b[90mOpen it → screenshot → drag into any tweet or post\x1b[0m\n`);
|
|
545
397
|
|
|
546
|
-
return { htmlPath,
|
|
398
|
+
return { htmlPath, grade, costRange };
|
|
547
399
|
}
|
|
548
400
|
|
|
549
401
|
module.exports = { generateSnapshotCard };
|