refactorai-cli 0.2.4__tar.gz → 0.2.5__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.
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/PKG-INFO +1 -1
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/pyproject.toml +1 -1
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/__init__.py +1 -1
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/setup_cmds.py +1 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/local_engine_runtime.py +11 -1
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/setup_flow.py +46 -8
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/PKG-INFO +1 -1
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/README.md +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/auth.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/client.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/__init__.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/auth_cmds.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/engine_cmds.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/model_cmds.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/rules_cmds.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/run_cmds.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/runtime_cmds.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/control_plane.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/credentials.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/local_constitution.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/local_paths.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/main.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/model_policy.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/runtime_manager.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/settings.py +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/SOURCES.txt +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/dependency_links.txt +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/entry_points.txt +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/requires.txt +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/top_level.txt +0 -0
- {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/setup.cfg +0 -0
|
@@ -53,6 +53,7 @@ def setup(
|
|
|
53
53
|
resume=resume,
|
|
54
54
|
from_stage=from_stage,
|
|
55
55
|
ask_approval=lambda prompt: _ask_approval(yes, prompt),
|
|
56
|
+
on_progress=lambda message: console.print(f"[cyan]{message}[/cyan]"),
|
|
56
57
|
)
|
|
57
58
|
except SetupError as exc:
|
|
58
59
|
console.print(f"[red]Setup failed:[/red] {exc}")
|
|
@@ -7,6 +7,7 @@ remain usable without a separate ``refactor-core`` package dependency.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import json
|
|
10
|
+
import re
|
|
10
11
|
import shutil
|
|
11
12
|
import subprocess
|
|
12
13
|
from datetime import datetime, timezone
|
|
@@ -22,6 +23,8 @@ ENGINE_DIR = "engine"
|
|
|
22
23
|
ENGINE_STATE_FILE = "engine.json"
|
|
23
24
|
ENGINE_MODELS_DIR = "models"
|
|
24
25
|
ENGINE_CACHE_DIR = "cache/ollama"
|
|
26
|
+
_ANSI_RE = re.compile(r"\x1b\[[0-9;?]*[A-Za-z]")
|
|
27
|
+
_CONTROL_RE = re.compile(r"[\x00-\x08\x0b-\x1f\x7f]")
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
def _now_iso() -> str:
|
|
@@ -85,6 +88,13 @@ def _run(cmd: list[str]) -> subprocess.CompletedProcess:
|
|
|
85
88
|
return subprocess.run(cmd, capture_output=True, text=True)
|
|
86
89
|
|
|
87
90
|
|
|
91
|
+
def _clean_output(text: str) -> str:
|
|
92
|
+
cleaned = _ANSI_RE.sub("", text or "")
|
|
93
|
+
cleaned = cleaned.replace("\r", "\n")
|
|
94
|
+
cleaned = _CONTROL_RE.sub("", cleaned)
|
|
95
|
+
return "\n".join(line.strip() for line in cleaned.splitlines() if line.strip())
|
|
96
|
+
|
|
97
|
+
|
|
88
98
|
def _exists(runtime: str, name: str) -> bool:
|
|
89
99
|
proc = _run([runtime, "container", "exists", name])
|
|
90
100
|
return proc.returncode == 0
|
|
@@ -235,7 +245,7 @@ def pull_model(*, runtime: str, container_name: str, model_id: str) -> tuple[boo
|
|
|
235
245
|
if probe.returncode != 0:
|
|
236
246
|
return False, "Engine image does not include `ollama`; use a model-capable engine image."
|
|
237
247
|
proc = _run([runtime, "exec", container_name, "sh", "-lc", f"ollama pull {model_id}"])
|
|
238
|
-
text = (proc.stdout or proc.stderr or "")
|
|
248
|
+
text = _clean_output(proc.stdout or proc.stderr or "")
|
|
239
249
|
if proc.returncode != 0:
|
|
240
250
|
return False, text or "Model pull failed."
|
|
241
251
|
return True, text or "Model pull complete."
|
|
@@ -7,6 +7,7 @@ import shutil
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from datetime import datetime, timezone
|
|
9
9
|
from pathlib import Path
|
|
10
|
+
from inspect import signature
|
|
10
11
|
from typing import Callable
|
|
11
12
|
|
|
12
13
|
from refactorai_cli.local_engine_runtime import (
|
|
@@ -142,7 +143,7 @@ def get_setup_diagnostics() -> dict:
|
|
|
142
143
|
return {"state": state, "stages": stages}
|
|
143
144
|
|
|
144
145
|
|
|
145
|
-
def _stage_s1_precheck(_ask_approval: Callable[[str], bool]) -> dict:
|
|
146
|
+
def _stage_s1_precheck(_ask_approval: Callable[[str], bool], _emit: Callable[[str], None] | None = None) -> dict:
|
|
146
147
|
profile = detect_machine_profile()
|
|
147
148
|
runtime, reason = resolve_engine_runtime("podman")
|
|
148
149
|
disk = shutil.disk_usage(str(refactor_home()))
|
|
@@ -159,7 +160,7 @@ def _stage_s1_precheck(_ask_approval: Callable[[str], bool]) -> dict:
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
|
|
162
|
-
def _stage_s2_auth(_ask_approval: Callable[[str], bool]) -> dict:
|
|
163
|
+
def _stage_s2_auth(_ask_approval: Callable[[str], bool], _emit: Callable[[str], None] | None = None) -> dict:
|
|
163
164
|
auth = ensure_authenticated(force_remote=True)
|
|
164
165
|
lease = ensure_lease(force_refresh=True)
|
|
165
166
|
return {
|
|
@@ -171,11 +172,17 @@ def _stage_s2_auth(_ask_approval: Callable[[str], bool]) -> dict:
|
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
|
|
174
|
-
def _stage_s3_runtime(ask_approval: Callable[[str], bool]) -> dict:
|
|
175
|
+
def _stage_s3_runtime(ask_approval: Callable[[str], bool], emit: Callable[[str], None] | None = None) -> dict:
|
|
176
|
+
if emit:
|
|
177
|
+
emit("Resolving runtime manifest from control plane...")
|
|
175
178
|
manifest = resolve_runtime_manifest(channel="stable")
|
|
176
179
|
if not ask_approval(f"Install runtime artifact version {manifest.runtime_version}?"):
|
|
177
180
|
raise SetupError("Runtime installation was not approved.")
|
|
181
|
+
if emit:
|
|
182
|
+
emit(f"Downloading runtime artifact {manifest.runtime_version}...")
|
|
178
183
|
artifact = download_artifact(manifest.artifact_url)
|
|
184
|
+
if emit:
|
|
185
|
+
emit("Verifying and activating runtime artifact...")
|
|
179
186
|
path = activate_runtime(manifest, artifact, channel="stable")
|
|
180
187
|
status = runtime_status()
|
|
181
188
|
return {
|
|
@@ -200,7 +207,9 @@ def _selected_backend() -> str:
|
|
|
200
207
|
return backend
|
|
201
208
|
|
|
202
209
|
|
|
203
|
-
def _stage_s4_backend_choice(
|
|
210
|
+
def _stage_s4_backend_choice(
|
|
211
|
+
ask_approval: Callable[[str], bool], _emit: Callable[[str], None] | None = None
|
|
212
|
+
) -> dict:
|
|
204
213
|
if ask_approval("Install local refactor-server and local model now?"):
|
|
205
214
|
backend = BACKEND_LOCAL_SERVER
|
|
206
215
|
else:
|
|
@@ -211,7 +220,7 @@ def _stage_s4_backend_choice(ask_approval: Callable[[str], bool]) -> dict:
|
|
|
211
220
|
}
|
|
212
221
|
|
|
213
222
|
|
|
214
|
-
def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
|
|
223
|
+
def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool], emit: Callable[[str], None] | None = None) -> dict:
|
|
215
224
|
backend = _selected_backend()
|
|
216
225
|
if backend == BACKEND_BYOK:
|
|
217
226
|
return {
|
|
@@ -222,9 +231,13 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
|
|
|
222
231
|
}
|
|
223
232
|
if not ask_approval("Install/start local refactor-server and bootstrap model now?"):
|
|
224
233
|
raise SetupError("Local server bootstrap was not approved.")
|
|
234
|
+
if emit:
|
|
235
|
+
emit("Checking local engine runtime availability...")
|
|
225
236
|
runtime, reason = resolve_engine_runtime("podman")
|
|
226
237
|
if not runtime:
|
|
227
238
|
raise SetupError(reason or "Engine runtime unavailable.")
|
|
239
|
+
if emit:
|
|
240
|
+
emit("Starting local refactor-engine container...")
|
|
228
241
|
ok, message, state = ensure_engine_up(runtime=runtime)
|
|
229
242
|
if not ok:
|
|
230
243
|
raise SetupError(f"Local server bootstrap failed: {message}")
|
|
@@ -232,6 +245,8 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
|
|
|
232
245
|
if str(estatus.get("status") or "").lower() != "ready":
|
|
233
246
|
# If a stale container from older installs exists (for example an image
|
|
234
247
|
# without ollama), rebuild once with the default engine image.
|
|
248
|
+
if emit:
|
|
249
|
+
emit("Detected non-ready engine container. Rebuilding once with default model runtime image...")
|
|
235
250
|
ok, message, _state = ensure_engine_up(
|
|
236
251
|
runtime=runtime,
|
|
237
252
|
image=DEFAULT_ENGINE_IMAGE,
|
|
@@ -247,6 +262,8 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
|
|
|
247
262
|
"Run `refactor engine status` and retry from S5."
|
|
248
263
|
)
|
|
249
264
|
|
|
265
|
+
if emit:
|
|
266
|
+
emit("Resolving signed model policy...")
|
|
250
267
|
policy = resolve_policy(force_refresh=False)
|
|
251
268
|
profile = detect_machine_profile()
|
|
252
269
|
model_id = recommended_model_id(policy, profile=str(profile["profile"]))
|
|
@@ -261,6 +278,11 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
|
|
|
261
278
|
runtime, reason = resolve_engine_runtime("podman")
|
|
262
279
|
if not runtime:
|
|
263
280
|
raise SetupError(reason or "Engine runtime unavailable.")
|
|
281
|
+
if emit:
|
|
282
|
+
emit(
|
|
283
|
+
f"Pulling recommended model '{model_id}' in local engine "
|
|
284
|
+
"(this can take several minutes on first download)..."
|
|
285
|
+
)
|
|
264
286
|
ok, message = pull_model(runtime=runtime, container_name=DEFAULT_ENGINE_CONTAINER, model_id=model_id)
|
|
265
287
|
if not ok:
|
|
266
288
|
raise SetupError(f"Model pull failed: {message}")
|
|
@@ -273,7 +295,7 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
|
|
|
273
295
|
}
|
|
274
296
|
|
|
275
297
|
|
|
276
|
-
def _stage_s6_validate(_ask_approval: Callable[[str], bool]) -> dict:
|
|
298
|
+
def _stage_s6_validate(_ask_approval: Callable[[str], bool], _emit: Callable[[str], None] | None = None) -> dict:
|
|
277
299
|
backend = _selected_backend()
|
|
278
300
|
runtime_status_data = runtime_status()
|
|
279
301
|
if not runtime_status_data.get("active_version") or not runtime_status_data.get("active_artifact_exists"):
|
|
@@ -309,7 +331,7 @@ def _stage_s6_validate(_ask_approval: Callable[[str], bool]) -> dict:
|
|
|
309
331
|
}
|
|
310
332
|
|
|
311
333
|
|
|
312
|
-
STAGE_HANDLERS: dict[str, Callable[
|
|
334
|
+
STAGE_HANDLERS: dict[str, Callable[..., dict]] = {
|
|
313
335
|
"S1": _stage_s1_precheck,
|
|
314
336
|
"S2": _stage_s2_auth,
|
|
315
337
|
"S3": _stage_s3_runtime,
|
|
@@ -319,12 +341,24 @@ STAGE_HANDLERS: dict[str, Callable[[Callable[[str], bool]], dict]] = {
|
|
|
319
341
|
}
|
|
320
342
|
|
|
321
343
|
|
|
344
|
+
def _invoke_stage_handler(
|
|
345
|
+
handler: Callable,
|
|
346
|
+
ask_approval: Callable[[str], bool],
|
|
347
|
+
on_progress: Callable[[str], None] | None,
|
|
348
|
+
) -> dict:
|
|
349
|
+
param_names = list(signature(handler).parameters.keys())
|
|
350
|
+
if any(name in {"emit", "_emit", "on_progress"} for name in param_names):
|
|
351
|
+
return handler(ask_approval, on_progress)
|
|
352
|
+
return handler(ask_approval)
|
|
353
|
+
|
|
354
|
+
|
|
322
355
|
def run_setup(
|
|
323
356
|
*,
|
|
324
357
|
auto_approve: bool = False,
|
|
325
358
|
resume: bool = False,
|
|
326
359
|
from_stage: str | None = None,
|
|
327
360
|
ask_approval: Callable[[str], bool] | None = None,
|
|
361
|
+
on_progress: Callable[[str], None] | None = None,
|
|
328
362
|
) -> SetupResult:
|
|
329
363
|
if ask_approval is None:
|
|
330
364
|
ask_approval = lambda _prompt: bool(auto_approve)
|
|
@@ -360,6 +394,8 @@ def run_setup(
|
|
|
360
394
|
}
|
|
361
395
|
)
|
|
362
396
|
for stage_id in STAGE_IDS[start_index:]:
|
|
397
|
+
if on_progress:
|
|
398
|
+
on_progress(f"Running stage {stage_id} ({STAGE_NAMES.get(stage_id, stage_id)})...")
|
|
363
399
|
write_setup_state(
|
|
364
400
|
{
|
|
365
401
|
"status": "in_progress",
|
|
@@ -371,7 +407,7 @@ def run_setup(
|
|
|
371
407
|
)
|
|
372
408
|
handler = STAGE_HANDLERS[stage_id]
|
|
373
409
|
try:
|
|
374
|
-
output = handler
|
|
410
|
+
output = _invoke_stage_handler(handler, ask_approval, on_progress)
|
|
375
411
|
except SetupError as exc:
|
|
376
412
|
write_setup_state(
|
|
377
413
|
{
|
|
@@ -410,6 +446,8 @@ def run_setup(
|
|
|
410
446
|
write_stage_output(stage_id, output)
|
|
411
447
|
if stage_id not in completed:
|
|
412
448
|
completed.append(stage_id)
|
|
449
|
+
if on_progress:
|
|
450
|
+
on_progress(f"Completed stage {stage_id}.")
|
|
413
451
|
final_backend = _normalize_backend(read_setup_state().get("execution_backend"))
|
|
414
452
|
write_setup_state(
|
|
415
453
|
{
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|