development-engine-vector 0.5.0__tar.gz → 0.5.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 (54) hide show
  1. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/PKG-INFO +1 -1
  2. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/changelog.md +19 -0
  3. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/kernel/kernel_core.py +2 -1
  4. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/kernel/otk_kernel.py +14 -0
  5. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/actions.py +1 -0
  6. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/routes.py +20 -4
  7. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/static/web.js +91 -23
  8. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/templates.py +7 -0
  9. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/workflow/release.py +2 -2
  10. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/workflow/versioning.py +7 -3
  11. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/pyproject.toml +1 -1
  12. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/tests/test_dev_cli.py +4 -6
  13. development_engine_vector-0.5.1/version +1 -0
  14. development_engine_vector-0.5.0/version +0 -1
  15. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/.flake8 +0 -0
  16. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/.gitignore +0 -0
  17. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/contributing.md +0 -0
  18. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/__init__.py +0 -0
  19. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/__main__.py +0 -0
  20. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/cli/__init__.py +0 -0
  21. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/cli/cli.py +0 -0
  22. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/kernel/__init__.py +0 -0
  23. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/kernel/config.py +0 -0
  24. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/kernel/db.py +0 -0
  25. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/kernel/paths.py +0 -0
  26. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/kernel/pmf_kernel.py +0 -0
  27. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/kernel/selfcheck.py +0 -0
  28. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/__init__.py +0 -0
  29. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/__init__.py +0 -0
  30. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/base.py +0 -0
  31. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/checks.py +0 -0
  32. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/events.py +0 -0
  33. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/health.py +0 -0
  34. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/identity.py +0 -0
  35. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/manifest.py +0 -0
  36. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/overview.py +0 -0
  37. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/db/runs.py +0 -0
  38. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/static/__init__.py +0 -0
  39. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/static/web.css +0 -0
  40. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/ui/web.py +0 -0
  41. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/utils.py +0 -0
  42. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/vcs/__init__.py +0 -0
  43. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/vcs/git.py +0 -0
  44. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/vcs/github.py +0 -0
  45. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/workflow/__init__.py +0 -0
  46. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/workflow/cda.py +0 -0
  47. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/workflow/changelog.py +0 -0
  48. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev/workflow/preflight.py +0 -0
  49. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/dev-cli.toml.example +0 -0
  50. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/docs/architecture.md +0 -0
  51. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/license +0 -0
  52. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/readme.md +0 -0
  53. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/tests/__init__.py +0 -0
  54. {development_engine_vector-0.5.0 → development_engine_vector-0.5.1}/tests/test_cda_preflight.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: development-engine-vector
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: Development Engine Vector — internal developer workflow CLI.
5
5
  Author-email: Ernie Butcher <ernie@fiosii.com>
6
6
  License-Expression: MIT
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.1] - 2026-05-12
9
+
10
+ ### Fixed
11
+ - UI service status stuck on `"starting"` — `_refresh()` now advances to `"running"` and persists state when the process is confirmed alive
12
+ - `VersionManager.get_sync_targets()` removed stale `vscode_ark/__init__.py` and `setup.py` targets; now resolves `dev/__init__.py` from the project tree
13
+ - `ReleaseOrchestrator.commit_and_tag()` staged `vscode_ark/__init__.py` and `setup.py` (copy-paste from cda); corrected to `dev/__init__.py`
14
+ - `get_recent_runs` unused import in `routes.py` removed (lint failure)
15
+ - Release page showed always-empty kernel run history; now shows real action states (pass/fail, exit code, timestamps)
16
+
17
+ ### Added
18
+ - `/api/kernel/logs?service=X&lines=N` endpoint — tails any service log file from the UI
19
+ - OTK page **Logs** button per service opens a modal with the last 100 log lines
20
+ - Action buttons now poll to completion and display a result toast with exit code and captured output
21
+ - Raw Query page has a database selector: `control.db` (vet history) or `dev.db` (kernel/projects)
22
+ - `sync` OTK task service added — bootstrap/editable-install from the OTK page
23
+ - Setup page shows `dev otk install` guidance when the LaunchAgent is not installed
24
+ - Git page shows "Clean — no commits since vX.Y.Z" on a clean repo instead of a blank panel
25
+ - `vet.py` writes `vet.start` / `vet.pass` / `vet.fail` events to the `events` table and refreshes `identity.version` on every passing run
26
+
8
27
  ## [0.5.0] - 2026-05-11
