neuroncli 2.2.0__tar.gz → 2.3.0__tar.gz

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.
Files changed (25) hide show
  1. {neuroncli-2.2.0 → neuroncli-2.3.0}/PKG-INFO +3 -20
  2. {neuroncli-2.2.0 → neuroncli-2.3.0}/README.md +2 -19
  3. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/auth.py +140 -70
  4. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/config.py +1 -1
  5. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli.egg-info/PKG-INFO +3 -20
  6. {neuroncli-2.2.0 → neuroncli-2.3.0}/pyproject.toml +1 -1
  7. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/__init__.py +0 -0
  8. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/__main__.py +0 -0
  9. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/agent.py +0 -0
  10. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/cli.py +0 -0
  11. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/git_integration.py +0 -0
  12. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/ollama_client.py +0 -0
  13. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/openrouter_client.py +0 -0
  14. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/prompts.py +0 -0
  15. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/provider.py +0 -0
  16. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/session.py +0 -0
  17. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/tools.py +0 -0
  18. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli/ui.py +0 -0
  19. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli.egg-info/SOURCES.txt +0 -0
  20. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli.egg-info/dependency_links.txt +0 -0
  21. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli.egg-info/entry_points.txt +0 -0
  22. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli.egg-info/requires.txt +0 -0
  23. {neuroncli-2.2.0 → neuroncli-2.3.0}/neuroncli.egg-info/top_level.txt +0 -0
  24. {neuroncli-2.2.0 → neuroncli-2.3.0}/setup.cfg +0 -0
  25. {neuroncli-2.2.0 → neuroncli-2.3.0}/tests/test_agent.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuroncli
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: AI Coding Agent — Free Claude Code alternative powered by Kimi K2.5 + Ollama
5
5
  Author-email: Rahul S <rahultech72216@gmail.com>
6
6
  License: MIT
@@ -34,25 +34,8 @@ Requires-Dist: requests>=2.28.0
34
34
  ## Install
35
35
 
36
36
  ```bash
37
- # Windows
38
- pip install neuroncli
39
-
40
- # Linux / macOS (modern distros use pipx)
41
- pipx install neuroncli
42
-
43
- # If pipx not installed:
44
- # sudo apt install pipx && pipx ensurepath (Debian/Ubuntu)
45
- # sudo dnf install pipx && pipx ensurepath (Fedora)
46
- # brew install pipx && pipx ensurepath (macOS)
47
-
48
- # From source (any platform)
49
- git clone https://github.com/RAHUL-DevelopeRR/neuroncli.git
50
- cd neuroncli && pip install -e .
51
- ```
52
-
53
- ```bash
54
- # Linux/macOS auto-install script
55
- curl -fsSL https://raw.githubusercontent.com/RAHUL-DevelopeRR/neuroncli/master/install.sh | bash
37
+ pip install neuroncli # Windows / macOS / Cloud VMs
38
+ pip install neuroncli --break-system-packages # Linux (Ubuntu 23+, Debian 12+, Fedora 38+)
56
39
  ```
57
40
 
58
41
  ## Quick Start
