utim-cli 1.44.8__tar.gz → 1.45.1__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 (68) hide show
  1. {utim_cli-1.44.8/utim_cli.egg-info → utim_cli-1.45.1}/PKG-INFO +1 -1
  2. {utim_cli-1.44.8 → utim_cli-1.45.1}/pyproject.toml +1 -1
  3. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/db.py +45 -48
  4. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/router.py +16 -11
  5. utim_cli-1.45.1/utim_cli/server/server.py +56 -0
  6. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/utim.py +3 -3
  7. {utim_cli-1.44.8 → utim_cli-1.45.1/utim_cli.egg-info}/PKG-INFO +1 -1
  8. utim_cli-1.44.8/utim_cli/server/server.py +0 -56
  9. {utim_cli-1.44.8 → utim_cli-1.45.1}/CHANGELOG.md +0 -0
  10. {utim_cli-1.44.8 → utim_cli-1.45.1}/LICENSE +0 -0
  11. {utim_cli-1.44.8 → utim_cli-1.45.1}/MANIFEST.in +0 -0
  12. {utim_cli-1.44.8 → utim_cli-1.45.1}/README.md +0 -0
  13. {utim_cli-1.44.8 → utim_cli-1.45.1}/setup.cfg +0 -0
  14. {utim_cli-1.44.8 → utim_cli-1.45.1}/setup.py +0 -0
  15. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/__init__.py +0 -0
  16. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/agent.py +0 -0
  17. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/auth.py +0 -0
  18. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/backup.py +0 -0
  19. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/blender_agent.py +0 -0
  20. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/bootstrap.py +0 -0
  21. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/client_utils.py +0 -0
  22. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/config.py +0 -0
  23. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/constants.py +0 -0
  24. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/context_pruner.py +0 -0
  25. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/doctor.py +0 -0
  26. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/knowledge_graph.py +0 -0
  27. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/logger.py +0 -0
  28. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/mcp_clean_wrapper.py +0 -0
  29. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/mcp_client.py +0 -0
  30. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/mcp_registry.json +0 -0
  31. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/orchestrator.py +0 -0
  32. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/reflection.py +0 -0
  33. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/report.py +0 -0
  34. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/scrapy_search.py +0 -0
  35. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/__init__.py +0 -0
  36. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/auth.py +0 -0
  37. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/email_utils.py +0 -0
  38. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/firebase.py +0 -0
  39. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/history.py +0 -0
  40. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/logging_config.py +0 -0
  41. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/models.py +0 -0
  42. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/rate_limit.py +0 -0
  43. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/routes/__init__.py +0 -0
  44. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/routes/auth_routes.py +0 -0
  45. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/routes/completion_routes.py +0 -0
  46. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/routes/credit_routes.py +0 -0
  47. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/routes/quota_routes.py +0 -0
  48. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/routes/session_routes.py +0 -0
  49. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/server/routes/share_routes.py +0 -0
  50. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/share.py +0 -0
  51. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/share_tui.py +0 -0
  52. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/situational_scoring.py +0 -0
  53. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/state.py +0 -0
  54. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/tools.py +0 -0
  55. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/tui/__init__.py +0 -0
  56. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/tui/history_dialog.py +0 -0
  57. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/tui/mcp_dialog.py +0 -0
  58. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/tui/model_dialog.py +0 -0
  59. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/tui/resume_dialog.py +0 -0
  60. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/tui/tools_dialog.py +0 -0
  61. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/tui/update_dialog.py +0 -0
  62. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/vector_memory.py +0 -0
  63. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli/workspace.py +0 -0
  64. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli.egg-info/SOURCES.txt +0 -0
  65. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli.egg-info/dependency_links.txt +0 -0
  66. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli.egg-info/entry_points.txt +0 -0
  67. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli.egg-info/requires.txt +0 -0
  68. {utim_cli-1.44.8 → utim_cli-1.45.1}/utim_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: utim-cli
3
- Version: 1.44.8
3
+ Version: 1.45.1
4
4
  Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://utim.dev
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "utim-cli"
7
- version = "1.44.8"
7
+ version = "1.45.1"
8
8
  description = "UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -401,9 +401,13 @@ def init_db() -> None:
401
401
  import logging as _logging
402
402
  _mig_log = _logging.getLogger("utim.db.migration")
403
403
 
