adelie-ai 0.2.14 → 0.2.15
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.
- package/adelie/__init__.py +1 -1
- package/adelie/agents/runner_ai.py +24 -14
- package/adelie/agents/tester_ai.py +12 -13
- package/adelie/pyproject.toml +1 -1
- package/adelie/sandbox.py +5 -5
- package/package.json +1 -1
package/adelie/__init__.py
CHANGED
|
@@ -26,14 +26,16 @@ from pathlib import Path
|
|
|
26
26
|
|
|
27
27
|
from rich.console import Console
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
import adelie.config as cfg
|
|
30
30
|
from adelie.llm_client import generate
|
|
31
31
|
from adelie.tool_registry import get_registry as get_tool_registry
|
|
32
32
|
|
|
33
33
|
console = Console()
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
def _get_runner_dirs():
|
|
36
|
+
import adelie.config as cfg
|
|
37
|
+
runner_root = cfg.WORKSPACE_PATH.parent / "runner"
|
|
38
|
+
return runner_root, runner_root / "processes.json"
|
|
37
39
|
|
|
38
40
|
_IS_WINDOWS = sys.platform == "win32"
|
|
39
41
|
|
|
@@ -299,7 +301,8 @@ def _extract_port(cmd: str) -> int | None:
|
|
|
299
301
|
|
|
300
302
|
def _save_process(pid: int, cmd: str, description: str, port: int | None = None) -> None:
|
|
301
303
|
"""Track a background process with optional port info."""
|
|
302
|
-
|
|
304
|
+
runner_root, process_file = _get_runner_dirs()
|
|
305
|
+
runner_root.mkdir(parents=True, exist_ok=True)
|
|
303
306
|
processes = _load_processes()
|
|
304
307
|
|
|
305
308
|
entry = {
|
|
@@ -313,14 +316,17 @@ def _save_process(pid: int, cmd: str, description: str, port: int | None = None)
|
|
|
313
316
|
if detected_port:
|
|
314
317
|
entry["port"] = detected_port
|
|
315
318
|
processes.append(entry)
|
|
316
|
-
|
|
319
|
+
|
|
320
|
+
_, process_file = _get_runner_dirs()
|
|
321
|
+
process_file.write_text(json.dumps(processes, indent=2), encoding="utf-8")
|
|
317
322
|
|
|
318
323
|
|
|
319
324
|
def _load_processes() -> list[dict]:
|
|
320
325
|
"""Load tracked processes."""
|
|
321
|
-
|
|
326
|
+
_, process_file = _get_runner_dirs()
|
|
327
|
+
if process_file.exists():
|
|
322
328
|
try:
|
|
323
|
-
return json.loads(
|
|
329
|
+
return json.loads(process_file.read_text(encoding="utf-8"))
|
|
324
330
|
except Exception:
|
|
325
331
|
pass
|
|
326
332
|
return []
|
|
@@ -328,7 +334,8 @@ def _load_processes() -> list[dict]:
|
|
|
328
334
|
|
|
329
335
|
def _cleanup_dead_processes() -> None:
|
|
330
336
|
"""Remove dead processes from tracking file."""
|
|
331
|
-
|
|
337
|
+
runner_root, process_file = _get_runner_dirs()
|
|
338
|
+
if not process_file.exists():
|
|
332
339
|
return
|
|
333
340
|
import os
|
|
334
341
|
processes = _load_processes()
|
|
@@ -350,8 +357,9 @@ def _cleanup_dead_processes() -> None:
|
|
|
350
357
|
is_alive = False
|
|
351
358
|
if is_alive:
|
|
352
359
|
alive.append(proc)
|
|
353
|
-
|
|
354
|
-
|
|
360
|
+
runner_root, process_file = _get_runner_dirs()
|
|
361
|
+
runner_root.mkdir(parents=True, exist_ok=True)
|
|
362
|
+
process_file.write_text(json.dumps(alive, indent=2), encoding="utf-8")
|
|
355
363
|
|
|
356
364
|
|
|
357
365
|
def _is_similar_running(description: str) -> int | None:
|
|
@@ -396,7 +404,7 @@ def run_pipeline(
|
|
|
396
404
|
Summary of execution results.
|
|
397
405
|
"""
|
|
398
406
|
if workspace_root is None:
|
|
399
|
-
workspace_root = PROJECT_ROOT
|
|
407
|
+
workspace_root = cfg.PROJECT_ROOT
|
|
400
408
|
|
|
401
409
|
console.print(f"[bold blue]🚀 Runner AI[/bold blue] — tier: {max_tier}")
|
|
402
410
|
|
|
@@ -409,7 +417,7 @@ def run_pipeline(
|
|
|
409
417
|
|
|
410
418
|
# ── Sandbox Mode ──────────────────────────────────────────────────────
|
|
411
419
|
from adelie.sandbox import get_effective_mode, get_sandbox_summary, SandboxMode
|
|
412
|
-
sandbox_mode = get_effective_mode(SANDBOX_MODE)
|
|
420
|
+
sandbox_mode = get_effective_mode(cfg.SANDBOX_MODE)
|
|
413
421
|
if sandbox_mode != SandboxMode.NONE:
|
|
414
422
|
console.print(f" [dim]{get_sandbox_summary(sandbox_mode)}[/dim]")
|
|
415
423
|
|
|
@@ -475,7 +483,8 @@ def run_pipeline(
|
|
|
475
483
|
else:
|
|
476
484
|
return {"executed": 0, "succeeded": 0, "failed": 0, "errors": []}
|
|
477
485
|
|
|
478
|
-
|
|
486
|
+
runner_root, _ = _get_runner_dirs()
|
|
487
|
+
runner_root.mkdir(parents=True, exist_ok=True)
|
|
479
488
|
|
|
480
489
|
tier_order = ["build", "run", "deploy"]
|
|
481
490
|
max_idx = tier_order.index(max_tier) if max_tier in tier_order else 0
|
|
@@ -563,7 +572,8 @@ def run_pipeline(
|
|
|
563
572
|
|
|
564
573
|
# Save log
|
|
565
574
|
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
566
|
-
|
|
575
|
+
runner_root, _ = _get_runner_dirs()
|
|
576
|
+
log_path = runner_root / f"{max_tier}_log_{ts}.md"
|
|
567
577
|
|
|
568
578
|
report = (
|
|
569
579
|
f"# Runner Log — {datetime.now().isoformat(timespec='seconds')}\n"
|
|
@@ -24,14 +24,11 @@ from pathlib import Path
|
|
|
24
24
|
|
|
25
25
|
from rich.console import Console
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
import adelie.config as cfg
|
|
28
28
|
from adelie.llm_client import generate
|
|
29
29
|
|
|
30
30
|
console = Console()
|
|
31
31
|
|
|
32
|
-
TEST_ROOT = WORKSPACE_PATH.parent / "tests"
|
|
33
|
-
SCRIPTS_DIR = TEST_ROOT / "scripts"
|
|
34
|
-
RESULTS_DIR = TEST_ROOT / "results"
|
|
35
32
|
|
|
36
33
|
# Security: only these command prefixes are allowed
|
|
37
34
|
ALLOWED_COMMANDS = [
|
|
@@ -154,7 +151,7 @@ def run_tests(
|
|
|
154
151
|
Summary dict with test results.
|
|
155
152
|
"""
|
|
156
153
|
if workspace_root is None:
|
|
157
|
-
workspace_root = PROJECT_ROOT
|
|
154
|
+
workspace_root = cfg.PROJECT_ROOT
|
|
158
155
|
|
|
159
156
|
if not source_files:
|
|
160
157
|
return {"total_tests": 0, "passed": 0, "failed": 0}
|
|
@@ -240,8 +237,10 @@ def run_tests(
|
|
|
240
237
|
return {"total_tests": 0, "passed": 0, "failed": 0}
|
|
241
238
|
|
|
242
239
|
# Ensure directories
|
|
243
|
-
|
|
244
|
-
|
|
240
|
+
scripts_dir = workspace_root / ".adelie" / "tests" / "scripts"
|
|
241
|
+
results_dir = workspace_root / ".adelie" / "tests" / "results"
|
|
242
|
+
scripts_dir.mkdir(parents=True, exist_ok=True)
|
|
243
|
+
results_dir.mkdir(parents=True, exist_ok=True)
|
|
245
244
|
|
|
246
245
|
total = 0
|
|
247
246
|
passed = 0
|
|
@@ -262,7 +261,7 @@ def run_tests(
|
|
|
262
261
|
continue
|
|
263
262
|
|
|
264
263
|
# Write test script (ensure parent dirs exist for nested paths)
|
|
265
|
-
script_path =
|
|
264
|
+
script_path = scripts_dir / filename
|
|
266
265
|
script_path.parent.mkdir(parents=True, exist_ok=True)
|
|
267
266
|
script_path.write_text(content, encoding="utf-8")
|
|
268
267
|
|
|
@@ -270,18 +269,18 @@ def run_tests(
|
|
|
270
269
|
lang = script.get("language", "python")
|
|
271
270
|
if lang in ("python", "py"):
|
|
272
271
|
python = env_profile.python_bin or "python"
|
|
273
|
-
run_cmd = f
|
|
272
|
+
run_cmd = f'{python} -m pytest "{script_path}" -v --tb=short'
|
|
274
273
|
elif lang in ("javascript", "js", "typescript", "ts"):
|
|
275
274
|
ext = script_path.suffix.lower()
|
|
276
275
|
if ext in (".ts", ".tsx", ".jsx"):
|
|
277
276
|
# TypeScript/JSX: use npx tsx to execute directly
|
|
278
277
|
# (vitest's include patterns conflict with Tester AI naming)
|
|
279
|
-
run_cmd = f
|
|
278
|
+
run_cmd = f'npx tsx "{script_path}"'
|
|
280
279
|
else:
|
|
281
|
-
run_cmd = f
|
|
280
|
+
run_cmd = f'node "{script_path}"'
|
|
282
281
|
else:
|
|
283
282
|
# Fall back to LLM-provided command if language unknown
|
|
284
|
-
run_cmd = script.get("run_command", f
|
|
283
|
+
run_cmd = script.get("run_command", f'python "{script_path}"')
|
|
285
284
|
|
|
286
285
|
# Wrap command with environment strategy
|
|
287
286
|
run_cmd = wrap_command(run_cmd, env_profile, env_strategy)
|
|
@@ -316,7 +315,7 @@ def run_tests(
|
|
|
316
315
|
|
|
317
316
|
# Save results
|
|
318
317
|
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
319
|
-
result_path =
|
|
318
|
+
result_path = results_dir / f"test_run_{ts}.md"
|
|
320
319
|
|
|
321
320
|
report = (
|
|
322
321
|
f"# Test Results — {datetime.now().isoformat(timespec='seconds')}\n\n"
|
package/adelie/pyproject.toml
CHANGED
package/adelie/sandbox.py
CHANGED
|
@@ -26,7 +26,7 @@ from enum import Enum
|
|
|
26
26
|
from pathlib import Path
|
|
27
27
|
from typing import Optional
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
import adelie.config as cfg
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class SandboxMode(str, Enum):
|
|
@@ -92,7 +92,7 @@ def _get_seatbelt_profile(project_root: Path | None = None) -> str:
|
|
|
92
92
|
Get the Seatbelt profile contents.
|
|
93
93
|
If .adelie/sandbox.sb exists, use that. Otherwise, use the default.
|
|
94
94
|
"""
|
|
95
|
-
root = project_root or PROJECT_ROOT
|
|
95
|
+
root = project_root or cfg.PROJECT_ROOT
|
|
96
96
|
adelie_dir = root / ".adelie" if (root / ".adelie").exists() else None
|
|
97
97
|
|
|
98
98
|
# Check for user-defined profile
|
|
@@ -112,7 +112,7 @@ def _write_seatbelt_profile(project_root: Path | None = None) -> Path:
|
|
|
112
112
|
"""
|
|
113
113
|
Write the Seatbelt profile to a temp file and return its path.
|
|
114
114
|
"""
|
|
115
|
-
root = project_root or PROJECT_ROOT
|
|
115
|
+
root = project_root or cfg.PROJECT_ROOT
|
|
116
116
|
profile_content = _get_seatbelt_profile(root)
|
|
117
117
|
|
|
118
118
|
# Write to a temp file in .adelie/
|
|
@@ -128,7 +128,7 @@ def export_seatbelt_profile(project_root: Path | None = None) -> Path:
|
|
|
128
128
|
Export the default Seatbelt profile to .adelie/sandbox.sb for user customization.
|
|
129
129
|
Returns the path of the exported file.
|
|
130
130
|
"""
|
|
131
|
-
root = project_root or PROJECT_ROOT
|
|
131
|
+
root = project_root or cfg.PROJECT_ROOT
|
|
132
132
|
adelie_dir = root / ".adelie"
|
|
133
133
|
adelie_dir.mkdir(parents=True, exist_ok=True)
|
|
134
134
|
|
|
@@ -219,7 +219,7 @@ def _wrap_docker(
|
|
|
219
219
|
if not is_docker_available():
|
|
220
220
|
return cmd # Fallback to no sandbox
|
|
221
221
|
|
|
222
|
-
root = project_root or PROJECT_ROOT
|
|
222
|
+
root = project_root or cfg.PROJECT_ROOT
|
|
223
223
|
config = load_docker_config(root)
|
|
224
224
|
image = docker_image or config.image
|
|
225
225
|
|