@@ -9,25 +9,8 @@
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- # Windows
13
- pip install neuroncli
14
-
15
- # Linux / macOS (modern distros use pipx)
16
- pipx install neuroncli
17
-
18
- # If pipx not installed:
19
- # sudo apt install pipx && pipx ensurepath (Debian/Ubuntu)
20
- # sudo dnf install pipx && pipx ensurepath (Fedora)
21
- # brew install pipx && pipx ensurepath (macOS)
22
-
23
- # From source (any platform)
24
- git clone https://github.com/RAHUL-DevelopeRR/neuroncli.git
25
- cd neuroncli && pip install -e .
26
- ```
27
-
28
- ```bash
29
- # Linux/macOS auto-install script
30
- curl -fsSL https://raw.githubusercontent.com/RAHUL-DevelopeRR/neuroncli/master/install.sh | bash
12
+ pip install neuroncli # Windows / macOS / Cloud VMs
13
+ pip install neuroncli --break-system-packages # Linux (Ubuntu 23+, Debian 12+, Fedora 38+)
31
14
  ```
32
15
 
33
16
  ## Quick Start
@@ -179,16 +179,11 @@ def _exchange_code_for_key(code: str, code_verifier: str) -> str:
179
179
 
180
180
  def run_oauth_flow() -> str | None:
181
181
  """
182
- Run the full OAuth PKCE flow:
183
- 1. Generate PKCE challenge
184
- 2. Open browser to OpenRouter auth
185
- 3. Start localhost server to catch redirect
186
- 4. Exchange code for API key
187
- 5. Store key locally
188
-
189
- Returns the API key on success, None on failure.
182
+ Run OAuth PKCE + manual paste IN PARALLEL:
183
+ - Background thread: localhost server waits for callback (10 min)
184
+ - Foreground: user can paste key manually while waiting
185
+ - Whichever succeeds first wins.
190
186
  """
191
- # Reset
192
187
  _OAuthCallbackHandler.auth_code = None
193
188
 
194
189
  # Generate PKCE pair
@@ -203,83 +198,160 @@ def run_oauth_flow() -> str | None:
203
198
  f"&code_challenge_method=S256"
204
199
  )
205
200
 
206
- # Start callback server in background
201
+ # Start callback server in background thread
202
+ server = None
207
203
  try:
208
204
  server = http.server.HTTPServer(("127.0.0.1", CALLBACK_PORT), _OAuthCallbackHandler)
209
205
  except OSError:
210
- # Port in use — another instance is running
211
- print(f"\n \033[91m[X] Port {CALLBACK_PORT} is in use. Close other instances first.\033[0m")
212
- return None
213
- server.timeout = 120 # 2 minute timeout
214
-
215
- print("\n \033[96m\033[1m[AUTH]\033[0m Opening browser for OpenRouter login...")
216
- print(f" \033[90mIf browser doesn't open, visit:\033[0m")
217
- print(f" \033[90m{auth_url[:80]}...\033[0m\n")
206
+ pass # Port in use — skip background listener
207
+
208
+ server_thread = None
209
+ if server:
210
+ server.timeout = 600 # 10 MINUTES — enough for signup + email verify + survey
211
+ def _listen():
212
+ try:
213
+ server.handle_request()
214
+ except Exception:
215
+ pass
216
+ finally:
217
+ server.server_close()
218
+ server_thread = threading.Thread(target=_listen, daemon=True)
219
+ server_thread.start()
218
220
 
219
221
  # Open browser
222
+ print("\n \033[96m\033[1m[AUTH]\033[0m Opening browser for OpenRouter login...")
220
223
  try:
221
224
  webbrowser.open(auth_url)
222
225
  except Exception:
223
- print(" \033[90m(Browser could not be opened. Use the URL above.)\033[0m")
226
+ pass
224
227
 
225
- print(" \033[93mWaiting for authentication...\033[0m", end="", flush=True)
228
+ # Show user-friendly instructions
229
+ print(f" \033[90m──────────────────────────────────────────────\033[0m")
230
+ print(f" \033[97mSign up / log in to OpenRouter in your browser.\033[0m")
231
+ print(f" \033[97mAfter login, you'll be redirected back here.\033[0m")
232
+ print(f" \033[90m──────────────────────────────────────────────\033[0m")
233
+ print(f" \033[90mIf auto-redirect doesn't work:\033[0m")
234
+ print(f" \033[90m1. Go to: \033[4mhttps://openrouter.ai/settings/keys\033[0m")
235
+ print(f" \033[90m2. Click 'Create Key' -> copy the key\033[0m")
236
+ print(f" \033[90m3. Paste it below\033[0m")
237
+ print(f" \033[90m──────────────────────────────────────────────\033[0m\n")
238
+
239
+ # PARALLEL: Wait for EITHER OAuth callback OR manual paste
240
+ # Poll every 2 seconds to check if background OAuth succeeded
241
+ import time
242
+ import sys
243
+ import select
244
+
245
+ api_key = None
246
+
247
+ # On Windows, we can't use select() on stdin. Use a simpler approach.
248
+ if sys.platform == "win32":
249
+ api_key = _windows_parallel_auth(server_thread, code_verifier)
250
+ else:
251
+ api_key = _unix_parallel_auth(server_thread, code_verifier)
226
252
 
227
- # Wait for the callback (blocks until request or timeout)
228
- try:
229
- server.handle_request() # Handles exactly one request
230
- except Exception:
231
- pass
232
- finally:
233
- server.server_close()
253
+ # Clean up server
254
+ if server:
255
+ try:
256
+ server.server_close()
257
+ except Exception:
258
+ pass
259
+
260
+ if api_key:
261
+ store_api_key(api_key)
262
+ print(f"\n \033[92m\033[1m[OK] API key saved to {CONFIG_FILE}\033[0m")
263
+ print(f" \033[92m\033[1m You won't be asked again.\033[0m\n")
264
+ return api_key
265
+
266
+ print(f"\n \033[90mNo API key provided. Using Ollama fallback if available.\033[0m\n")
267
+ return None
234
268
 
269
+
270
+ def _check_oauth_result(code_verifier: str) -> str | None:
271
+ """Check if the background OAuth server received a code."""
235
272
  code = _OAuthCallbackHandler.auth_code
236
273
  if not code:
237
- print(f"\n \033[91m[X] OAuth timed out or failed.\033[0m")
238
- return _manual_key_fallback()
274
+ return None
275
+ try:
276
+ return _exchange_code_for_key(code, code_verifier)
277
+ except RuntimeError:
278
+ return None
239
279
 
240
- print(f" \033[92mv\033[0m")
241
- print(" \033[96mExchanging code for API key...\033[0m", end="", flush=True)
242
280
 
243
- # Exchange code for key
281
+ def _validate_key(key: str) -> bool:
282
+ """Check if a pasted key looks valid."""
283
+ key = key.strip()
284
+ return len(key) > 10 and (key.startswith("sk-or-") or key.startswith("sk-"))
285
+
286
+
287
+ def _windows_parallel_auth(server_thread, code_verifier: str) -> str | None:
288
+ """Windows: prompt for manual paste while checking OAuth in background."""
289
+ import time
290
+
291
+ print(" \033[93mPaste your API key below (or wait for auto-redirect):\033[0m")
292
+ print(" \033[93m> \033[0m", end="", flush=True)
293
+
244
294
  try:
245
- api_key = _exchange_code_for_key(code, code_verifier)
246
- except RuntimeError as e:
247
- print(f"\n \033[91m[X] {e}\033[0m")
248
- return _manual_key_fallback()
295
+ key = input().strip()
296
+ except (EOFError, KeyboardInterrupt):
297
+ key = ""
298
+
299
+ if key and _validate_key(key):
300
+ return key
301
+
302
+ # Check if OAuth completed while user was typing
303
+ result = _check_oauth_result(code_verifier)
304
+ if result:
305
+ print(" \033[92m[v] Auto-redirect succeeded!\033[0m")
306
+ return result
307
+
308
+ # Neither worked — wait up to 30 more seconds for OAuth
309
+ print(" \033[90mWaiting for browser redirect...\033[0m", end="", flush=True)
310
+ for _ in range(15):
311
+ time.sleep(2)
312
+ result = _check_oauth_result(code_verifier)
313
+ if result:
314
+ print(f" \033[92mv\033[0m")
315
+ return result
316
+ print(".", end="", flush=True)
249
317
 
250
- # Store
251
- store_api_key(api_key)
252
- print(f" \033[92mv\033[0m")
253
- print(f" \033[92m\033[1m[OK] API key saved to {CONFIG_FILE}\033[0m\n")
318
+ print(f" \033[91mx\033[0m")
319
+ return None
254
320
 
255
- return api_key
256
321
 
322
+ def _unix_parallel_auth(server_thread, code_verifier: str) -> str | None:
323
+ """Unix: prompt for manual paste while checking OAuth in background."""
324
+ import time
257
325
 
258
- def _manual_key_fallback() -> str | None:
259
- """Fallback: let user manually paste their OpenRouter API key."""
260
- print(f"\n \033[96m\033[1m[MANUAL SETUP]\033[0m")
261
- print(f" \033[90mOAuth didn't work. You can paste your key instead:\033[0m")
262
- print(f" \033[90m1. Go to: https://openrouter.ai/settings/keys\033[0m")
263
- print(f" \033[90m2. Click 'Create Key'\033[0m")
264
- print(f" \033[90m3. Copy the key (starts with sk-or-...)\033[0m\n")
326
+ print(" \033[93mPaste your API key below (or wait for auto-redirect):\033[0m")
327
+ print(" \033[93m> \033[0m", end="", flush=True)
265
328
 
266
329
  try:
267
- key = input(" \033[93mPaste your API key (or Enter to skip): \033[0m").strip()
330
+ key = input().strip()
268
331
  except (EOFError, KeyboardInterrupt):
269
- return None
270
-
271
- if not key:
272
- print(f"\n \033[90mSkipped. Using Ollama fallback if available.\033[0m\n")
273
- return None
332
+ key = ""
274
333
 
275
- if key.startswith("sk-or-") or key.startswith("sk-"):
276
- store_api_key(key)
277
- print(f" \033[92m\033[1m[OK] API key saved to {CONFIG_FILE}\033[0m\n")
334
+ if key and _validate_key(key):
278
335
  return key
279
- else:
280
- print(f" \033[91m[X] Invalid key format. Expected 'sk-or-...' \033[0m")
281
- print(f" \033[90mGet one at: https://openrouter.ai/settings/keys\033[0m\n")
282
- return None
336
+
337
+ # Check if OAuth completed while user was typing
338
+ result = _check_oauth_result(code_verifier)
339
+ if result:
340
+ print(" \033[92m[v] Auto-redirect succeeded!\033[0m")
341
+ return result
342
+
343
+ # Neither worked — wait up to 30 more seconds for OAuth
344
+ print(" \033[90mWaiting for browser redirect...\033[0m", end="", flush=True)
345
+ for _ in range(15):
346
+ time.sleep(2)
347
+ result = _check_oauth_result(code_verifier)
348
+ if result:
349
+ print(f" \033[92mv\033[0m")
350
+ return result
351
+ print(".", end="", flush=True)
352
+
353
+ print(f" \033[91mx\033[0m")
354
+ return None
283
355
 
284
356
 
285
357
  # ── Public API ────────────────────────────────────────────────────
@@ -289,12 +361,11 @@ def ensure_api_key() -> str | None:
289
361
  Ensure an API key is available. Checks in order:
290
362
  1. OPENROUTER_API_KEY env var
291
363
  2. ~/.neuroncli/config.json
292
- 3. Interactive OAuth PKCE flow (first-run)
293
- 4. Manual key paste (fallback if OAuth fails)
364
+ 3. OAuth PKCE (background) + manual paste (foreground) — in parallel
294
365
 
295
366
  Returns the API key or None if all methods fail.
296
367
  """
297
- # Check env var first
368
+ # Check env var
298
369
  env_key = os.environ.get("OPENROUTER_API_KEY")
299
370
  if env_key:
300
371
  return env_key
@@ -304,12 +375,11 @@ def ensure_api_key() -> str | None:
304
375
  if stored_key:
305
376
  return stored_key
306
377
 
307
- # First run — interactive OAuth
308
- print("\n \033[96m\033[1m╔════════════════════════════════════════════════╗")
309
- print(" ║ Welcome to NeuronCLI! ║")
310
- print(" ║ Let's connect your free OpenRouter account. ║")
311
- print(" ║ This only happens once. ║")
312
- print(" ╚════════════════════════════════════════════════╝\033[0m")
378
+ # First run — welcome message
379
+ print("\n \033[96m\033[1m╔══════════════════════════════════════════════════╗")
380
+ print(" ║ Welcome to NeuronCLI! ║")
381
+ print(" ║ Free AI coding agent — one-time setup below. ║")
382
+ print(" ╚══════════════════════════════════════════════════╝\033[0m")
313
383
 
314
384
  return run_oauth_flow()
315
385
 
@@ -109,4 +109,4 @@ class AgentConfig:
109
109
  # ── App-wide constants ────────────────────────────────────────────
110
110
 
111
111
  APP_NAME = "NeuronCLI"
112
- VERSION = "2.2.0"
112
+ VERSION = "2.3.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuroncli
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: AI Coding Agent — Free Claude Code alternative powered by Kimi K2.5 + Ollama
5
5
  Author-email: Rahul S <rahultech72216@gmail.com>
6
6
  License: MIT
@@ -34,25 +34,8 @@ Requires-Dist: requests>=2.28.0
34
34
  ## Install
35
35
 
36
36
  ```bash