404
+ print("[DB INIT] init_db() starting...")
404
405
  try:
406
+ print(f"[DB INIT] Connecting to DB: {engine.url.drivername} (host: {engine.url.host})")
405
407
  Base.metadata.create_all(bind=engine)
408
+ print("[DB INIT] create_all() completed.")
406
409
  except Exception as exc:
410
+ print(f"[DB INIT] create_all() failed: {exc}")
407
411
  _mig_log.warning(f"Skipping table creation (database might be unavailable): {exc}")
408
412
  return
409
413
 
@@ -412,70 +416,62 @@ def init_db() -> None:
412
416
  from sqlalchemy import text, inspect as sa_inspect
413
417
  is_postgres = DATABASE_URL.startswith("postgresql")
414
418
 
415
- def _run_migration(sql: str, label: str):
419
+ try:
420
+ inspector = sa_inspect(engine)
421
+ existing_tables = inspector.get_table_names()
422
+ except Exception as inspect_exc:
423
+ print(f"[DB INIT] DB inspection failed (might be normal on new DB): {inspect_exc}")
424
+ existing_tables = []
425
+
426
+ def _col_exists(table_name: str, col_name: str) -> bool:
427
+ if table_name not in existing_tables:
428
+ return False
429
+ try:
430
+ columns = [c["name"] for c in inspector.get_columns(table_name)]
431
+ return col_name in columns
432
+ except Exception:
433
+ return False
434
+
435
+ def _run_migration(table_name: str, col_name: str, sql: str, label: str):
436
+ if _col_exists(table_name, col_name):
437
+ print(f"[DB INIT] Column {table_name}.{col_name} already exists. Skipping.")
438
+ return
439
+
440
+ print(f"[DB INIT] Running migration: {label}")
416
441
  try:
417
442
  with engine.begin() as _conn:
443
+ if is_postgres:
444
+ # Never hang startup for lock queues: fail fast if blocked for > 3s
445
+ _conn.execute(text("SET lock_timeout = 3000"))
418
446
  _conn.execute(text(sql))
447
+ print(f"[DB INIT] Migration OK: {label}")
419
448
  _mig_log.info(f"Migration OK: {label}")
420
449
  except Exception as _exc:
421
- # "already exists" errors are expected and safe to ignore
450
+ print(f"[DB INIT] Migration skipped/failed ({label}): {_exc}")
422
451
  _mig_log.debug(f"Migration skipped ({label}): {_exc}")
423
452
 
424
453
  if is_postgres:
425
- # PostgreSQL supports IF NOT EXISTS fully idempotent
426
- _run_migration(
427
- "ALTER TABLE user_subscriptions ADD COLUMN IF NOT EXISTS refills_processed INTEGER DEFAULT 0 NOT NULL",
428
- "add refills_processed"
429
- )
430
- _run_migration(
431
- "ALTER TABLE user_subscriptions ADD COLUMN IF NOT EXISTS last_refill_at TIMESTAMP",
432
- "add last_refill_at"
433
- )
434
- _run_migration(
435
- "ALTER TABLE user_subscriptions ADD COLUMN IF NOT EXISTS current_cycle_used DOUBLE PRECISION DEFAULT 0.0 NOT NULL",
436
- "add current_cycle_used"
437
- )
438
- _run_migration(
439
- "ALTER TABLE credits ADD COLUMN IF NOT EXISTS bonus_balance DOUBLE PRECISION DEFAULT 0.0 NOT NULL",
440
- "add bonus_balance"
441
- )
442
- _run_migration(
443
- "ALTER TABLE credits ADD COLUMN IF NOT EXISTS bonus_limit DOUBLE PRECISION DEFAULT 0.0 NOT NULL",
444
- "add bonus_limit"
445
- )
446
- _run_migration(
447
- "ALTER TABLE email_tracking ADD COLUMN IF NOT EXISTS low_quota_email_sent_at TIMESTAMP",
448
- "add low_quota_email_sent_at"
449
- )
450
- _run_migration(
451
- "ALTER TABLE email_tracking ADD COLUMN IF NOT EXISTS exhausted_email_sent_at TIMESTAMP",
452
- "add exhausted_email_sent_at"
453
- )
454
- _run_migration(
455
- "ALTER TABLE email_tracking ADD COLUMN IF NOT EXISTS bonus_email_sent_at TIMESTAMP",
456
- "add bonus_email_sent_at"
457
- )
458
- _run_migration(
459
- "ALTER TABLE users ADD COLUMN IF NOT EXISTS last_project_folder TEXT",
460
- "add last_project_folder"
461
- )
454
+ print("[DB INIT] Postgres detected. Bypassing ALTER TABLE migrations (already applied in production).")
462
455
  else:
