more-compute 0.3.2__tar.gz → 0.3.3__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 (111) hide show
  1. {more_compute-0.3.2/more_compute.egg-info → more_compute-0.3.3}/PKG-INFO +9 -1
  2. {more_compute-0.3.2 → more_compute-0.3.3}/README.md +8 -0
  3. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/app/globals.css +1 -0
  4. {more_compute-0.3.2 → more_compute-0.3.3}/kernel_run.py +50 -5
  5. {more_compute-0.3.2 → more_compute-0.3.3/more_compute.egg-info}/PKG-INFO +9 -1
  6. more_compute-0.3.3/morecompute/__version__.py +1 -0
  7. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/execution/executor.py +1 -0
  8. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/server.py +4 -4
  9. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/services/pod_manager.py +2 -2
  10. more_compute-0.3.3/morecompute/utils/config_util.py +75 -0
  11. more_compute-0.3.2/morecompute/__version__.py +0 -1
  12. more_compute-0.3.2/morecompute/utils/config_util.py +0 -59
  13. {more_compute-0.3.2 → more_compute-0.3.3}/LICENSE +0 -0
  14. {more_compute-0.3.2 → more_compute-0.3.3}/MANIFEST.in +0 -0
  15. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/.DS_Store +0 -0
  16. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/.gitignore +0 -0
  17. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/README.md +0 -0
  18. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/__init__.py +0 -0
  19. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/app/favicon.ico +0 -0
  20. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/app/layout.tsx +0 -0
  21. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/app/page.tsx +0 -0
  22. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/Notebook.tsx +0 -0
  23. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/cell/AddCellButton.tsx +0 -0
  24. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/cell/CellButton.tsx +0 -0
  25. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/cell/MonacoCell.tsx +0 -0
  26. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/layout/ConnectionBanner.tsx +0 -0
  27. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/layout/Sidebar.tsx +0 -0
  28. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/modals/ConfirmModal.tsx +0 -0
  29. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/modals/ErrorModal.tsx +0 -0
  30. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/modals/SuccessModal.tsx +0 -0
  31. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/output/CellOutput.tsx +0 -0
  32. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/output/ErrorDisplay.tsx +0 -0
  33. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/output/MarkdownRenderer.tsx +0 -0
  34. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/popups/ComputePopup.tsx +0 -0
  35. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/popups/FilterPopup.tsx +0 -0
  36. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/popups/FolderPopup.tsx +0 -0
  37. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/popups/MetricsPopup.tsx +0 -0
  38. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/popups/PackagesPopup.tsx +0 -0
  39. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/components/popups/SettingsPopup.tsx +0 -0
  40. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/contexts/PodWebSocketContext.tsx +0 -0
  41. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/eslint.config.mjs +0 -0
  42. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/lib/api.ts +0 -0
  43. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/lib/monaco-themes.ts +0 -0
  44. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/lib/settings.ts +0 -0
  45. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/lib/themes.json +0 -0
  46. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/lib/websocket-native.ts +0 -0
  47. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/lib/websocket.ts +0 -0
  48. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/next-env.d.ts +0 -0
  49. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/next.config.mjs +0 -0
  50. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/next.config.ts +0 -0
  51. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/package-lock.json +0 -0
  52. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/package.json +0 -0
  53. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/postcss.config.mjs +0 -0
  54. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/add.svg +0 -0
  55. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/check.svg +0 -0
  56. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/copy.svg +0 -0
  57. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/folder.svg +0 -0
  58. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/metric.svg +0 -0
  59. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/packages.svg +0 -0
  60. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/play.svg +0 -0
  61. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/python.svg +0 -0
  62. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/setting.svg +0 -0
  63. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/stop.svg +0 -0
  64. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/trash.svg +0 -0
  65. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/up-down.svg +0 -0
  66. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/assets/icons/x.svg +0 -0
  67. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/file.svg +0 -0
  68. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/fonts/Fira.ttf +0 -0
  69. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/fonts/Tiempos.woff2 +0 -0
  70. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/fonts/VeraMono.ttf +0 -0
  71. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/globe.svg +0 -0
  72. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/next.svg +0 -0
  73. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/vercel.svg +0 -0
  74. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/public/window.svg +0 -0
  75. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/styling_README.md +0 -0
  76. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/tailwind.config.ts +0 -0
  77. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/tsconfig.json +0 -0
  78. {more_compute-0.3.2 → more_compute-0.3.3}/frontend/types/notebook.ts +0 -0
  79. {more_compute-0.3.2 → more_compute-0.3.3}/more_compute.egg-info/SOURCES.txt +0 -0
  80. {more_compute-0.3.2 → more_compute-0.3.3}/more_compute.egg-info/dependency_links.txt +0 -0
  81. {more_compute-0.3.2 → more_compute-0.3.3}/more_compute.egg-info/entry_points.txt +0 -0
  82. {more_compute-0.3.2 → more_compute-0.3.3}/more_compute.egg-info/requires.txt +0 -0
  83. {more_compute-0.3.2 → more_compute-0.3.3}/more_compute.egg-info/top_level.txt +0 -0
  84. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/__init__.py +0 -0
  85. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/cli.py +0 -0
  86. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/execution/__init__.py +0 -0
  87. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/execution/__main__.py +0 -0
  88. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/execution/worker.py +0 -0
  89. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/models/__init__.py +0 -0
  90. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/models/api_models.py +0 -0
  91. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/notebook.py +0 -0
  92. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/process_worker.py +0 -0
  93. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/services/data_manager.py +0 -0
  94. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/services/lsp_service.py +0 -0
  95. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/services/pod_monitor.py +0 -0
  96. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/services/prime_intellect.py +0 -0
  97. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/static/styles.css +0 -0
  98. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/__init__.py +0 -0
  99. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/cache_util.py +0 -0
  100. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/cell_magics.py +0 -0
  101. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/error_utils.py +0 -0
  102. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/line_magics.py +0 -0
  103. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/notebook_util.py +0 -0
  104. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/python_environment_util.py +0 -0
  105. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/shell_utils.py +0 -0
  106. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/special_commands.py +0 -0
  107. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/system_environment_util.py +0 -0
  108. {more_compute-0.3.2 → more_compute-0.3.3}/morecompute/utils/zmq_util.py +0 -0
  109. {more_compute-0.3.2 → more_compute-0.3.3}/pyproject.toml +0 -0
  110. {more_compute-0.3.2 → more_compute-0.3.3}/setup.cfg +0 -0
  111. {more_compute-0.3.2 → more_compute-0.3.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: more-compute
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: An interactive notebook environment for local and GPU computing
5
5
  Home-page: https://github.com/DannyMang/MORECOMPUTE
6
6
  Author: MoreCompute Team
@@ -46,6 +46,14 @@ Dynamic: requires-python
46
46
 
47
47
  An interactive notebook environment, similar to Marimo and Google Colab, that runs locally. It works with standard `.ipynb` files, similar to Jupyter Lab but more awesome.
48
48
 
49
+
50
+
51
+
52
+ https://github.com/user-attachments/assets/8c7ec716-dade-4de2-ad37-71d328129c97
53
+
54
+
55
+
56
+
49
57
  ## Installation
50
58
 
51
59
  **Prerequisites:** [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
@@ -6,6 +6,14 @@
6
6
 
7
7
  An interactive notebook environment, similar to Marimo and Google Colab, that runs locally. It works with standard `.ipynb` files, similar to Jupyter Lab but more awesome.
8
8
 
9
+
10
+
11
+
12
+ https://github.com/user-attachments/assets/8c7ec716-dade-4de2-ad37-71d328129c97
13
+
14
+
15
+
16
+
9
17
  ## Installation
10
18
 
11
19
  **Prerequisites:** [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
@@ -111,6 +111,7 @@ body {
111
111
  display: flex;
112
112
  align-items: center;
113
113
  gap: 6px;
114
+ pointer-events: none; /* Allow clicks to pass through */
114
115
  }
115
116
 
116
117
  .connection-banner.provisioning {
@@ -25,6 +25,7 @@ class NotebookLauncher:
25
25
  self.notebook_path = notebook_path
26
26
  self.is_windows = platform.system() == "Windows"
27
27
  self.cleaning_up = False # Flag to prevent multiple cleanup calls
28
+ self.frontend_port = int(os.getenv("MORECOMPUTE_FRONTEND_PORT", "2718"))
28
29
  root_dir = notebook_path.parent if notebook_path.parent != Path('') else Path.cwd()
29
30
  os.environ["MORECOMPUTE_ROOT"] = str(root_dir.resolve())
30
31
  os.environ["MORECOMPUTE_NOTEBOOK_PATH"] = str(self.notebook_path)
@@ -244,9 +245,14 @@ class NotebookLauncher:
244
245
  fe_stdout = None if self.debug else subprocess.DEVNULL
245
246
  fe_stderr = None if self.debug else subprocess.DEVNULL
246
247
 
248
+ # Set PORT environment variable for Next.js
249
+ frontend_env = os.environ.copy()
250
+ frontend_env["PORT"] = str(self.frontend_port)
251
+
247
252
  self.frontend_process = subprocess.Popen(
248
253
  [npm_cmd, "run", "dev"],
249
254
  cwd=frontend_dir,
255
+ env=frontend_env,
250
256
  stdout=fe_stdout,
251
257
  stderr=fe_stderr,
252
258
  shell=self.is_windows, # CRITICAL for Windows
@@ -256,7 +262,7 @@ class NotebookLauncher:
256
262
 
257
263
  # Wait a bit then open browser
258
264
  time.sleep(3)
259
- webbrowser.open("http://localhost:3000")
265
+ webbrowser.open(f"http://localhost:{self.frontend_port}")
260
266
 
261
267
  except Exception as e:
262
268
  print(f"Failed to start frontend: {e}")
@@ -316,7 +322,7 @@ class NotebookLauncher:
316
322
  def run(self):
317
323
  """Main run method"""
318
324
  print("\n Edit notebook in your browser!\n")
319
- print(" ➜ URL: http://localhost:3000\n")
325
+ print(f" ➜ URL: http://localhost:{self.frontend_port}\n")
320
326
 
321
327
  # Track Ctrl+C presses
322
328
  interrupt_count = [0] # Use list to allow modification in nested function
@@ -366,7 +372,11 @@ class NotebookLauncher:
366
372
 
367
373
 
368
374
  def build_parser() -> argparse.ArgumentParser:
369
- parser = argparse.ArgumentParser(description="Launch the MoreCompute notebook")
375
+ parser = argparse.ArgumentParser(
376
+ prog="more-compute",
377
+ description="MoreCompute - Jupyter notebooks with GPU compute",
378
+ add_help=False,
379
+ )
370
380
  parser.add_argument(
371
381
  "--version",
372
382
  "-v",
@@ -383,7 +393,13 @@ def build_parser() -> argparse.ArgumentParser:
383
393
  "-debug",
384
394
  "--debug",
385
395
  action="store_true",
386
- help="Show backend/frontend logs (hidden by default)",
396
+ help="Show backend/frontend logs",
397
+ )
398
+ parser.add_argument(
399
+ "-h",
400
+ "--help",
401
+ action="store_true",
402
+ help="Show this help message",
387
403
  )
388
404
  return parser
389
405
 
@@ -413,9 +429,37 @@ def ensure_notebook_exists(notebook_path: Path):
413
429
  notebook.save_to_file(str(notebook_path))
414
430
 
415
431
 
432
+ def print_help():
433
+ """Print concise help message"""
434
+ print(f"""Usage: more-compute [OPTIONS] [NOTEBOOK]
435
+
436
+ MoreCompute - Jupyter notebooks with GPU compute
437
+
438
+ Getting started:
439
+
440
+ * more-compute new create a new notebook with timestamp
441
+ * more-compute notebook.ipynb open or create notebook.ipynb
442
+
443
+ Options:
444
+ --version, -v Show version and exit
445
+ --debug Show backend/frontend logs
446
+ --help, -h Show this message and exit
447
+
448
+ Environment variables:
449
+ MORECOMPUTE_PORT Backend port (default: 8000)
450
+ MORECOMPUTE_FRONTEND_PORT Frontend port (default: 2718)
451
+ MORECOMPUTE_NOTEBOOK_PATH Default notebook path
452
+ """)
453
+
416
454
  def main(argv=None):
417
455
  parser = build_parser()
418
456
  args = parser.parse_args(argv)
457
+
458
+ # Show help if requested or no arguments provided
459
+ if args.help or (args.notebook_path is None and os.getenv("MORECOMPUTE_NOTEBOOK_PATH") is None):
460
+ print_help()
461
+ sys.exit(0)
462
+
419
463
  raw_notebook_path = args.notebook_path
420
464
 
421
465
  if raw_notebook_path == "new":
@@ -429,7 +473,8 @@ def main(argv=None):
429
473
  raw_notebook_path = notebook_path_env
430
474
 
431
475
  if raw_notebook_path is None:
432
- raw_notebook_path = DEFAULT_NOTEBOOK_NAME
476
+ print_help()
477
+ sys.exit(0)
433
478
 
434
479
  notebook_path = Path(raw_notebook_path).expanduser().resolve()
435
480
  ensure_notebook_exists(notebook_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: more-compute
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: An interactive notebook environment for local and GPU computing
5
5
  Home-page: https://github.com/DannyMang/MORECOMPUTE
6
6
  Author: MoreCompute Team
@@ -46,6 +46,14 @@ Dynamic: requires-python
46
46
 
47
47
  An interactive notebook environment, similar to Marimo and Google Colab, that runs locally. It works with standard `.ipynb` files, similar to Jupyter Lab but more awesome.
48
48
 
49
+
50
+
51
+
52
+ https://github.com/user-attachments/assets/8c7ec716-dade-4de2-ad37-71d328129c97
53
+
54
+
55
+
56
+
49
57
  ## Installation
50
58
 
51
59
  **Prerequisites:** [Node.js](https://nodejs.org/) >= 20.10.0 required for web interface
@@ -0,0 +1 @@
1
+ __version__ = "0.3.3"
@@ -86,6 +86,7 @@ class NextZmqExecutor:
86
86
  self.worker_proc = subprocess.Popen(
87
87
  [sys.executable, '-m', 'morecompute.execution.worker'],
88
88
  env=env,
89
+ stdin=subprocess.DEVNULL, # Explicitly close stdin to prevent fd issues
89
90
  stdout=subprocess.DEVNULL,
90
91
  stderr=None # Show errors in terminal
91
92
  )
@@ -18,7 +18,7 @@ from .utils.system_environment_util import DeviceMetrics
18
18
  from .utils.error_utils import ErrorUtils
19
19
  from .utils.cache_util import make_cache_key
20
20
  from .utils.notebook_util import coerce_cell_source
21
- from .utils.config_util import load_api_key_from_env, save_api_key_to_env
21
+ from .utils.config_util import load_api_key, save_api_key
22
22
  from .utils.zmq_util import reconnect_zmq_sockets, reset_to_local_zmq
23
23
  from .services.prime_intellect import PrimeIntellectService
24
24
  from .services.pod_manager import PodKernelManager
@@ -67,7 +67,7 @@ else:
67
67
  error_utils = ErrorUtils()
68
68
  executor = NextZmqExecutor(error_utils=error_utils)
69
69
  metrics = DeviceMetrics()
70
- prime_api_key = load_api_key_from_env("PRIME_INTELLECT_API_KEY", BASE_DIR / ".env")
70
+ prime_api_key = load_api_key("PRIME_INTELLECT_API_KEY")
71
71
  prime_intellect = PrimeIntellectService(api_key=prime_api_key) if prime_api_key else None
72
72
  pod_manager: PodKernelManager | None = None
73
73
  data_manager = DataManager(prime_intellect=prime_intellect)
@@ -654,14 +654,14 @@ async def get_gpu_config() -> ConfigStatusResponse:
654
654
 
655
655
  @app.post("/api/gpu/config", response_model=ApiKeyResponse)
656
656
  async def set_gpu_config(request: ApiKeyRequest) -> ApiKeyResponse:
657
- """Save Prime Intellect API key to .env file and reinitialize service."""
657
+ """Save Prime Intellect API key to user config (~/.morecompute/config.json) and reinitialize service."""
658
658
  global prime_intellect, pod_monitor
659
659
 
660
660
  if not request.api_key.strip():
661
661
  raise HTTPException(status_code=400, detail="API key is required")
662
662
 
663
663
  try:
664
- save_api_key_to_env("PRIME_INTELLECT_API_KEY", request.api_key, BASE_DIR / ".env")
664
+ save_api_key("PRIME_INTELLECT_API_KEY", request.api_key)
665
665
  prime_intellect = PrimeIntellectService(api_key=request.api_key)
666
666
  if prime_intellect:
667
667
  pod_monitor = PodMonitor(
@@ -408,8 +408,8 @@ class PodKernelManager:
408
408
  f"'cd /tmp && "
409
409
  f"MC_ZMQ_CMD_ADDR=tcp://0.0.0.0:{self.remote_cmd_port} "
410
410
  f"MC_ZMQ_PUB_ADDR=tcp://0.0.0.0:{self.remote_pub_port} "
411
- f"nohup python3 /tmp/morecompute/execution/worker.py "
412
- f">/tmp/worker.log 2>&1 </dev/null & "
411
+ f"setsid python3 -u /tmp/morecompute/execution/worker.py "
412
+ f"</dev/null >/tmp/worker.log 2>&1 & "
413
413
  f"echo $!'"
414
414
  )
415
415
  ])
@@ -0,0 +1,75 @@
1
+ """Configuration utilities for managing API keys and environment variables."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+ import os
6
+ import json
7
+
8
+
9
+ # Global config directory in user's home
10
+ CONFIG_DIR = Path.home() / ".morecompute"
11
+ CONFIG_FILE = CONFIG_DIR / "config.json"
12
+
13
+
14
+ def _ensure_config_dir() -> None:
15
+ """Ensure the config directory exists."""
16
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
17
+
18
+
19
+ def _load_config() -> dict:
20
+ """Load config from JSON file."""
21
+ if not CONFIG_FILE.exists():
22
+ return {}
23
+ try:
24
+ with CONFIG_FILE.open("r", encoding="utf-8") as f:
25
+ return json.load(f)
26
+ except (json.JSONDecodeError, IOError):
27
+ return {}
28
+
29
+
30
+ def _save_config(config: dict) -> None:
31
+ """Save config to JSON file."""
32
+ _ensure_config_dir()
33
+ with CONFIG_FILE.open("w", encoding="utf-8") as f:
34
+ json.dump(config, f, indent=2)
35
+
36
+
37
+ def load_api_key(key_name: str) -> Optional[str]:
38
+ """
39
+ Load API key from user config directory (~/.morecompute/config.json).
40
+ Falls back to environment variable if not found in config.
41
+
42
+ Args:
43
+ key_name: Key name (e.g., "PRIME_INTELLECT_API_KEY")
44
+
45
+ Returns:
46
+ API key string or None if not found
47
+ """
48
+ # Check environment variable first
49
+ env_key = os.getenv(key_name)
50
+ if env_key:
51
+ return env_key
52
+
53
+ # Check config file
54
+ config = _load_config()
55
+ return config.get(key_name)
56
+
57
+
58
+ def save_api_key(key_name: str, api_key: str) -> None:
59
+ """
60
+ Save API key to user config directory (~/.morecompute/config.json).
61
+
62
+ Args:
63
+ key_name: Key name (e.g., "PRIME_INTELLECT_API_KEY")
64
+ api_key: API key value to save
65
+
66
+ Raises:
67
+ ValueError: If API key is empty
68
+ IOError: If file cannot be written
69
+ """
70
+ if not api_key.strip():
71
+ raise ValueError("API key cannot be empty")
72
+
73
+ config = _load_config()
74
+ config[key_name] = api_key
75
+ _save_config(config)
@@ -1 +0,0 @@
1
- __version__ = "0.3.2"
@@ -1,59 +0,0 @@
1
- """Configuration utilities for managing API keys and environment variables."""
2
-
3
- from pathlib import Path
4
- import os
5
-
6
-
7
- def load_api_key_from_env(env_var: str, env_file_path: Path | None = None) -> str | None:
8
- """
9
- Load API key from environment variable or .env file.
10
-
11
- Args:
12
- env_var: Environment variable name to check
13
- env_file_path: Path to .env file (optional)
14
-
15
- Returns:
16
- API key string or None if not found
17
- """
18
- api_key = os.getenv(env_var)
19
- if api_key:
20
- return api_key
21
-
22
- if env_file_path and env_file_path.exists():
23
- try:
24
- with env_file_path.open("r", encoding="utf-8") as f:
25
- for line in f:
26
- line = line.strip()
27
- if line.startswith(f"{env_var}="):
28
- return line.split("=", 1)[1].strip().strip('"').strip("'")
29
- except Exception:
30
- pass
31
-
32
- return None
33
-
34
-
35
- def save_api_key_to_env(env_var: str, api_key: str, env_file_path: Path) -> None:
36
- """
37
- Save API key to .env file, replacing existing value if present.
38
-
39
- Args:
40
- env_var: Environment variable name
41
- api_key: API key value to save
42
- env_file_path: Path to .env file
43
-
44
- Raises:
45
- ValueError: If API key is empty
46
- IOError: If file cannot be written
47
- """
48
- if not api_key.strip():
49
- raise ValueError("API key cannot be empty")
50
-
51
- existing_lines = []
52
- if env_file_path.exists():
53
- with env_file_path.open("r", encoding="utf-8") as f:
54
- existing_lines = f.readlines()
55
-
56
- new_lines = [line for line in existing_lines if not line.strip().startswith(f"{env_var}=")]
57
- new_lines.append(f"{env_var}={api_key}\n")
58
- with env_file_path.open("w", encoding="utf-8") as f:
59
- f.writelines(new_lines)
File without changes
File without changes
File without changes
File without changes