37
- # Windows
38
- pip install neuroncli
39
-
40
- # Linux / macOS (modern distros use pipx)
41
- pipx install neuroncli
42
-
43
- # If pipx not installed:
44
- # sudo apt install pipx && pipx ensurepath (Debian/Ubuntu)
45
- # sudo dnf install pipx && pipx ensurepath (Fedora)
46
- # brew install pipx && pipx ensurepath (macOS)
47
-
48
- # From source (any platform)
49
- git clone https://github.com/RAHUL-DevelopeRR/neuroncli.git
50
- cd neuroncli && pip install -e .
51
- ```
52
-
53
- ```bash
54
- # Linux/macOS auto-install script
55
- curl -fsSL https://raw.githubusercontent.com/RAHUL-DevelopeRR/neuroncli/master/install.sh | bash
37
+ pip install neuroncli # Windows / macOS / Cloud VMs
38
+ pip install neuroncli --break-system-packages # Linux (Ubuntu 23+, Debian 12+, Fedora 38+)
56
39
  ```
57
40
 
58
41
  ## Quick Start
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "neuroncli"
7
- version = "2.2.0"
7
+ version = "2.3.0"
8
8
  description = "AI Coding Agent — Free Claude Code alternative powered by Kimi K2.5 + Ollama"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes