loki-mode 7.81.1 → 7.83.0

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.
@@ -140,6 +140,9 @@ def register_project(
140
140
  path: str,
141
141
  name: Optional[str] = None,
142
142
  alias: Optional[str] = None,
143
+ pid: Optional[int] = None,
144
+ port: Optional[int] = None,
145
+ status: Optional[str] = None,
143
146
  ) -> dict:
144
147
  """
145
148
  Register a project in the registry.
@@ -148,6 +151,18 @@ def register_project(
148
151
  path: Absolute path to the project directory
149
152
  name: Display name (defaults to directory name)
150
153
  alias: Short alias for CLI usage
154
+ pid: Live runtime pid for the project's current build (optional)
155
+ port: Live runtime port for the project's current build (optional)
156
+ status: Runtime status override, e.g. "active" / "running" (optional)
157
+
158
+ The pid / port / status kwargs let a caller register a project AND set its
159
+ runtime fields in one atomic, locked load->mutate->save. Before this, the
160
+ runner had to call register_project() and then do a SECOND, unlocked
161
+ load-mutate-save to stamp pid/port/status, which lost-updates a concurrent
162
+ writer (two writers each load the registry, set their own runtime fields,
163
+ and save -- the last save wins and silently drops the other's entry). Set
164
+ these here so the whole change happens inside _registry_lock(). When all
165
+ three are omitted the behavior is unchanged (backward compatible).
151
166
 
152
167
  Returns:
153
168
  The registered project entry
@@ -188,6 +203,16 @@ def register_project(
188
203
  }
189
204
  registry["projects"][project_id] = project
190
205
 
206
+ # Atomically stamp runtime fields inside the same lock (both the create
207
+ # and update branches). None means "leave as-is" so omitting these
208
+ # keeps the legacy behavior.
209
+ if pid is not None:
210
+ project["pid"] = pid
211
+ if port is not None:
212
+ project["port"] = port
213
+ if status is not None:
214
+ project["status"] = status
215
+
191
216
  _save_registry(registry)
192
217
  return project
193
218
 
@@ -438,7 +463,6 @@ def sync_registry_with_discovery() -> dict:
438
463
  Returns:
439
464
  Summary of sync results
440
465
  """
441
- registry = _load_registry()
442
466
  discovered = discover_projects()
443
467
 
444
468
  # Track results
@@ -446,31 +470,50 @@ def sync_registry_with_discovery() -> dict:
446
470
  updated = []
447
471
  missing = []
448
472
 
449
- # Add/update discovered projects
450
- discovered_paths = set()
451
- for project_info in discovered:
452
- path = project_info["path"]
453
- discovered_paths.add(path)
454
- project_id = _generate_project_id(path)
455
-
456
- if project_id not in registry["projects"]:
457
- # New project
458
- project = register_project(path)
459
- added.append(project)
460
- else:
461
- # Update existing
462
- project = registry["projects"][project_id]
463
- project["has_loki_dir"] = True
464
- project["updated_at"] = datetime.now(timezone.utc).isoformat()
465
- updated.append(project)
473
+ # Mutate a single registry copy under the lock and save it ONCE at the end.
474
+ # Previously this loaded the registry, then called register_project() in the
475
+ # loop (which loads+mutates+saves its OWN copy), and finally saved the stale
476
+ # original over the top -- silently dropping every newly added project (the
477
+ # summary said "added: N" while the on-disk registry kept zero of them). All
478
+ # mutations now happen on the same in-memory dict that gets persisted.
479
+ with _registry_lock():
480
+ registry = _load_registry()
466
481
 
467
- # Check for missing projects
468
- for project_id, project in registry["projects"].items():
469
- if not os.path.isdir(project["path"]):
470
- project["status"] = "missing"
471
- missing.append(project)
482
+ # Add/update discovered projects
483
+ for project_info in discovered:
484
+ path = project_info["path"]
485
+ project_id = _generate_project_id(path)
486
+
487
+ if project_id not in registry["projects"]:
488
+ # New project
489
+ now = datetime.now(timezone.utc).isoformat()
490
+ project = {
491
+ "id": project_id,
492
+ "path": path,
493
+ "name": os.path.basename(path),
494
+ "alias": None,
495
+ "registered_at": now,
496
+ "updated_at": now,
497
+ "last_accessed": None,
498
+ "has_loki_dir": os.path.isdir(os.path.join(path, ".loki")),
499
+ "status": "active",
500
+ }
501
+ registry["projects"][project_id] = project
502
+ added.append(project)
503
+ else:
504
+ # Update existing
505
+ project = registry["projects"][project_id]
506
+ project["has_loki_dir"] = True
507
+ project["updated_at"] = datetime.now(timezone.utc).isoformat()
508
+ updated.append(project)
472
509
 
473
- _save_registry(registry)
510
+ # Check for missing projects
511
+ for project_id, project in registry["projects"].items():
512
+ if not os.path.isdir(project["path"]):
513
+ project["status"] = "missing"
514
+ missing.append(project)
515
+
516
+ _save_registry(registry)
474
517
 
475
518
  return {
476
519
  "added": len(added),