vibehacker 4.2.0 → 4.2.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,6 +1,6 @@
1
1
  {
2
2
  "name": "vibehacker",
3
- "version": "4.2.0",
3
+ "version": "4.2.1",
4
4
  "description": "Vibe Hacker — Terminal AI cybersecurity assistant. Free models, autonomous agent, multi-provider rotation.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/src/app.js CHANGED
@@ -1711,24 +1711,69 @@ class HackerCLIApp {
1711
1711
  this._menuOpen = true;
1712
1712
  const sc = this.screen;
1713
1713
 
1714
- const prompt = blessed.prompt({
1714
+ const W = Math.min(64, sc.width - 4);
1715
+
1716
+ const box = blessed.box({
1715
1717
  parent: sc,
1716
1718
  top: 'center', left: 'center',
1717
- width: 56, height: 8,
1719
+ width: W, height: 7,
1718
1720
  tags: true,
1719
1721
  border: { type: 'line' },
1720
1722
  label: ` {#00ff88-fg}{bold}Input{/bold}{/#00ff88-fg} `,
1721
1723
  style: { fg: '#00ff88', bg: '#000a00', border: { fg: '#00aa44' } },
1724
+ padding: { left: 1, right: 1 },
1725
+ });
1726
+
1727
+ // Label text
1728
+ blessed.text({
1729
+ parent: box,
1730
+ top: 0, left: 0, right: 0, height: 1,
1731
+ tags: true,
1732
+ content: ` {#888888-fg}${label}{/#888888-fg}`,
1733
+ style: { bg: '#000a00' },
1722
1734
  });
1723
1735
 
1724
- prompt.input(label, '', (err, value) => {
1736
+ // Textarea input immune to right-click cancel bug
1737
+ const input = blessed.textarea({
1738
+ parent: box,
1739
+ top: 2, left: 0, right: 0, height: 3,
1740
+ border: { type: 'line' },
1741
+ style: {
1742
+ fg: '#00ff88', bg: '#001100',
1743
+ border: { fg: '#003300' },
1744
+ focus: { border: { fg: '#00aa44' } },
1745
+ },
1746
+ inputOnFocus: true,
1747
+ mouse: true,
1748
+ keys: true,
1749
+ });
1750
+
1751
+ let done = false;
1752
+ const finish = (value) => {
1753
+ if (done) return;
1754
+ done = true;
1725
1755
  this._menuOpen = false;
1726
- prompt.destroy();
1756
+ try { box.destroy(); } catch (_) {}
1727
1757
  this.inputBox.focus();
1728
1758
  sc.render();
1729
- if (!err && value && value.trim()) callback(value.trim());
1759
+ if (value && value.trim()) callback(value.trim());
1760
+ };
1761
+
1762
+ // Enter submits, Escape cancels
1763
+ input.key(['enter', 'return'], () => {
1764
+ finish(input.getValue().replace(/\n/g, '').trim());
1765
+ });
1766
+
1767
+ input.key(['escape'], () => {
1768
+ finish(null);
1769
+ });
1770
+
1771
+ // Block newlines
1772
+ input.on('keypress', (ch, key) => {
1773
+ if (key && (key.name === 'enter' || key.name === 'return')) return false;
1730
1774
  });
1731
1775
 
1776
+ input.focus();
1732
1777
  sc.render();
1733
1778
  }
1734
1779
 
package/src/src/config.js CHANGED
@@ -36,7 +36,7 @@ const config = {
36
36
  apiKey: _saved.apiKey || '',
37
37
  baseURL: _saved.baseURL || 'https://openrouter.ai/api/v1',
38
38
  appName: 'Vibe Hacker',
39
- version: '4.2.0',
39
+ version: '4.2.1',
40
40
  noLogging: true,
41
41
  maxTokens: 8192,
42
42
  temperature: 0.6, // slightly lower for more reliable tool use
package/src/src/setup.js CHANGED
@@ -7,11 +7,14 @@ function ew(s) {
7
7
  }
8
8
 
9
9
  // ── First-run setup — simple API key paste ─────────────────────────────────
10
+ // Uses a raw textarea instead of textbox/prompt to avoid blessed's
11
+ // right-click-fires-cancel bug. Paste works with Ctrl+V, Shift+Insert,
12
+ // and terminal right-click paste on all platforms.
10
13
 
11
14
  function showSetup(screen) {
12
15
  return new Promise((resolve) => {
13
16
  const W = Math.min(screen.width - 4, 60);
14
- const H = Math.min(screen.height - 2, 22);
17
+ const H = Math.min(screen.height - 2, 20);
15
18
 
16
19
  const overlay = blessed.box({
17
20
  parent: screen,
@@ -27,68 +30,78 @@ function showSetup(screen) {
27
30
  '',
28
31
  '{center}{green-fg}{bold}◈ Vibe Hacker — Get Started{/bold}{/green-fg}{/center}',
29
32
  '',
30
- '{center}{#888888-fg}Enter your API key to begin.{/#888888-fg}{/center}',
33
+ '{center}{#888888-fg}Paste your API key below.{/#888888-fg}{/center}',
31
34
  '{center}{#888888-fg}Get a free key at vibsecurity.com{/#888888-fg}{/center}',
32
35
  '',
33
- '{#003300-fg} ════════════════════════════════════════════{/#003300-fg}',
34
- '',
35
36
  ' {#555555-fg}Supported keys:{/#555555-fg}',
36
- ' {#44ff88-fg}Vibe Hacker{/#44ff88-fg} {#666666-fg}vh_… / sk-or-v1-…{/#666666-fg}',
37
37
  ' {#44ff88-fg}Groq{/#44ff88-fg} {#666666-fg}gsk_…{/#666666-fg}',
38
38
  ' {#44ff88-fg}Gemini{/#44ff88-fg} {#666666-fg}AIza…{/#666666-fg}',
39
39
  ' {#44ff88-fg}Cerebras{/#44ff88-fg} {#666666-fg}csk-…{/#666666-fg}',
40
+ ' {#44ff88-fg}OpenRouter{/#44ff88-fg} {#666666-fg}sk-or-v1-…{/#666666-fg}',
40
41
  ' {#44ff88-fg}OpenAI{/#44ff88-fg} {#666666-fg}sk-…{/#666666-fg}',
41
42
  ' {#44ff88-fg}Anthropic{/#44ff88-fg} {#666666-fg}sk-ant-…{/#666666-fg}',
42
43
  '',
43
44
  ].join('\n'));
44
45
 
45
- const input = blessed.textbox({
46
+ // Use textarea instead of textbox — it doesn't fire cancel on right-click
47
+ const input = blessed.textarea({
46
48
  parent: overlay,
47
49
  bottom: 1, left: 2, right: 2, height: 3,
48
50
  border: { type: 'line' },
49
- label: ' {#00ff88-fg}Paste API Key{/#00ff88-fg} ',
50
- style: { fg: '#00ff88', bg: '#001100', border: { fg: '#00aa44' } },
51
- inputOnFocus: false,
51
+ label: ' {#00ff88-fg}Paste API Key (Enter to submit){/#00ff88-fg} ',
52
+ style: {
53
+ fg: '#00ff88', bg: '#001100',
54
+ border: { fg: '#00aa44' },
55
+ focus: { border: { fg: '#00ff88' } },
56
+ },
57
+ inputOnFocus: true,
52
58
  mouse: true,
59
+ keys: true,
53
60
  });
54
61
 
55
62
  const statusLine = blessed.text({
56
63
  parent: overlay,
57
64
  bottom: 0, left: 2, right: 2, height: 1,
58
65
  tags: true,
59
- content: '',
66
+ content: ' {#444444-fg}Paste key then press Enter{/#444444-fg}',
60
67
  style: { fg: '#00aa44', bg: 'black' },
61
68
  });
62
69
 
63
- overlay.focus();
64
- screen.render();
65
-
66
- setTimeout(() => {
67
- input.focus();
68
- input.readInput();
70
+ let resolved = false;
71
+ function finish(val) {
72
+ if (resolved) return;
73
+ resolved = true;
74
+ try { input.destroy(); overlay.destroy(); } catch (_) {}
69
75
  screen.render();
70
- }, 50);
76
+ resolve(val);
77
+ }
71
78
 
72
- input.on('submit', (value) => {
73
- const key = (value || '').trim();
79
+ // Handle key events manually — Enter submits, Escape cancels
80
+ input.key(['enter', 'return'], () => {
81
+ const key = (input.getValue() || '').replace(/\n/g, '').trim();
74
82
  if (!key || key.length < 10) {
75
83
  statusLine.setContent(' {red-fg}Key too short — paste a valid API key{/red-fg}');
76
84
  input.clearValue();
77
- input.focus();
78
- input.readInput();
79
85
  screen.render();
80
86
  return;
81
87
  }
82
- try { input.destroy(); overlay.destroy(); } catch (_) {}
83
- screen.render();
84
- resolve(key);
88
+ finish(key);
85
89
  });
86
90
 
87
- input.on('cancel', () => {
88
- try { input.destroy(); overlay.destroy(); } catch (_) {}
89
- screen.render();
90
- resolve(null);
91
+ input.key(['escape'], () => {
92
+ finish(null);
93
+ });
94
+
95
+ // Prevent newlines in the input (textarea allows them by default)
96
+ input.on('keypress', (ch, key) => {
97
+ if (key && (key.name === 'enter' || key.name === 'return')) {
98
+ // Already handled above — prevent default newline insertion
99
+ return false;
100
+ }
91
101
  });
102
+
103
+ input.focus();
104
+ screen.render();
92
105
  });
93
106
  }
94
107