qalita 2.6.3__py3-none-any.whl → 2.8.0__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.
Files changed (136) hide show
  1. qalita/__main__.py +29 -19
  2. qalita/_frontend/.next/BUILD_ID +1 -1
  3. qalita/_frontend/.next/build-manifest.json +7 -7
  4. qalita/_frontend/.next/prerender-manifest.json +3 -3
  5. qalita/_frontend/.next/server/app/_global-error/page/build-manifest.json +5 -5
  6. qalita/_frontend/.next/server/app/_global-error/page.js +1 -1
  7. qalita/_frontend/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. qalita/_frontend/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. qalita/_frontend/.next/server/app/_global-error.html +2 -2
  10. qalita/_frontend/.next/server/app/_global-error.rsc +7 -7
  11. qalita/_frontend/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  12. qalita/_frontend/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. qalita/_frontend/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  14. qalita/_frontend/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  15. qalita/_frontend/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. qalita/_frontend/.next/server/app/_not-found/page/build-manifest.json +5 -5
  17. qalita/_frontend/.next/server/app/_not-found/page.js +1 -1
  18. qalita/_frontend/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. qalita/_frontend/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. qalita/_frontend/.next/server/app/_not-found.html +1 -1
  21. qalita/_frontend/.next/server/app/_not-found.rsc +14 -12
  22. qalita/_frontend/.next/server/app/_not-found.segments/_full.segment.rsc +14 -12
  23. qalita/_frontend/.next/server/app/_not-found.segments/_head.segment.rsc +3 -3
  24. qalita/_frontend/.next/server/app/_not-found.segments/_index.segment.rsc +6 -4
  25. qalita/_frontend/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  26. qalita/_frontend/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  27. qalita/_frontend/.next/server/app/_not-found.segments/_tree.segment.rsc +3 -2
  28. qalita/_frontend/.next/server/app/page/build-manifest.json +5 -5
  29. qalita/_frontend/.next/server/app/page.js +1 -1
  30. qalita/_frontend/.next/server/app/page.js.nft.json +1 -1
  31. qalita/_frontend/.next/server/app/page_client-reference-manifest.js +1 -1
  32. qalita/_frontend/.next/server/app/sources/add/page/build-manifest.json +5 -5
  33. qalita/_frontend/.next/server/app/sources/add/page.js +1 -1
  34. qalita/_frontend/.next/server/app/sources/add/page.js.nft.json +1 -1
  35. qalita/_frontend/.next/server/app/sources/add/page_client-reference-manifest.js +1 -1
  36. qalita/_frontend/.next/server/app/sources/add.html +1 -1
  37. qalita/_frontend/.next/server/app/sources/add.rsc +18 -16
  38. qalita/_frontend/.next/server/app/sources/add.segments/_full.segment.rsc +18 -16
  39. qalita/_frontend/.next/server/app/sources/add.segments/_head.segment.rsc +3 -3
  40. qalita/_frontend/.next/server/app/sources/add.segments/_index.segment.rsc +6 -4
  41. qalita/_frontend/.next/server/app/sources/add.segments/_tree.segment.rsc +3 -2
  42. qalita/_frontend/.next/server/app/sources/add.segments/sources/add/__PAGE__.segment.rsc +4 -4
  43. qalita/_frontend/.next/server/app/sources/add.segments/sources/add.segment.rsc +3 -3
  44. qalita/_frontend/.next/server/app/sources/add.segments/sources.segment.rsc +3 -3
  45. qalita/_frontend/.next/server/app/sources/edit/[id]/page/build-manifest.json +5 -5
  46. qalita/_frontend/.next/server/app/sources/edit/[id]/page.js +1 -1
  47. qalita/_frontend/.next/server/app/sources/edit/[id]/page.js.nft.json +1 -1
  48. qalita/_frontend/.next/server/app/sources/edit/[id]/page_client-reference-manifest.js +1 -1
  49. qalita/_frontend/.next/server/app/sources/page/build-manifest.json +5 -5
  50. qalita/_frontend/.next/server/app/sources/page.js +1 -1
  51. qalita/_frontend/.next/server/app/sources/page.js.nft.json +1 -1
  52. qalita/_frontend/.next/server/app/sources/page_client-reference-manifest.js +1 -1
  53. qalita/_frontend/.next/server/app/sources.html +1 -1
  54. qalita/_frontend/.next/server/app/sources.rsc +18 -16
  55. qalita/_frontend/.next/server/app/sources.segments/_full.segment.rsc +18 -16
  56. qalita/_frontend/.next/server/app/sources.segments/_head.segment.rsc +3 -3
  57. qalita/_frontend/.next/server/app/sources.segments/_index.segment.rsc +6 -4
  58. qalita/_frontend/.next/server/app/sources.segments/_tree.segment.rsc +3 -2
  59. qalita/_frontend/.next/server/app/sources.segments/sources/__PAGE__.segment.rsc +4 -4
  60. qalita/_frontend/.next/server/app/sources.segments/sources.segment.rsc +3 -3
  61. qalita/_frontend/.next/server/chunks/[root-of-the-server]__e868c9e1._.js +1 -1
  62. qalita/_frontend/.next/server/chunks/[root-of-the-server]__ebaae723._.js +1 -1
  63. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__17f2c9b6._.js +1 -1
  64. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__1d5b5394._.js +3 -0
  65. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__21824174._.js +3 -0
  66. qalita/_frontend/.next/server/chunks/ssr/{[root-of-the-server]__b9356576._.js → [root-of-the-server]__336e4c46._.js} +2 -2
  67. qalita/_frontend/.next/server/chunks/ssr/{[root-of-the-server]__c507bbfe._.js → [root-of-the-server]__7876511a._.js} +2 -2
  68. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__be91267c._.js +3 -0
  69. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__d15765f1._.js +3 -0
  70. qalita/_frontend/.next/server/chunks/ssr/{_3b4a232c._.js → _404f6e81._.js} +4 -4
  71. qalita/_frontend/.next/server/chunks/ssr/{_cd257a0c._.js → _6a67f6f0._.js} +4 -4
  72. qalita/_frontend/.next/server/chunks/ssr/_cafb65ac._.js +3 -0
  73. qalita/_frontend/.next/server/chunks/ssr/_cb7b44d6._.js +1 -1
  74. qalita/_frontend/.next/server/chunks/ssr/_d44c43ed._.js +3 -0
  75. qalita/_frontend/.next/server/chunks/ssr/components_DashboardContent_tsx_c3635665._.js +1 -1
  76. qalita/_frontend/.next/server/chunks/ssr/node_modules_next_dist_server_route-modules_app-page_vendored_a443a6bf._.js +3 -0
  77. qalita/_frontend/.next/server/middleware-build-manifest.js +5 -5
  78. qalita/_frontend/.next/server/pages/404.html +1 -1
  79. qalita/_frontend/.next/server/pages/500.html +2 -2
  80. qalita/_frontend/.next/server/server-reference-manifest.js +1 -1
  81. qalita/_frontend/.next/server/server-reference-manifest.json +1 -1
  82. qalita/_frontend/.next/static/chunks/02a64570f0a14789.js +1 -0
  83. qalita/_frontend/.next/static/chunks/{7340adf74ff47ec0.js → 0b082245f106d665.js} +1 -1
  84. qalita/_frontend/.next/static/chunks/27b3ba70c7ef50a8.js +1 -0
  85. qalita/_frontend/.next/static/chunks/517e9b74d1a3c0ce.js +1 -0
  86. qalita/_frontend/.next/static/chunks/58689c96b0676c41.js +1 -0
  87. qalita/_frontend/.next/static/chunks/{236f7e5abd6f09ff.js → 89ba62a8ba9b79ce.js} +2 -2
  88. qalita/_frontend/.next/static/chunks/acc5da18ff20daa1.js +3 -0
  89. qalita/_frontend/.next/static/chunks/bdc8a8e7721f5675.js +2 -0
  90. qalita/_frontend/.next/static/chunks/e0df86cbf44bbf9f.js +1 -0
  91. qalita/_frontend/.next/static/chunks/e4c3a252774ab7fd.css +1 -0
  92. qalita/_frontend/.next/static/chunks/e6ce59ba40b863f2.js +1 -0
  93. qalita/_frontend/.next/static/chunks/{30ea11065999f7ac.js → ec4b1f1e3cd3ae43.js} +1 -1
  94. qalita/_frontend/.next/static/chunks/{turbopack-25186fc8e1264445.js → turbopack-d21156d03715fafa.js} +1 -1
  95. qalita/_frontend/node_modules/@swc/helpers/package.json +225 -2
  96. qalita/_frontend/node_modules/next/node_modules/@swc/helpers/package.json +471 -0
  97. qalita/_frontend/package.json +12 -1
  98. qalita/commands/pack.py +61 -8
  99. qalita/commands/worker.py +46 -20
  100. qalita/commands/worker_grpc.py +941 -0
  101. qalita/grpc/__init__.py +8 -0
  102. qalita/grpc/client.py +693 -0
  103. qalita/grpc/protos/__init__.py +4 -0
  104. qalita/grpc/protos/qalita.proto +391 -0
  105. qalita/grpc/protos/qalita_pb2.py +112 -0
  106. qalita/grpc/protos/qalita_pb2_grpc.py +588 -0
  107. qalita/internal/data_preview.py +565 -0
  108. qalita/internal/request.py +2 -1
  109. qalita/internal/utils.py +1 -1
  110. qalita/web/app.py +6 -2
  111. qalita/web/blueprints/dashboard.py +12 -44
  112. qalita/web/blueprints/helpers.py +119 -46
  113. qalita/web/blueprints/sources.py +5 -99
  114. qalita/web/blueprints/workers.py +6 -6
  115. {qalita-2.6.3.dist-info → qalita-2.8.0.dist-info}/METADATA +7 -1
  116. {qalita-2.6.3.dist-info → qalita-2.8.0.dist-info}/RECORD +124 -111
  117. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__345b6cae._.js +0 -3
  118. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__7213ba1d._.js +0 -3
  119. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__9130e1f5._.js +0 -3
  120. qalita/_frontend/.next/server/chunks/ssr/[root-of-the-server]__e2a7729d._.js +0 -3
  121. qalita/_frontend/.next/server/chunks/ssr/app_layout_tsx_271801d7._.js +0 -3
  122. qalita/_frontend/.next/static/chunks/0f84739db4a8acc7.js +0 -1
  123. qalita/_frontend/.next/static/chunks/1107bdca1eff6d34.css +0 -1
  124. qalita/_frontend/.next/static/chunks/4b0c5de8d4cc313f.js +0 -1
  125. qalita/_frontend/.next/static/chunks/4dd28bc3f722184a.js +0 -2
  126. qalita/_frontend/.next/static/chunks/711d597b816a80c1.js +0 -1
  127. qalita/_frontend/.next/static/chunks/bb29c2be4df20a40.js +0 -1
  128. qalita/_frontend/.next/static/chunks/ecf559101be0ae12.js +0 -3
  129. /qalita/_frontend/.next/static/{N9MqNrf23ZZkbbSW2aXkt → oDJBrlQBPl3vggds1RNfL}/_buildManifest.js +0 -0
  130. /qalita/_frontend/.next/static/{N9MqNrf23ZZkbbSW2aXkt → oDJBrlQBPl3vggds1RNfL}/_clientMiddlewareManifest.json +0 -0
  131. /qalita/_frontend/.next/static/{N9MqNrf23ZZkbbSW2aXkt → oDJBrlQBPl3vggds1RNfL}/_ssgManifest.js +0 -0
  132. /qalita/_frontend/node_modules/{@swc → next/node_modules/@swc}/helpers/cjs/_interop_require_default.cjs +0 -0
  133. /qalita/_frontend/node_modules/{@swc → next/node_modules/@swc}/helpers/cjs/_interop_require_wildcard.cjs +0 -0
  134. {qalita-2.6.3.dist-info → qalita-2.8.0.dist-info}/WHEEL +0 -0
  135. {qalita-2.6.3.dist-info → qalita-2.8.0.dist-info}/entry_points.txt +0 -0
  136. {qalita-2.6.3.dist-info → qalita-2.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,12 +4,10 @@
4
4
 
5
5
  from flask import Blueprint, current_app, request, jsonify
6
6
 
7
- from qalita.internal.request import send_request
8
7
  from qalita.internal.utils import logger
9
8
  from .helpers import (
10
- compute_agent_summary,
11
- read_selected_env,
12
- parse_env_file,
9
+ compute_worker_summary,
10
+ get_platform_url,
13
11
  )
14
12
 
15
13
 
@@ -33,46 +31,16 @@ def dashboard_api():
33
31
  def _get_dashboard_data_json():
34
32
  """Get dashboard data as JSON"""
35
33
  cfg = current_app.config["QALITA_CONFIG_OBJ"]
36
- agent_conf, agent_runs = compute_agent_summary(cfg)
37
- # Load local sources regardless of agent configuration
34
+ worker_conf, worker_runs = compute_worker_summary(cfg)
35
+ # Load local sources regardless of worker configuration
38
36
  try:
39
37
  cfg.load_source_config()
40
38
  sources = list(reversed(cfg.config.get("sources", [])))
41
39
  except Exception:
42
40
  sources = []
43
41
 
44
- # Resolve public platform URL
45
- platform_url = None
46
- try:
47
- backend_url = getattr(cfg, "url", None)
48
- try:
49
- env_path = read_selected_env()
50
- if env_path:
51
- data = parse_env_file(env_path)
52
- backend_url = (
53
- data.get("QALITA_AGENT_ENDPOINT")
54
- or data.get("QALITA_URL")
55
- or data.get("URL")
56
- or backend_url
57
- )
58
- except Exception:
59
- pass
60
- if backend_url:
61
- try:
62
- r = send_request.__wrapped__(
63
- cfg, request=f"{backend_url}/api/v1/info", mode="get"
64
- ) # type: ignore[attr-defined]
65
- except Exception:
66
- r = None
67
- if r is not None and getattr(r, "status_code", None) == 200:
68
- try:
69
- platform_url = (r.json() or {}).get("public_platform_url")
70
- except Exception:
71
- platform_url = None
72
- except Exception:
73
- platform_url = None
74
- if isinstance(platform_url, str):
75
- platform_url = platform_url.rstrip("/")
42
+ # Resolve public platform URL using centralized helper
43
+ platform_url = get_platform_url()
76
44
 
77
45
  # Pagination for runs
78
46
  try:
@@ -87,20 +55,20 @@ def _get_dashboard_data_json():
87
55
  page = 1
88
56
  if per_page <= 0:
89
57
  per_page = 10
90
- total_runs = len(agent_runs)
58
+ total_runs = len(worker_runs)
91
59
  start_idx = (page - 1) * per_page
92
60
  end_idx = start_idx + per_page
93
- agent_runs_page = agent_runs[start_idx:end_idx]
61
+ worker_runs_page = worker_runs[start_idx:end_idx]
94
62
  runs_has_prev = start_idx > 0
95
63
  runs_has_next = end_idx < total_runs
96
64
  runs_start = (start_idx + 1) if total_runs > 0 and start_idx < total_runs else 0
97
65
  runs_end = min(end_idx, total_runs) if total_runs > 0 else 0
98
66
 
99
67
  return jsonify({
100
- "agent_conf": agent_conf or {},
68
+ "worker_conf": worker_conf or {},
101
69
  "sources": sources,
102
- "agent_runs": agent_runs,
103
- "agent_runs_page": agent_runs_page,
70
+ "worker_runs": worker_runs,
71
+ "worker_runs_page": worker_runs_page,
104
72
  "runs_total": total_runs,
105
73
  "runs_page": page,
106
74
  "runs_per_page": per_page,
@@ -122,7 +90,7 @@ def validate_sources():
122
90
  from qalita.commands.source import validate_source as _validate
123
91
 
124
92
  cfg = current_app.config["QALITA_CONFIG_OBJ"]
125
- # Run validation (graceful if agent not configured)
93
+ # Run validation (graceful if worker not configured)
126
94
  try:
127
95
  try:
128
96
  _validate.__wrapped__(cfg) # type: ignore[attr-defined]
@@ -164,8 +164,8 @@ def ensure_default_env_selected(pointer_path: str):
164
164
  except Exception:
165
165
  name = None
166
166
  if not name:
167
- name = "agent"
168
- safe = re.sub(r"[^A-Za-z0-9._-]+", "-", str(name)).strip("-_.") or "agent"
167
+ name = "worker"
168
+ safe = re.sub(r"[^A-Za-z0-9._-]+", "-", str(name)).strip("-_.") or "worker"
169
169
  target = os.path.normpath(os.path.join(base, f".env-{safe}"))
170
170
  os.makedirs(base, exist_ok=True)
171
171
  materialize_env_from_process_env(target)
@@ -264,12 +264,12 @@ def list_env_files():
264
264
  return files
265
265
 
266
266
 
267
- def agent_pid_file_path():
268
- return os.path.join(qalita_home(), "agent_run.pid")
267
+ def worker_pid_file_path():
268
+ return os.path.join(qalita_home(), "worker_run.pid")
269
269
 
270
270
 
271
- def read_agent_pid():
272
- p = agent_pid_file_path()
271
+ def read_worker_pid():
272
+ p = worker_pid_file_path()
273
273
  try:
274
274
  if os.path.isfile(p):
275
275
  with open(p, "r", encoding="utf-8") as f:
@@ -280,8 +280,8 @@ def read_agent_pid():
280
280
  return None
281
281
 
282
282
 
283
- def agent_log_file_path():
284
- return os.path.join(qalita_home(), "agent_run.log")
283
+ def worker_log_file_path():
284
+ return os.path.join(qalita_home(), "worker_run.log")
285
285
 
286
286
 
287
287
  def is_pid_running(pid: int) -> bool:
@@ -306,8 +306,8 @@ def is_pid_running(pid: int) -> bool:
306
306
  return False
307
307
 
308
308
 
309
- def agent_status_payload() -> dict:
310
- pid = read_agent_pid()
309
+ def worker_status_payload() -> dict:
310
+ pid = read_worker_pid()
311
311
  running = bool(pid) and is_pid_running(int(pid))
312
312
  return {"running": running, "pid": int(pid) if running else None}
313
313
 
@@ -348,10 +348,10 @@ def open_path_in_file_explorer(target_path: str) -> tuple[bool, str]:
348
348
  return False, "none"
349
349
 
350
350
 
351
- def compute_agent_summary(cfg):
352
- agent_conf = None
351
+ def compute_worker_summary(cfg):
352
+ worker_conf = None
353
353
  try:
354
- raw = cfg.load_agent_config()
354
+ raw = cfg.load_worker_config()
355
355
  if isinstance(raw, dict) and raw:
356
356
 
357
357
  def pick(obj, *path):
@@ -362,66 +362,66 @@ def compute_agent_summary(cfg):
362
362
  cur = cur[key]
363
363
  return cur
364
364
 
365
- agent_conf = {
365
+ worker_conf = {
366
366
  "name": pick(raw, "context", "remote", "name") or raw.get("name", ""),
367
367
  "mode": pick(raw, "context", "local", "mode") or raw.get("mode", ""),
368
368
  "url": pick(raw, "context", "local", "url") or raw.get("url", ""),
369
- "agent_id": pick(raw, "context", "remote", "id")
370
- or raw.get("agent_id", ""),
369
+ "worker_id": pick(raw, "context", "remote", "id")
370
+ or raw.get("worker_id", ""),
371
371
  }
372
372
  else:
373
- agent_conf = None
373
+ worker_conf = None
374
374
  except SystemExit:
375
- # Gracefully handle missing/invalid .agent in web requests
376
- agent_conf = None
375
+ # Gracefully handle missing/invalid .worker in web requests
376
+ worker_conf = None
377
377
  except Exception:
378
- agent_conf = None
378
+ worker_conf = None
379
379
  # Overlay with selected context values
380
380
  try:
381
381
  env_path = read_selected_env()
382
382
  if env_path:
383
383
  data = parse_env_file(env_path)
384
- if agent_conf is None:
385
- agent_conf = {}
386
- agent_conf["name"] = (
384
+ if worker_conf is None:
385
+ worker_conf = {}
386
+ worker_conf["name"] = (
387
387
  data.get("QALITA_WORKER_NAME")
388
388
  or data.get("QALITA_AGENT_NAME")
389
389
  or data.get("AGENT_NAME")
390
390
  or data.get("NAME")
391
- or agent_conf.get("name", "")
391
+ or worker_conf.get("name", "")
392
392
  )
393
- agent_conf["mode"] = (
393
+ worker_conf["mode"] = (
394
394
  data.get("QALITA_WORKER_MODE")
395
395
  or data.get("QALITA_AGENT_MODE")
396
396
  or data.get("AGENT_MODE")
397
397
  or data.get("MODE")
398
- or agent_conf.get("mode", "")
398
+ or worker_conf.get("mode", "")
399
399
  )
400
- agent_conf["url"] = (
400
+ worker_conf["url"] = (
401
401
  data.get("QALITA_WORKER_ENDPOINT")
402
402
  or data.get("QALITA_AGENT_ENDPOINT")
403
403
  or data.get("QALITA_URL")
404
404
  or data.get("URL")
405
- or agent_conf.get("url", "")
405
+ or worker_conf.get("url", "")
406
406
  )
407
407
  except Exception:
408
408
  pass
409
409
  # Final overlay from live cfg values
410
410
  try:
411
- if agent_conf is None:
412
- agent_conf = {}
413
- if not agent_conf.get("name"):
414
- agent_conf["name"] = getattr(cfg, "name", "") or agent_conf.get("name", "")
415
- if not agent_conf.get("mode"):
416
- agent_conf["mode"] = getattr(cfg, "mode", "") or agent_conf.get("mode", "")
417
- if not agent_conf.get("url"):
418
- agent_conf["url"] = getattr(cfg, "url", "") or agent_conf.get("url", "")
411
+ if worker_conf is None:
412
+ worker_conf = {}
413
+ if not worker_conf.get("name"):
414
+ worker_conf["name"] = getattr(cfg, "name", "") or worker_conf.get("name", "")
415
+ if not worker_conf.get("mode"):
416
+ worker_conf["mode"] = getattr(cfg, "mode", "") or worker_conf.get("mode", "")
417
+ if not worker_conf.get("url"):
418
+ worker_conf["url"] = getattr(cfg, "url", "") or worker_conf.get("url", "")
419
419
  except Exception:
420
420
  pass
421
- # Build agent runs
422
- agent_runs = []
421
+ # Build worker runs
422
+ worker_runs = []
423
423
  try:
424
- run_root = cfg.get_agent_run_path()
424
+ run_root = cfg.get_worker_run_path()
425
425
  if os.path.isdir(run_root):
426
426
  pattern = re.compile(r"^\d{14}_[a-z0-9]{5}$")
427
427
  for entry in sorted(os.listdir(run_root), reverse=True):
@@ -435,7 +435,7 @@ def compute_agent_summary(cfg):
435
435
  )
436
436
  except Exception:
437
437
  when = ts
438
- agent_runs.append(
438
+ worker_runs.append(
439
439
  {
440
440
  "name": entry,
441
441
  "path": os.path.join(run_root, entry),
@@ -444,12 +444,85 @@ def compute_agent_summary(cfg):
444
444
  }
445
445
  )
446
446
  except Exception:
447
- agent_runs = []
448
- return agent_conf, agent_runs
447
+ worker_runs = []
448
+ return worker_conf, worker_runs
449
+
450
+
451
+ def get_backend_url() -> str | None:
452
+ """Get the current backend URL from the selected context.
453
+
454
+ Reads from the selected .env file, falling back to the config object.
455
+ This ensures consistency with compute_worker_summary().
456
+
457
+ Returns:
458
+ The backend URL or None if not configured.
459
+ """
460
+ backend_url = None
461
+ try:
462
+ cfg = current_app.config.get("QALITA_CONFIG_OBJ")
463
+ backend_url = getattr(cfg, "url", None)
464
+ except Exception:
465
+ pass
466
+
467
+ # Override from selected .env context
468
+ try:
469
+ env_path = read_selected_env()
470
+ if env_path:
471
+ data = parse_env_file(env_path)
472
+ backend_url = (
473
+ data.get("QALITA_WORKER_ENDPOINT")
474
+ or data.get("QALITA_AGENT_ENDPOINT")
475
+ or data.get("QALITA_URL")
476
+ or data.get("URL")
477
+ or backend_url
478
+ )
479
+ except Exception:
480
+ pass
481
+
482
+ return backend_url
483
+
484
+
485
+ def get_platform_url() -> str | None:
486
+ """Get the public platform URL for the current context.
487
+
488
+ This is the single source of truth for resolving platform_url.
489
+ It fetches from the backend's /api/v1/info endpoint using the
490
+ current context's backend URL.
491
+
492
+ Returns:
493
+ The public platform URL (without trailing slash) or None.
494
+ """
495
+ from qalita.internal.request import send_request
496
+
497
+ platform_url = None
498
+ try:
499
+ backend_url = get_backend_url()
500
+ if backend_url:
501
+ cfg = current_app.config.get("QALITA_CONFIG_OBJ")
502
+ try:
503
+ r = send_request.__wrapped__(
504
+ cfg, request=f"{backend_url}/api/v1/info", mode="get"
505
+ ) # type: ignore[attr-defined]
506
+ except Exception:
507
+ r = None
508
+ if r is not None and getattr(r, "status_code", None) == 200:
509
+ try:
510
+ platform_url = (r.json() or {}).get("public_platform_url")
511
+ except Exception:
512
+ platform_url = None
513
+ except Exception:
514
+ platform_url = None
515
+
516
+ # Normalize to avoid double slashes when building links
517
+ if isinstance(platform_url, str):
518
+ platform_url = platform_url.rstrip("/")
519
+
520
+ return platform_url
449
521
 
450
522
 
451
523
  # Aliases for backward compatibility (agent -> worker refactoring)
452
- worker_pid_file_path = agent_pid_file_path
453
- worker_log_file_path = agent_log_file_path
454
- worker_status_payload = agent_status_payload
455
- compute_worker_summary = compute_agent_summary
524
+ agent_pid_file_path = worker_pid_file_path
525
+ agent_log_file_path = worker_log_file_path
526
+ agent_status_payload = worker_status_payload
527
+ compute_agent_summary = compute_worker_summary
528
+ read_agent_pid = read_worker_pid
@@ -16,12 +16,12 @@ from flask import (
16
16
  request,
17
17
  jsonify,
18
18
  )
19
- from qalita.internal.request import send_request
20
19
  from qalita.commands.source import (
21
20
  validate_source_object,
22
21
  validate_source as _validate_all,
23
22
  push_single_programmatic,
24
23
  )
24
+ from .helpers import get_platform_url
25
25
 
26
26
 
27
27
  bp = Blueprint("sources", __name__)
@@ -33,57 +33,8 @@ def list_sources():
33
33
  cfg.load_source_config()
34
34
  sources = cfg.config.get("sources", [])
35
35
 
36
- # Resolve public platform URL from backend /api/v1/info using the agent backend URL
37
- platform_url = None
38
- try:
39
- backend_url = getattr(cfg, "url", None)
40
- # Try override from selected .env context without loading agent config (avoid SystemExit)
41
- try:
42
- env_path_file = os.path.join(
43
- getattr(cfg, "qalita_home", os.path.expanduser("~/.qalita")),
44
- ".current_env",
45
- )
46
- if os.path.isfile(env_path_file):
47
- with open(env_path_file, "r", encoding="utf-8") as f:
48
- sel = f.read().strip()
49
- if sel and os.path.isfile(sel):
50
- with open(sel, "r", encoding="utf-8") as ef:
51
- for line in ef.readlines():
52
- line = line.strip()
53
- if not line or line.startswith("#") or "=" not in line:
54
- continue
55
- k, v = line.split("=", 1)
56
- k = k.strip().upper()
57
- v = v.strip().strip('"').strip("'")
58
- if k in (
59
- "QALITA_AGENT_ENDPOINT",
60
- "AGENT_ENDPOINT",
61
- "QALITA_URL",
62
- "URL",
63
- ):
64
- backend_url = v
65
- break
66
- except Exception:
67
- pass
68
-
69
- if backend_url:
70
- try:
71
- r = send_request.__wrapped__(
72
- cfg, request=f"{backend_url}/api/v1/info", mode="get"
73
- ) # type: ignore[attr-defined]
74
- except Exception:
75
- r = None
76
- if r is not None and getattr(r, "status_code", None) == 200:
77
- try:
78
- platform_url = (r.json() or {}).get("public_platform_url")
79
- except Exception:
80
- platform_url = None
81
- except Exception:
82
- platform_url = None
83
-
84
- # Normalize platform_url to avoid double slashes when building links
85
- if isinstance(platform_url, str):
86
- platform_url = platform_url.rstrip("/")
36
+ # Resolve public platform URL using centralized helper
37
+ platform_url = get_platform_url()
87
38
 
88
39
  return jsonify({
89
40
  "sources": sources,
@@ -237,53 +188,8 @@ def add_source_post():
237
188
  # Skip push when not logged in / no .agent
238
189
  pass
239
190
 
240
- # Resolve platform_url as in list_sources
241
- platform_url = None
242
- try:
243
- backend_url = getattr(cfg, "url", None)
244
- try:
245
- env_path_file = os.path.join(
246
- getattr(cfg, "qalita_home", os.path.expanduser("~/.qalita")),
247
- ".current_env",
248
- )
249
- if os.path.isfile(env_path_file):
250
- with open(env_path_file, "r", encoding="utf-8") as f:
251
- sel = f.read().strip()
252
- if sel and os.path.isfile(sel):
253
- with open(sel, "r", encoding="utf-8") as ef:
254
- for line in ef.readlines():
255
- line = line.strip()
256
- if not line or line.startswith("#") or "=" not in line:
257
- continue
258
- k, v = line.split("=", 1)
259
- k = k.strip().upper()
260
- v = v.strip().strip('"').strip("'")
261
- if k in (
262
- "QALITA_AGENT_ENDPOINT",
263
- "AGENT_ENDPOINT",
264
- "QALITA_URL",
265
- "URL",
266
- ):
267
- backend_url = v
268
- break
269
- except Exception:
270
- pass
271
- if backend_url:
272
- try:
273
- r = send_request.__wrapped__(
274
- cfg, request=f"{backend_url}/api/v1/info", mode="get"
275
- ) # type: ignore[attr-defined]
276
- except Exception:
277
- r = None
278
- if r is not None and getattr(r, "status_code", None) == 200:
279
- try:
280
- platform_url = (r.json() or {}).get("public_platform_url")
281
- except Exception:
282
- platform_url = None
283
- except Exception:
284
- platform_url = None
285
- if isinstance(platform_url, str):
286
- platform_url = platform_url.rstrip("/")
191
+ # Resolve platform_url using centralized helper
192
+ platform_url = get_platform_url()
287
193
 
288
194
  # Try to get the created source id (if present after validate/push)
289
195
  src_id = None
@@ -78,7 +78,7 @@ def worker_start():
78
78
  except Exception:
79
79
  worker_name = None
80
80
  if not worker_name:
81
- worker_name = "agent"
81
+ worker_name = "worker"
82
82
 
83
83
  # Ensure we have minimum credentials before preflight
84
84
  if not getattr(cfg, "token", None) or not getattr(cfg, "url", None):
@@ -166,7 +166,7 @@ def worker_start():
166
166
  except Exception:
167
167
  env["QALITA_WORKER_MODE"] = "worker"
168
168
  logger.info(
169
- f"worker_start: effective agent mode is [{env.get('QALITA_WORKER_MODE') or env.get('AGENT_MODE') or env.get('MODE')}]"
169
+ f"worker_start: effective worker mode is [{env.get('QALITA_WORKER_MODE') or env.get('AGENT_MODE') or env.get('MODE')}]"
170
170
  )
171
171
  if os.name == "nt":
172
172
  python_bin = sys.executable or "python"
@@ -248,7 +248,7 @@ def worker_start():
248
248
  except Exception:
249
249
  env["QALITA_WORKER_MODE"] = "worker"
250
250
  logger.info(
251
- f"worker_start(fallback): effective agent mode is [{env.get('QALITA_WORKER_MODE') or env.get('AGENT_MODE') or env.get('MODE')}]"
251
+ f"worker_start(fallback): effective worker mode is [{env.get('QALITA_WORKER_MODE') or env.get('AGENT_MODE') or env.get('MODE')}]"
252
252
  )
253
253
  python_bin = sys.executable or "python3"
254
254
  cmd = [
@@ -368,7 +368,7 @@ def open_worker_run(run_name: str):
368
368
  return jsonify({
369
369
  "ok": False,
370
370
  "error": "Invalid path",
371
- "message": "If the path is invalid it means: Your local agent is not the one that ran the analysis you are trying to get files from, or the job failed or was cancelled.",
371
+ "message": "If the path is invalid it means: Your local worker is not the one that ran the analysis you are trying to get files from, or the job failed or was cancelled.",
372
372
  }), 400
373
373
 
374
374
  if not os.path.isdir(candidate):
@@ -376,7 +376,7 @@ def open_worker_run(run_name: str):
376
376
  "ok": False,
377
377
  "error": "Run folder not found",
378
378
  "path": candidate,
379
- "message": "If the run folder does not exist it means: Your local agent is not the one that ran the analysis you are trying to get files from, or the job failed or was cancelled.",
379
+ "message": "If the run folder does not exist it means: Your local worker is not the one that ran the analysis you are trying to get files from, or the job failed or was cancelled.",
380
380
  }), 404
381
381
 
382
382
  ok, method_used = open_path_in_file_explorer(candidate)
@@ -424,7 +424,7 @@ def _login_with_env(env_path: str) -> None:
424
424
  name = (
425
425
  pick("QALITA_WORKER_NAME", "AGENT_NAME", "NAME")
426
426
  or getattr(cfg, "name", None)
427
- or "agent"
427
+ or "worker"
428
428
  )
429
429
  mode = (
430
430
  pick("QALITA_WORKER_MODE", "AGENT_MODE", "MODE")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qalita
3
- Version: 2.6.3
3
+ Version: 2.8.0
4
4
  Summary: QALITA Platform Command Line Interface
5
5
  Author-email: QALITA SAS <contact@qalita.io>
6
6
  License-File: LICENSE
@@ -27,12 +27,16 @@ Requires-Dist: flask>=3.0.3
27
27
  Requires-Dist: gevent-websocket>=0.10.1
28
28
  Requires-Dist: gevent>=24.2.0
29
29
  Requires-Dist: google-cloud-storage>=2.16.0
30
+ Requires-Dist: grpcio-tools>=1.60.0
31
+ Requires-Dist: grpcio>=1.60.0
30
32
  Requires-Dist: hdfs>=2.7.3
31
33
  Requires-Dist: loguru>=0.7.0
32
34
  Requires-Dist: openpyxl>=3.1.5
33
35
  Requires-Dist: oracledb>=2.5.0
36
+ Requires-Dist: pandas>=2.0.0
34
37
  Requires-Dist: paramiko>=3.4.0
35
38
  Requires-Dist: psycopg2-binary>=2.9.9
39
+ Requires-Dist: pyarrow>=14.0.0
36
40
  Requires-Dist: pymongo>=4.6.3
37
41
  Requires-Dist: pymysql>=1.1.0
38
42
  Requires-Dist: python-socketio>=5.10.0
@@ -46,8 +50,10 @@ Requires-Dist: waitress==3.0.2
46
50
  Provides-Extra: dev
47
51
  Requires-Dist: black>=25.1.0; extra == 'dev'
48
52
  Requires-Dist: flake8<8.0,>=3.8.1; extra == 'dev'
53
+ Requires-Dist: joserfc>=1.0.2; extra == 'dev'
49
54
  Requires-Dist: pre-commit>=3.3.3; extra == 'dev'
50
55
  Requires-Dist: pylint>=2.17.4; extra == 'dev'
56
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
51
57
  Requires-Dist: pytest>=7.4.0; extra == 'dev'
52
58
  Provides-Extra: studio
53
59
  Requires-Dist: qalita-studio>=0.1.0; extra == 'studio'