more-compute 0.3.2__py3-none-any.whl → 0.3.3__py3-none-any.whl

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.
frontend/app/globals.css CHANGED
@@ -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 {
kernel_run.py CHANGED
@@ -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
@@ -1,4 +1,4 @@
1
- kernel_run.py,sha256=sQchzBOWgynXOKkSMp8T8BAkljw5CqDFrHX2Y0U6yto,16526
1
+ kernel_run.py,sha256=VlIQDKa2z9noDwAyRpUz49LCspjHntnlHugHIuP47dw,17936
2
2
  frontend/.DS_Store,sha256=uQeHnkKyuTF1AVax3NPqtN0uCH6XNXAxL9Nkb6Q9cGw,8196
3
3
  frontend/.gitignore,sha256=IH4mX_SQH5rZ-W2M4IUw4E-fxgCBVHKmbQpEYJbWVM0,480
4
4
  frontend/README.md,sha256=YLVf9995r3JZD5UkII5GZCvDK9wXXNrUE0loHA4vlY8,1450
@@ -14,7 +14,7 @@ frontend/styling_README.md,sha256=jvoXKUzJv0rEIU40gz23Z6Xugy4To8P0N9szg_hKrqw,56
14
14
  frontend/tailwind.config.ts,sha256=eP9nVaAuyYo46vGQfCyWbo25_pr2hW830fs1Itcix9Q,620
15
15
  frontend/tsconfig.json,sha256=7SvBlRBYmuXAlAteRQTGwEE7ooWuNaPUrZ219dOo61E,598
16
16
  frontend/app/favicon.ico,sha256=K4rS0zRVqPc2_DqOv48L3qiEitTA20iigzvQ-c13WTI,25931
17
- frontend/app/globals.css,sha256=0k9WgoXRHIQjsI5kVSSkKLfYJs04FYOhOY8JvQrSulg,33823
17
+ frontend/app/globals.css,sha256=pjnnmkXxa0SoPYPtfBdqyHutSeUUqGvUsgSD4Zt7dcA,33884
18
18
  frontend/app/layout.tsx,sha256=VMkhvePu0hV0w-huEmTocTr7lZ6DF_7eidrhzCCMwzc,7604
19
19
  frontend/app/page.tsx,sha256=p-DgDv8xDnwcRfDJY4rtfSQ2VdYwbnP3G-otWqDR1l0,256
20
20
  frontend/components/Notebook.tsx,sha256=JImlThoX4F363yShOqdYDjIIO3i1uqfCLRkDlzurinQ,21851
@@ -64,29 +64,29 @@ frontend/public/fonts/Fira.ttf,sha256=dbSM4W7Drd9n_EkfXq8P31KuxbjZ1wtuFiZ8aFebvT
64
64
  frontend/public/fonts/Tiempos.woff2,sha256=h83bJKvAK301wXCMIvK7ZG5j0H2K3tzAfgo0yk4z8OE,13604
65
65
  frontend/public/fonts/VeraMono.ttf,sha256=2kKB3H2xej385kpiztkodcWJU0AFXsi6JKORTrl7NJ0,49224
66
66
  frontend/types/notebook.ts,sha256=v23RaZe6H3lU5tq6sqnJDPxC2mu0NZFDCJfiN0mgvSs,1359
67
- more_compute-0.3.2.dist-info/licenses/LICENSE,sha256=0Ot-XIetYt06iay6IhtpJkruD-cLZtjyv7_aIEE-oSc,1073
67
+ more_compute-0.3.3.dist-info/licenses/LICENSE,sha256=0Ot-XIetYt06iay6IhtpJkruD-cLZtjyv7_aIEE-oSc,1073
68
68
  morecompute/__init__.py,sha256=pcMVq8Q7qb42AOn7tqgoZJOi3epDDBnEriiv2WVKnXY,87
69
- morecompute/__version__.py,sha256=vNiWJ14r_cw5t_7UDqDQIVZvladKFGyHH2avsLpN7Vg,22
69
+ morecompute/__version__.py,sha256=8KcCYTXH99C2-gCLuPILJvtT9YftRWJsartIx6TQ2ZY,22
70
70
  morecompute/cli.py,sha256=kVvzvPBqF8xO6UuhU_-TBn99nKwJ405R2mAS6zU0KBc,734
71
71
  morecompute/notebook.py,sha256=KEcv0eOEh9N7bPVGoRuKJb47G9MmpQ5zz1B6Dm58w18,4651
72
72
  morecompute/process_worker.py,sha256=KsE3r-XpkYGuyO4w3t54VKkD51LfNHAZc3TYattMtrg,7185
73
- morecompute/server.py,sha256=mdN80iBUixq4KUQfu53aoVsNZ6DM9RaBXc-lwt4bxFk,40285
73
+ morecompute/server.py,sha256=8Bn0RzH6HB1meCr2BXwcjcFAUSFSEVK2KSkM31wX65Q,40246
74
74
  morecompute/execution/__init__.py,sha256=jPmBmq8BZWbUEY9XFSpqt5FkgX04uNS10WnUlr7Rnms,134
75
75
  morecompute/execution/__main__.py,sha256=pAWB_1bn99u8Gb-tVMSMI-NYvbYbDiwbn40L0a0djeA,202
76
- morecompute/execution/executor.py,sha256=BesGdx10HSGj8PyPrekdQZzc0tXoqidznEaFITOq8JM,20554
76
+ morecompute/execution/executor.py,sha256=C7exiQN5eeg_XJzfqbJThFvRiwBPdwNrJ6koCZnVwN8,20643
77
77
  morecompute/execution/worker.py,sha256=-_-KD89LVA1DmGa1fTqKekttTOGX_JMtUdO02-SV5DE,16388
78
78
  morecompute/models/__init__.py,sha256=VLJ5GWi2uTNiZBdvl-ipSbmA6EL8FZHZ5oq-rJmm9z0,640
79
79
  morecompute/models/api_models.py,sha256=-ydvi9SeTfdoY9oVPNQS4by-kQGSknx6BHhGD8E2tpo,4553
80
80
  morecompute/services/data_manager.py,sha256=c4GKucetMM-VPNbHyzce6bZRvFfmz8kTd5RppLjoLVc,14329
81
81
  morecompute/services/lsp_service.py,sha256=Le8ARImcg2P6oueF_14L8rStHOOseHruRTd_wfDVw7s,12237
82
- morecompute/services/pod_manager.py,sha256=rg6mQJOieElDaO-KGBmg7wO6APkBThLpWmDi472Mvbw,21068
82
+ morecompute/services/pod_manager.py,sha256=FxhhsvxN4N4u1_RSqp6sNyvZy1Aws5_dumC-rQ8EvWI,21072
83
83
  morecompute/services/pod_monitor.py,sha256=Y5aiNoVsvkGiHddNbfR1laAKn8G0eY0_nJyTM4VVkyg,4711
84
84
  morecompute/services/prime_intellect.py,sha256=b705rHv3RPRsgWedRlHwoP_S-TxxZtMSyZhnaiZpMgk,10273
85
85
  morecompute/static/styles.css,sha256=el_NtrUMbAUNSiMVBn1xlG70m3iPv7dyaIbWQMexhsY,19277
86
86
  morecompute/utils/__init__.py,sha256=VIxCL3S1pnjEs4cjKGZqZB68_P8FegdeMIqBjJhI5jQ,419
87
87
  morecompute/utils/cache_util.py,sha256=lVlXudHvtyvSo_kCSxORJrI85Jod8FrQLbI2f_JOIbA,661
88
88
  morecompute/utils/cell_magics.py,sha256=XUxy6lIsrx-9r0mWn0smI1gvT3-kv0rhN5km9cs3D48,27278
89
- morecompute/utils/config_util.py,sha256=fGTQll7Zh05ZHrW8LuQNTJGziGnIfvKIU3azbrY-I-s,1793
89
+ morecompute/utils/config_util.py,sha256=I90om4Wf4BODc5Gy9u8Tu34AcqpkfkGAKQO6JE_NMzU,1940
90
90
  morecompute/utils/error_utils.py,sha256=e50WLFdD6ngIC30xAgrzdTYtD8tPOIFkKAAh_sPbK0I,11667
91
91
  morecompute/utils/line_magics.py,sha256=kTutYBPAWoURY_pk8HXQ38IP712M2rBBfUg3oN8VrP0,33740
92
92
  morecompute/utils/notebook_util.py,sha256=3hH94dtXvhizRVTU9a2b38m_51Y4igoXpkjAXUqpVBQ,1353
@@ -95,8 +95,8 @@ morecompute/utils/shell_utils.py,sha256=fGFLhQLZU-lmMGALbbS-fKPkhQtmMhZ1FkgQ3Teo
95
95
  morecompute/utils/special_commands.py,sha256=IyF9MTINMNNo3P_f56Q6yS_P2HNjA9vohYVxEV7KYnc,17331
96
96
  morecompute/utils/system_environment_util.py,sha256=32mQRubo0i4X61o-825T7m-eUSidcEp07qkInP1sWZA,4774
97
97
  morecompute/utils/zmq_util.py,sha256=tx7-iS04UN69OFtBzkxcEnRhT7xtI9EzRnrZ_nsH_O0,1889
98
- more_compute-0.3.2.dist-info/METADATA,sha256=qO93-kelqgnC3wWl2NzLe_JyMfOnGtBJ3wPY5WqadrI,3631
99
- more_compute-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
100
- more_compute-0.3.2.dist-info/entry_points.txt,sha256=xp7z9eRPNRM4oxkZZVlyXkhkSjN1AjoYI_B7qpDJ1bI,49
101
- more_compute-0.3.2.dist-info/top_level.txt,sha256=Tamm6ADzjwaQa1z27O7Izcyhyt9f0gVjMv1_tC810aI,32
102
- more_compute-0.3.2.dist-info/RECORD,,
98
+ more_compute-0.3.3.dist-info/METADATA,sha256=RO_WGr4vGpdG7gIvYqrMH6iBnc9dQiYSpnGYV-uhQYM,3718
99
+ more_compute-0.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
100
+ more_compute-0.3.3.dist-info/entry_points.txt,sha256=xp7z9eRPNRM4oxkZZVlyXkhkSjN1AjoYI_B7qpDJ1bI,49
101
+ more_compute-0.3.3.dist-info/top_level.txt,sha256=Tamm6ADzjwaQa1z27O7Izcyhyt9f0gVjMv1_tC810aI,32
102
+ more_compute-0.3.3.dist-info/RECORD,,
@@ -1 +1 @@
1
- __version__ = "0.3.2"
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
  )
morecompute/server.py CHANGED
@@ -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
  ])
@@ -1,45 +1,67 @@
1
1
  """Configuration utilities for managing API keys and environment variables."""
2
2
 
3
3
  from pathlib import Path
4
+ from typing import Optional
4
5
  import os
6
+ import json
5
7
 
6
8
 
7
- def load_api_key_from_env(env_var: str, env_file_path: Path | None = None) -> str | None:
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]:
8
38
  """
9
- Load API key from environment variable or .env file.
39
+ Load API key from user config directory (~/.morecompute/config.json).
40
+ Falls back to environment variable if not found in config.
10
41
 
11
42
  Args:
12
- env_var: Environment variable name to check
13
- env_file_path: Path to .env file (optional)
43
+ key_name: Key name (e.g., "PRIME_INTELLECT_API_KEY")
14
44
 
15
45
  Returns:
16
46
  API key string or None if not found
17
47
  """
18
- api_key = os.getenv(env_var)
19
- if api_key:
20
- return api_key
48
+ # Check environment variable first
49
+ env_key = os.getenv(key_name)
50
+ if env_key:
51
+ return env_key
21
52
 
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
53
+ # Check config file
54
+ config = _load_config()
55
+ return config.get(key_name)
31
56
 
32
- return None
33
57
 
34
-
35
- def save_api_key_to_env(env_var: str, api_key: str, env_file_path: Path) -> None:
58
+ def save_api_key(key_name: str, api_key: str) -> None:
36
59
  """
37
- Save API key to .env file, replacing existing value if present.
60
+ Save API key to user config directory (~/.morecompute/config.json).
38
61
 
39
62
  Args:
40
- env_var: Environment variable name
63
+ key_name: Key name (e.g., "PRIME_INTELLECT_API_KEY")
41
64
  api_key: API key value to save
42
- env_file_path: Path to .env file
43
65
 
44
66
  Raises:
45
67
  ValueError: If API key is empty
@@ -48,12 +70,6 @@ def save_api_key_to_env(env_var: str, api_key: str, env_file_path: Path) -> None
48
70
  if not api_key.strip():
49
71
  raise ValueError("API key cannot be empty")
50
72
 
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)
73
+ config = _load_config()
74
+ config[key_name] = api_key
75
+ _save_config(config)