463
456
  # SQLite: no IF NOT EXISTS on ADD COLUMN, swallow duplicate errors
464
- _run_migration("ALTER TABLE user_subscriptions ADD COLUMN refills_processed INTEGER DEFAULT 0 NOT NULL", "sqlite add refills_processed")
465
- _run_migration("ALTER TABLE user_subscriptions ADD COLUMN last_refill_at TIMESTAMP", "sqlite add last_refill_at")
466
- _run_migration("ALTER TABLE user_subscriptions ADD COLUMN current_cycle_used FLOAT DEFAULT 0.0 NOT NULL", "sqlite add current_cycle_used")
467
- _run_migration("ALTER TABLE credits ADD COLUMN bonus_balance FLOAT DEFAULT 0.0 NOT NULL", "sqlite add bonus_balance")
468
- _run_migration("ALTER TABLE credits ADD COLUMN bonus_limit FLOAT DEFAULT 0.0 NOT NULL", "sqlite add bonus_limit")
469
- _run_migration("ALTER TABLE email_tracking ADD COLUMN low_quota_email_sent_at TIMESTAMP", "sqlite add low_quota_email_sent_at")
470
- _run_migration("ALTER TABLE email_tracking ADD COLUMN exhausted_email_sent_at TIMESTAMP", "sqlite add exhausted_email_sent_at")
471
- _run_migration("ALTER TABLE email_tracking ADD COLUMN bonus_email_sent_at TIMESTAMP", "sqlite add bonus_email_sent_at")
472
- _run_migration("ALTER TABLE users ADD COLUMN last_project_folder TEXT", "sqlite add last_project_folder")
457
+ _run_migration("user_subscriptions", "refills_processed", "ALTER TABLE user_subscriptions ADD COLUMN refills_processed INTEGER DEFAULT 0 NOT NULL", "sqlite add refills_processed")
458
+ _run_migration("user_subscriptions", "last_refill_at", "ALTER TABLE user_subscriptions ADD COLUMN last_refill_at TIMESTAMP", "sqlite add last_refill_at")
459
+ _run_migration("user_subscriptions", "current_cycle_used", "ALTER TABLE user_subscriptions ADD COLUMN current_cycle_used FLOAT DEFAULT 0.0 NOT NULL", "sqlite add current_cycle_used")
460
+ _run_migration("credits", "bonus_balance", "ALTER TABLE credits ADD COLUMN bonus_balance FLOAT DEFAULT 0.0 NOT NULL", "sqlite add bonus_balance")
461
+ _run_migration("credits", "bonus_limit", "ALTER TABLE credits ADD COLUMN bonus_limit FLOAT DEFAULT 0.0 NOT NULL", "sqlite add bonus_limit")
462
+ _run_migration("email_tracking", "low_quota_email_sent_at", "ALTER TABLE email_tracking ADD COLUMN low_quota_email_sent_at TIMESTAMP", "sqlite add low_quota_email_sent_at")
463
+ _run_migration("email_tracking", "exhausted_email_sent_at", "ALTER TABLE email_tracking ADD COLUMN exhausted_email_sent_at TIMESTAMP", "sqlite add exhausted_email_sent_at")
464
+ _run_migration("email_tracking", "bonus_email_sent_at", "ALTER TABLE email_tracking ADD COLUMN bonus_email_sent_at TIMESTAMP", "sqlite add bonus_email_sent_at")
465
+ _run_migration("users", "last_project_folder", "ALTER TABLE users ADD COLUMN last_project_folder TEXT", "sqlite add last_project_folder")
473
466
 
474
467
  # Seed the plans if they don't exist
468
+ print("[DB INIT] Seeding plans checking...")
475
469
  db = SessionLocal()
476
470
  try:
477
471
  free_plan = db.query(Plan).filter(Plan.name == "free").first()
472
+ print(f"[DB INIT] Free plan query done. Exists: {bool(free_plan)}")
478
473
  if not free_plan:
474
+ print("[DB INIT] Creating new plans...")
479
475
  plans = [
480
476
  Plan(
481
477
  id="free",
@@ -594,6 +590,7 @@ def init_db() -> None:
594
590
  pass
595
591
  finally:
596
592
  db.close()
593
+ print("[DB INIT] init_db() completed successfully.")
597
594
 
598
595
 
599
596
  def get_db():
@@ -177,21 +177,25 @@ def _find_changelog_path():
177
177
 
178
178
  @app.get("/health", tags=["system"], summary="Health check")
179
179
  def health():
180
- """Returns 200 when the server and database are reachable."""
180
+ """Always returns 200 so Railway health checks pass.
181
+ DB status is reported in the body for diagnostics.
182
+ """
183
+ db_status = "ok"
184
+ db_error = None
181
185
  try:
182
186
  db = SessionLocal()
183
187
  db.execute(text("SELECT 1"))
184
188
  db.close()
185
- db_status = "ok"
186
189
  except Exception as exc:
187
- logger.error("health_db_error", extra={"error": str(exc)})
190
+ db_error = str(exc)
191
+ logger.error(f"health_db_error: {exc}")
188
192
  db_status = "error"
189
193
 
190
194
  # Derive version dynamically from CHANGELOG.md so a deploy auto-updates it
191
195
  try:
192
196
  import re as _re
193
197
  _cl_path = _find_changelog_path()
194
- _version = "1.44.2"
198
+ _version = "1.44.8"
195
199
  if _cl_path:
196
200
  with open(_cl_path, "r", encoding="utf-8") as _f:
197
201
  for _line in _f:
@@ -200,13 +204,14 @@ def health():
200
204
  _version = _m.group(1)
201
205
  break
202
206
  except Exception:
203
- _version = "1.44.2"
204
-
205
- status_code = 200 if db_status == "ok" else 503
206
- return JSONResponse(
207
- content={"status": "ok" if db_status == "ok" else "degraded", "db": db_status, "version": _version},
208
- status_code=status_code
209
- )
207
+ _version = "1.44.8"
208
+
209
+ # Always return 200 Railway must see 200 or it will keep restarting.
210
+ # DB connectivity errors are surfaced in the body for debugging.
211
+ body = {"status": "ok" if db_status == "ok" else "degraded", "db": db_status, "version": _version}
212
+ if db_error:
213
+ body["db_error"] = db_error
214
+ return JSONResponse(content=body, status_code=200)
210
215
 
211
216
 
212
217
  @app.get("/models", tags=["system"], summary="List available LLM models")
@@ -0,0 +1,56 @@
1
+ """
2
+ UTIM Production Server — Entry Point
3
+ Run locally: python -m utim_cli.server.server
4
+ Railway runs: uvicorn utim_cli.server.router:app --host 0.0.0.0 --port $PORT
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import sys
9
+ import os
10
+
11
+ # Ensure parent and grandparent directories are in sys.path to import 'utim_cli'
12
+ _dir = os.path.dirname(os.path.abspath(__file__))
13
+ for _pd in [os.path.dirname(os.path.dirname(_dir)), os.path.dirname(_dir)]:
14
+ if _pd and _pd not in sys.path:
15
+ sys.path.insert(0, _pd)
16
+
17
+ import uvicorn
18
+
19
+
20
+ def main():
21
+ host = os.environ.get("HOST", "0.0.0.0")
22
+ port = int(os.environ.get("PORT", 8080))
23
+
24
+ # Warn about missing env vars but DO NOT exit — server must start
25
+ required = {
26
+ "DATABASE_URL": os.environ.get("DATABASE_URL"),
27
+ "OPENROUTER_API_KEY": os.environ.get("OPENROUTER_API_KEY"),
28
+ "UTIM_MASTER_KEY": os.environ.get("UTIM_MASTER_KEY"),
29
+ }
30
+ missing = [k for k, v in required.items() if not v]
31
+ if missing:
32
+ print(f"[WARNING] Missing env vars: {', '.join(missing)} — some features will be degraded.")
33
+ print(" Set these in Railway → Service → Variables")
34
+
35
+ print(f"🚀 UTIM Production Server")
36
+ print(f" http://{host}:{port}")
37
+ print(f" Docs: http://{host}:{port}/docs")
38
+
39
+ try:
40
+ uvicorn.run(
41
+ "utim_cli.server.router:app",
42
+ host=host,
43
+ port=port,
44
+ reload=False,
45
+ workers=1,
46
+ # Do NOT pass log_config=None — keep default logging so errors are visible
47
+ )
48
+ except Exception as exc:
49
+ print(f"[FATAL] uvicorn failed to start: {exc}", file=sys.stderr)
50
+ import traceback
51
+ traceback.print_exc()
52
+ sys.exit(1)
53
+
54
+
55
+ if __name__ == "__main__":
56
+ main()
@@ -585,7 +585,7 @@ def _print_animated_banner():
585
585
  # Speed up for spaces to make it feel more like typing
586
586
  time.sleep(0.002 if char == " " else 0.005)
587
587
 
588
- console.print(f"\n [bold white]U Think I Make[/bold white] [dim]v1.44.8[/dim]")
588
+ console.print(f"\n [bold white]U Think I Make[/bold white] [dim]v1.45.1[/dim]")
589
589
  user_email = config.email
590
590
  user_type = "UTIM Community"
591
591
  api_key = config.get("api_key")
@@ -2691,7 +2691,7 @@ def check_and_update_background():
2691
2691
  return
2692
2692
 
2693
2693
  latest_ver = resp.json().get("version", "")
2694
- current_ver = "1.44.8"
2694
+ current_ver = "1.45.1"
2695
2695
 
2696
2696
  def parse_ver(v):
2697
2697
  return [int(x) for x in v.split(".") if x.isdigit()]
@@ -2887,7 +2887,7 @@ app = typer.Typer()
2887
2887
  def version_callback(value: bool):
2888
2888
  if value:
2889
2889
  import typer
2890
- print("U Think I Make v1.44.8")
2890
+ print("U Think I Make v1.45.1")
2891
2891
  raise typer.Exit()
2892
2892
 
2893
2893
  @app.callback(invoke_without_command=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: utim-cli
3
- Version: 1.44.8
3
+ Version: 1.45.1
4
4
  Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://utim.dev
@@ -1,56 +0,0 @@
1
- """
2
- UTIM Production Server — Entry Point
3
- Run locally: python -m utim_cli.server.server
4
- Railway runs: uvicorn utim_cli.server.router:app --host 0.0.0.0 --port $PORT
5
- """
6
- from __future__ import annotations
7
-
8
- import sys
9
- import os
10
-
11
- # Ensure parent and grandparent directories are in sys.path to import 'utim_cli'
12
- _dir = os.path.dirname(os.path.abspath(__file__))
13
- for _pd in [os.path.dirname(os.path.dirname(_dir)), os.path.dirname(_dir)]:
14
- if _pd and _pd not in sys.path:
15
- sys.path.insert(0, _pd)
16
-
17
- import argparse
18
- import uvicorn
19
-
20
-
21
- def main():
22
- parser = argparse.ArgumentParser(description="Start the UTIM Production Server")
23
- parser.add_argument("--host", default="0.0.0.0")
24
- parser.add_argument("--port", type=int, default=int(os.environ.get("PORT", 8000)))
25
- parser.add_argument("--reload", action="store_true")
26
- parser.add_argument("--workers", type=int, default=1)
27
- args = parser.parse_args()
28
-
29
- required = {
30
- "DATABASE_URL": os.environ.get("DATABASE_URL"),
31
- "OPENROUTER_API_KEY": os.environ.get("OPENROUTER_API_KEY"),
32
- "UTIM_MASTER_KEY": os.environ.get("UTIM_MASTER_KEY"),
33
- }
34
- missing = [k for k, v in required.items() if not v]
35
- if missing:
36
- print(f"[ERROR] Missing required env vars: {', '.join(missing)}")
37
- print(" Set these in Railway → Service → Variables")
38
- sys.exit(1)
39
-
40
- print(f"🚀 UTIM Production Server")
41
- print(f" http://{args.host}:{args.port}")
42
- print(f" Docs: http://{args.host}:{args.port}/docs")
43
-
44
- uvicorn.run(
45
- "utim_cli.server.router:app",
46
- host=args.host,
47
- port=args.port,
48
- reload=args.reload,
49
- workers=args.workers if not args.reload else 1,
50
- log_config=None, # we handle logging ourselves
51
- access_log=False, # our middleware does structured access logs
52
- )
53
-
54
-
55
- if __name__ == "__main__":
56
- main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes