vexi-cli 0.9.0 → 0.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.
@@ -38,11 +38,19 @@ export const PROVIDER_PATTERNS = [
38
38
  // they always fall back to the interactive provider-selection list.
39
39
  ];
40
40
  /**
41
- * Sanitize a pasted API key: trim whitespace/newlines and strip
42
- * surrounding quotes (users often paste keys with extra characters).
41
+ * Sanitize a pasted API key: strip characters that can't be sent in an HTTP
42
+ * header, trim whitespace/newlines, and strip surrounding quotes.
43
+ *
44
+ * API keys are always printable ASCII, but pasting one — especially on an
45
+ * RTL/multilingual system — can silently pull in invisible bidi marks
46
+ * (U+200E/U+200F), a non-breaking space, smart quotes, or a stray newline.
47
+ * `fetch()` then rejects the Authorization header with a cryptic
48
+ * "character … has a value greater than 255" error on *every* request. We
49
+ * drop anything outside printable ASCII up front so that never happens.
43
50
  */
44
51
  export function sanitizeKey(raw) {
45
- let key = raw.trim();
52
+ // Keep only space + printable ASCII (0x20–0x7E); trim() handles edge spaces.
53
+ let key = raw.replace(/[^\x20-\x7E]/g, '').trim();
46
54
  // Strip matching surrounding quotes ("key", 'key', `key`)
47
55
  while (key.length >= 2 && `"'\``.includes(key[0]) && key[0] === key[key.length - 1]) {
48
56
  key = key.slice(1, -1).trim();
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { sanitizeKey, detectProvider } from './detect.js';
3
+ describe('sanitizeKey', () => {
4
+ it('trims surrounding whitespace and quotes', () => {
5
+ expect(sanitizeKey(' gsk_abc123 ')).toBe('gsk_abc123');
6
+ expect(sanitizeKey('"gsk_abc123"')).toBe('gsk_abc123');
7
+ expect(sanitizeKey("'gsk_abc123'")).toBe('gsk_abc123');
8
+ });
9
+ it('strips invisible bidi marks pasted on RTL systems', () => {
10
+ // U+200F RIGHT-TO-LEFT MARK embedded around the key — the exact class of
11
+ // char that makes fetch() throw "value greater than 255" on the header.
12
+ expect(sanitizeKey('‏gsk_abc123‏')).toBe('gsk_abc123');
13
+ expect(sanitizeKey('gsk_‎abc123')).toBe('gsk_abc123');
14
+ });
15
+ it('strips non-breaking spaces, newlines, and smart quotes', () => {
16
+ expect(sanitizeKey('gsk_abc 123')).toBe('gsk_abc123');
17
+ expect(sanitizeKey('gsk_abc123\n')).toBe('gsk_abc123');
18
+ expect(sanitizeKey('“gsk_abc123”')).toBe('gsk_abc123');
19
+ });
20
+ it('produces a header-safe (all printable ASCII) result', () => {
21
+ const cleaned = sanitizeKey('‏gsk_ab c123');
22
+ expect([...cleaned].every((ch) => ch.charCodeAt(0) >= 0x21 && ch.charCodeAt(0) <= 0x7e)).toBe(true);
23
+ });
24
+ it('leaves a clean key untouched and still auto-detects the provider', () => {
25
+ const key = sanitizeKey('gsk_1234567890');
26
+ expect(key).toBe('gsk_1234567890');
27
+ expect(detectProvider(key)).toBe('groq');
28
+ });
29
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vexi-cli",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Open-source AI coding agent for your terminal. Bring your own key, zero config, multilingual.",
5
5
  "type": "module",
6
6
  "bin": {