9
28
 
10
29
  ### Added
@@ -150,9 +150,10 @@ class KernelCore:
150
150
 
151
151
  pid = state.get("pid")
152
152
  if pid and self._is_alive(pid):
153
- if state["status"] not in ("running", "starting"):
153
+ if state["status"] != "running":
154
154
  state["status"] = "running"
155
155
  state["updated_at"] = self._now_iso()
156
+ self._save_state()
156
157
  return state
157
158
 
158
159
  if spec.pid_file and spec.pid_file.exists():
@@ -174,6 +174,10 @@ def _vet_command(options: Optional[Dict[str, str]] = None) -> List[str]:
174
174
  return [sys.executable, str(_CONTROL_SCRIPTS / "vet.py")]
175
175
 
176
176
 
177
+ def _sync_command(options: Optional[Dict[str, str]] = None) -> List[str]:
178
+ return [sys.executable, "-m", "dev.cli.cli", "sync", "--project", _project_arg(options)]
179
+
180
+
177
181
  def _publish_check_command(options: Optional[Dict[str, str]] = None) -> List[str]:
178
182
  return [sys.executable, str(_CONTROL_SCRIPTS / "push.py"), "--dry-run", "--skip-vet"]
179
183
 
@@ -243,6 +247,16 @@ SERVICE_SPECS: Dict[str, ServiceSpec] = {
243
247
  log_file=LOG_DIR / "release-dryrun.log",
244
248
  allowed_actions=["start", "status", "logs"],
245
249
  ),
250
+ "sync": ServiceSpec(
251
+ service_id="sync",
252
+ label="Sync",
253
+ service_type="task",
254
+ description="Bootstrap or sync project dependencies (editable install + requirements).",
255
+ command_builder=_sync_command,
256
+ cwd=_SOURCE_ROOT,
257
+ log_file=LOG_DIR / "sync.log",
258
+ allowed_actions=["start", "status", "logs"],
259
+ ),
246
260
  }
247
261
 
248
262
 
@@ -21,6 +21,7 @@ _ACTION_TO_SERVICE = {
21
21
  "build": "build",
22
22
  "publish-check": "publish-check",
23
23
  "release-dryrun": "release-dryrun",
24
+ "sync": "sync",
24
25
  }
25
26
 
26
27
 
@@ -6,7 +6,7 @@ from urllib.parse import parse_qs
6
6
 
7
7
  from dev import __version__ as DEV_VERSION
8
8
  from dev.kernel.config import DevConfig
9
- from dev.kernel.db import get_projects, get_recent_runs
9
+ from dev.kernel.db import get_projects
10
10
  from dev.kernel.paths import (
11
11
  DEV_HOME, DATA_DIR, RUN_DIR, LOG_DIR, CONFIG_DIR, PMF_DIR,
12
12
  GOCOSMIX_HOME, GOCOSMIX_APPS, GOCOSMIX_TOOLS, GOCOSMIX_SYSTEM,
@@ -125,6 +125,13 @@ def application(environ, start_response):
125
125
  start_response("200 OK", [("Content-Type", "application/json")])
126
126
  return [json.dumps({"events": data}).encode("utf-8")]
127
127
 
128
+ elif path == "/api/kernel/logs":
129
+ service_id = query.get("service", [""])[0]
130
+ lines = int(query.get("lines", ["50"])[0])
131
+ output = _KERNEL.tail_log(service_id, lines=lines)
132
+ start_response("200 OK", [("Content-Type", "application/json")])
133
+ return _json_ok({"service": service_id, "lines": lines, "output": output})
134
+
128
135
  elif path == "/api/kernel/start" and method == "POST":
129
136
  body = environ["wsgi.input"].read()
130
137
  payload = json.loads(body.decode("utf-8"))
@@ -220,8 +227,7 @@ def application(environ, start_response):
220
227
  current_version = VersionManager(_SOURCE_DIR).read()
221
228
  start_response("200 OK", [("Content-Type", "application/json")])
222
229
  return _json_ok({
223
- "project_runs": get_recent_runs("dev", 10),
224
- "recent_runs": get_recent_runs(limit=10),
230
+ "action_states": list_action_states(limit=20),
225
231
  "projects": get_projects(),
226
232
  "version": current_version,
227
233
  })
@@ -265,7 +271,17 @@ def application(environ, start_response):
265
271
  body = environ["wsgi.input"].read()
266
272
  payload = json.loads(body.decode("utf-8"))
267
273
  sql = payload.get("sql", "")
268
- rows = query_rows(sql)
274
+ db_target = payload.get("db", "control")
275
+ if db_target == "kernel":
276
+ import sqlite3 as _sqlite3
277
+ conn = _sqlite3.connect(str(DB_PATH))
278
+ conn.row_factory = _sqlite3.Row
279
+ try:
280
+ rows = [dict(r) for r in conn.execute(sql).fetchall()]
281
+ finally:
282
+ conn.close()
283
+ else:
284
+ rows = query_rows(sql)
269
285
  start_response("200 OK", [("Content-Type", "application/json")])
270
286
  return [json.dumps({"rows": rows}).encode("utf-8")]
271
287
 
@@ -496,7 +496,7 @@ function initRelease() {
496
496
  if (!container) return;
497
497
  container.innerHTML = '<div class="spinner"></div> Loading release state...';
498
498
  fetch('/api/dev/release').then(r => r.json()).then(data => {
499
- const runs = safeArray(data.recent_runs);
499
+ const actions = safeArray(data.action_states);
500
500
  const projects = safeArray(data.projects);
501
501
  container.innerHTML = `
502
502
  <div class="card mb-20">
@@ -507,30 +507,23 @@ function initRelease() {
507
507
  <button class="button button-primary" onclick="triggerAction('build').then(() => initRelease())">Build</button>
508
508
  <button class="button button-secondary" onclick="triggerAction('publish-check').then(() => initRelease())">Publish Check</button>
509
509
  <button class="button button-secondary" onclick="triggerAction('release-dryrun').then(() => initRelease())">Release Dry-Run</button>
510
+ <button class="button button-secondary" onclick="triggerAction('sync').then(() => initRelease())">Sync</button>
510
511
  </div>
511
512
  </div>
512
- <div class="grid-4">
513
+ <div class="grid-3">
513
514
  <div class="card"><div class="card-header">Version</div><div class="card-value">${data.version || '—'}</div></div>
514
- <div class="card"><div class="card-header">Recent Runs</div><div class="card-value">${runs.length}</div></div>
515
+ <div class="card"><div class="card-header">Action History</div><div class="card-value">${actions.length}</div></div>
515
516
  <div class="card"><div class="card-header">Registered Projects</div><div class="card-value">${projects.length}</div></div>
516
- <div class="card"><div class="card-header">Project Runs</div><div class="card-value">${safeArray(data.project_runs).length}</div></div>
517
517
  </div>
518
518
  <div class="card mt-20">
519
- <div class="card-header">Recent Release Runs</div>
520
- <table class="table">
521
- <thead><tr><th>Project</th><th>Action</th><th>Version</th><th>Outcome</th><th>Started</th></tr></thead>
522
- <tbody>
523
- ${runs.map(r => `
524
- <tr>
525
- <td><strong>${r.project}</strong></td>
526
- <td>${r.action}</td>
527
- <td>${r.version || '—'}</td>
528
- <td><span class="badge ${r.outcome === 'pass' ? 'badge-success' : r.outcome === 'running' ? 'badge-warning' : 'badge-danger'}">${r.outcome}</span></td>
529
- <td>${r.started_at ? new Date(r.started_at).toLocaleString() : '—'}</td>
530
- </tr>
531
- `).join('')}
532
- </tbody>
533
- </table>
519
+ <div class="card-header">Recent Actions</div>
520
+ ${actions.length ? `<table class="table"><thead><tr><th>Action</th><th>Status</th><th>Exit</th><th>Started</th><th>Completed</th></tr></thead><tbody>${actions.map(a => {
521
+ const ok = a.status === 'completed' && a.returncode === 0;
522
+ const fail = a.status === 'failed' || (a.status === 'completed' && a.returncode !== 0);
523
+ const badge = ok ? 'badge-success' : fail ? 'badge-danger' : 'badge-warning';
524
+ const label = ok ? 'PASS' : fail ? 'FAIL' : a.status || '—';
525
+ return `<tr><td><strong>${a.action_id || '—'}</strong><div class="text-muted">${a.service_id || ''}</div></td><td><span class="badge ${badge}">${label}</span></td><td>${a.returncode !== null && a.returncode !== undefined ? a.returncode : '—'}</td><td>${a.started_at ? new Date(a.started_at).toLocaleString() : '—'}</td><td>${a.completed_at ? new Date(a.completed_at).toLocaleString() : '—'}</td></tr>`;
526
+ }).join('')}</tbody></table>` : '<div class="text-muted">No actions run this session.</div>'}
534
527
  </div>
535
528
  `;
536
529
  }).catch(() => {
@@ -586,7 +579,7 @@ function initGit() {
586
579
  </div>
587
580
  <div class="card mt-20">
588
581
  <div class="card-header">Commits Since Tag</div>
589
- ${commits.length ? `<ul class="drawer-list">${commits.map(c => `<li><span>${c.replace(/</g, '&lt;')}</span></li>`).join('')}</ul>` : '<div class="text-muted">No commits returned.</div>'}
582
+ ${commits.length ? `<ul class="drawer-list">${commits.map(c => `<li><span>${c.replace(/</g, '&lt;')}</span></li>`).join('')}</ul>` : `<div class="text-muted">Clean — no commits since ${data.last_tag || 'last tag'}.</div>`}
590
583
  </div>
591
584
  `;
592
585
  }).catch(() => {
@@ -630,6 +623,7 @@ function initOtk() {
630
623
  <button class="button button-small" data-service="${s.service_id}" data-op="start">Start</button>
631
624
  <button class="button button-small" data-service="${s.service_id}" data-op="stop">Stop</button>
632
625
  <button class="button button-small" data-service="${s.service_id}" data-op="restart">Restart</button>
626
+ <button class="button button-small" data-service="${s.service_id}" data-op="logs">Logs</button>
633
627
  </div>
634
628
  </td>
635
629
  </tr>
@@ -652,7 +646,11 @@ function initOtk() {
652
646
  btn.addEventListener('click', () => {
653
647
  const service = btn.dataset.service;
654
648
  const op = btn.dataset.op;
655
- controlKernelService(service, op).then(() => initOtk());
649
+ if (op === 'logs') {
650
+ _showServiceLogs(service);
651
+ } else {
652
+ controlKernelService(service, op).then(() => initOtk());
653
+ }
656
654
  });
657
655
  });
658
656
  }).catch(() => {
@@ -675,6 +673,7 @@ function initSetup() {
675
673
  <div class="card"><div class="card-header">Installed</div><div class="card-value">${data.launch_agent_installed ? 'Yes' : 'No'}</div></div>
676
674
  <div class="card"><div class="card-header">Port</div><div class="card-value">${data.default_port || '—'}</div></div>
677
675
  </div>
676
+ ${!data.launch_agent_installed ? `<div class="alert alert-info mt-20" style="font-size:13px;">LaunchAgent not installed — <code>dev</code> will not auto-start on login. Run: <code>dev otk install</code></div>` : ''}
678
677
  <div class="card mt-20">
679
678
  <div class="card-header">Runtime Paths</div>
680
679
  <table class="table">
@@ -809,6 +808,8 @@ function initQuery() {
809
808
 
810
809
  function executeQuery() {
811
810
  const sql = document.getElementById('query-input').value;
811
+ const dbSelect = document.getElementById('query-db');
812
+ const db = dbSelect ? dbSelect.value : 'control';
812
813
  if (!sql) return alert('Enter a SQL query');
813
814
  const results = document.getElementById('query-results');
814
815
  if (!results) return;
@@ -817,7 +818,7 @@ function executeQuery() {
817
818
  fetch('/api/query', {
818
819
  method: 'POST',
819
820
  headers: {'Content-Type': 'application/json'},
820
- body: JSON.stringify({sql}),
821
+ body: JSON.stringify({sql, db}),
821
822
  }).then(r => r.json()).then(data => {
822
823
  if (data.error) {
823
824
  results.innerHTML = '<div class="alert alert-danger">Error: ' + data.error + '</div>';
@@ -855,12 +856,62 @@ function runVet() {
855
856
  });
856
857
  }
857
858
 
859
+ // ── Action polling ───────────────────────────────────────────────────────────
860
+
858
861
  function triggerAction(action) {
859
862
  return fetch('/api/action', {
860
863
  method: 'POST',
861
864
  headers: {'Content-Type': 'application/json'},
862
865
  body: JSON.stringify({action}),
863
- }).then(r => r.json());
866
+ }).then(r => r.json()).then(data => {
867
+ if (data.action_id) {
868
+ return _pollActionToast(action, data.action_id);
869
+ }
870
+ return data;
871
+ });
872
+ }
873
+
874
+ function _pollActionToast(action, actionId) {
875
+ const toast = _showActionToast(action);
876
+ return new Promise(resolve => {
877
+ const interval = setInterval(() => {
878
+ fetch('/api/action/status?action_id=' + encodeURIComponent(actionId))
879
+ .then(r => r.json())
880
+ .then(state => {
881
+ const done = state.status === 'completed' || state.status === 'failed';
882
+ if (done) {
883
+ clearInterval(interval);
884
+ _updateActionToast(toast, action, state);
885
+ resolve(state);
886
+ }
887
+ })
888
+ .catch(() => { clearInterval(interval); resolve({}); });
889
+ }, 1200);
890
+ });
891
+ }
892
+
893
+ function _showActionToast(action) {
894
+ let container = document.getElementById('action-toasts');
895
+ if (!container) {
896
+ container = document.createElement('div');
897
+ container.id = 'action-toasts';
898
+ container.style.cssText = 'position:fixed;bottom:20px;right:20px;z-index:9999;display:flex;flex-direction:column;gap:8px;max-width:420px;';
899
+ document.body.appendChild(container);
900
+ }
901
+ const toast = document.createElement('div');
902
+ toast.style.cssText = 'background:#1e2130;border:1px solid #3a3f55;border-radius:8px;padding:12px 16px;color:#e2e8f0;font-size:13px;box-shadow:0 4px 16px rgba(0,0,0,.4);';
903
+ toast.innerHTML = `<div style="display:flex;align-items:center;gap:8px;"><span class="spinner" style="width:14px;height:14px;border-width:2px;margin:0;"></span><strong>${action}</strong> running…</div><div class="toast-output" style="margin-top:6px;font-size:11px;color:#8892a4;max-height:120px;overflow-y:auto;white-space:pre-wrap;word-break:break-all;"></div>`;
904
+ container.appendChild(toast);
905
+ return toast;
906
+ }
907
+
908
+ function _updateActionToast(toast, action, state) {
909
+ const ok = state.status === 'completed' && (state.returncode === 0 || state.returncode === null);
910
+ const badge = ok ? 'badge-success' : 'badge-danger';
911
+ const label = ok ? 'PASS' : 'FAIL';
912
+ const output = (state.output || '').replace(/</g, '&lt;').replace(/>/g, '&gt;');
913
+ toast.innerHTML = `<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;"><span><strong>${action}</strong> <span class="badge ${badge}">${label}</span></span><button onclick="this.closest('div[style]').remove()" style="background:none;border:none;color:#8892a4;cursor:pointer;font-size:16px;line-height:1;">×</button></div>${output ? `<pre style="margin-top:6px;font-size:11px;color:#8892a4;max-height:180px;overflow-y:auto;white-space:pre-wrap;word-break:break-all;">${output}</pre>` : ''}`;
914
+ setTimeout(() => { if (toast.parentNode) toast.remove(); }, ok ? 8000 : 30000);
864
915
  }
865
916
 
866
917
  function controlKernelService(service, operation) {
@@ -870,3 +921,20 @@ function controlKernelService(service, operation) {
870
921
  body: JSON.stringify({service}),
871
922
  }).then(r => r.json());
872
923
  }
924
+
925
+ function _showServiceLogs(serviceId) {
926
+ fetch('/api/kernel/logs?service=' + encodeURIComponent(serviceId) + '&lines=100')
927
+ .then(r => r.json())
928
+ .then(data => {
929
+ let modal = document.getElementById('log-modal');
930
+ if (!modal) {
931
+ modal = document.createElement('div');
932
+ modal.id = 'log-modal';
933
+ modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:9998;display:flex;align-items:center;justify-content:center;';
934
+ modal.innerHTML = '<div style="background:#161926;border:1px solid #3a3f55;border-radius:10px;width:80vw;max-width:900px;max-height:80vh;display:flex;flex-direction:column;overflow:hidden;"><div style="display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid #3a3f55;"><strong id="log-modal-title" style="color:#e2e8f0;font-size:14px;"></strong><button onclick="document.getElementById(\'log-modal\').remove()" style="background:none;border:none;color:#8892a4;cursor:pointer;font-size:20px;line-height:1;">×</button></div><pre id="log-modal-body" style="margin:0;padding:16px;overflow-y:auto;font-size:12px;color:#c5cdd9;white-space:pre-wrap;word-break:break-all;flex:1;"></pre></div>';
935
+ document.body.appendChild(modal);
936
+ }
937
+ document.getElementById('log-modal-title').textContent = serviceId + ' — last 100 lines';
938
+ document.getElementById('log-modal-body').textContent = data.output || '(no output)';
939
+ });
940
+ }
@@ -326,6 +326,13 @@ def render_query():
326
326
  </div>
327
327
  </div>
328
328
  <div class="card mb-20">
329
+ <div class="form-group">
330
+ <label class="form-label">Database</label>
331
+ <select id="query-db" class="form-input" style="max-width:220px;">
332
+ <option value="control">control.db (vet history)</option>
333
+ <option value="kernel">dev.db (kernel / projects)</option>
334
+ </select>
335
+ </div>
329
336
  <div class="form-group">
330
337
  <label class="form-label">SQL Query</label>
331
338
  <textarea id="query-input" class="form-textarea" placeholder="SELECT * FROM runs ORDER BY id DESC LIMIT 10"></textarea>
@@ -78,8 +78,8 @@ class ReleaseOrchestrator:
78
78
  "version",
79
79
  "changelog.md",
80
80
  "pyproject.toml",
81
- "setup.py",
82
- "vscode_ark/__init__.py",
81
+ "dev/__init__.py",
82
+ "source/dev/__init__.py",
83
83
  ]
84
84
 
85
85
  existing = [path for path in files_to_stage if (self.project_dir / path).exists()]
@@ -80,8 +80,12 @@ class VersionManager:
80
80
 
81
81
  def get_sync_targets(self):
82
82
  """Return standard sync target patterns for common files."""
83
- return {
83
+ targets = {
84
84
  "pyproject.toml": r'(^version\s*=\s*")' + self.VERSION_PATTERN + r'(")',
85
- "setup.py": r'(\s*version\s*=\s*")' + self.VERSION_PATTERN + r'(",)',
86
- "vscode_ark/__init__.py": r'(\s*__version__\s*=\s*")' + self.VERSION_PATTERN + r'(")',
87
85
  }
86
+ # include __init__.py only if it exists under the source package
87
+ for candidate in ["dev/__init__.py", "source/dev/__init__.py"]:
88
+ if (self.project_dir / candidate).exists():
89
+ targets[candidate] = r'(\s*__version__\s*=\s*")' + self.VERSION_PATTERN + r'(")' # noqa: E501
90
+ break
91
+ return targets
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "development-engine-vector"
3
- version = "0.5.0"
3
+ version = "0.5.1"
4
4
  description = "Development Engine Vector — internal developer workflow CLI."
5
5
  readme = "readme.md"
6
6
  requires-python = ">=3.8"
@@ -16,12 +16,11 @@ class TestVersionManager(unittest.TestCase):
16
16
  self.version_file = self.temp_dir / "version"
17
17
  self.version_file.write_text("1.2.3\n")
18
18
  self.pyproject = self.temp_dir / "pyproject.toml"
19
- self.setup_py = self.temp_dir / "setup.py"
20
- self.init_py = self.temp_dir / "vscode_ark"
21
- self.init_py.mkdir()
22
- self.init_file = self.init_py / "__init__.py"
23
19
  self.pyproject.write_text('version = "1.2.3"\n')
24
- self.setup_py.write_text('setup(version="1.2.3",)\n')
20
+ # create dev/__init__.py as the canonical sync target for this project
21
+ dev_pkg = self.temp_dir / "dev"
22
+ dev_pkg.mkdir()
23
+ self.init_file = dev_pkg / "__init__.py"
25
24
  self.init_file.write_text('__version__ = "1.2.3"\n')
26
25
 
27
26
  def tearDown(self):
@@ -45,7 +44,6 @@ class TestVersionManager(unittest.TestCase):
45
44
 
46
45
  vm.sync_to_files("2.1.0", vm.get_sync_targets())
47
46
  self.assertIn('version = "2.1.0"', self.pyproject.read_text())
48
- self.assertIn('setup(version="2.1.0",)', self.setup_py.read_text())
49
47
  self.assertIn('__version__ = "2.1.0"', self.init_file.read_text())
50
48
 
51
49
 
@@ -0,0 +1 @@
1
+ 0.5.1
@@ -1 +0,0 @@
1
- 0.5.0