refactorai-cli 0.3.9__tar.gz → 0.3.12__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.3.9 → refactorai_cli-0.3.12}/PKG-INFO +2 -2
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/pyproject.toml +2 -2
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/__init__.py +1 -1
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/cloud_rr.py +153 -16
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/run_cmds.py +7 -6
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli.egg-info/PKG-INFO +2 -2
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli.egg-info/requires.txt +1 -1
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/README.md +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/auth.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/client.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/__init__.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/account_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/auth_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/cloud_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/engine_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/model_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/rules_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/runtime_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/setup_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/toolchains_cmds.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/control_plane.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/credentials.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/local_constitution.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/local_engine_runtime.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/local_paths.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/main.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/model_policy.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/runtime_manager.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/settings.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/setup_flow.py +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli.egg-info/SOURCES.txt +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli.egg-info/dependency_links.txt +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli.egg-info/entry_points.txt +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli.egg-info/top_level.txt +0 -0
- {refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Summary: Local-first CLI for the refactor platform
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -8,7 +8,7 @@ Requires-Dist: typer>=0.12.0
|
|
|
8
8
|
Requires-Dist: httpx>=0.27.0
|
|
9
9
|
Requires-Dist: rich>=13.7.0
|
|
10
10
|
Requires-Dist: PyYAML>=6.0.1
|
|
11
|
-
Requires-Dist: refactorai-core>=3.1.
|
|
11
|
+
Requires-Dist: refactorai-core>=3.1.1
|
|
12
12
|
|
|
13
13
|
# refactorai-cli
|
|
14
14
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "refactorai-cli"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.12"
|
|
4
4
|
description = "Local-first CLI for the refactor platform"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -12,7 +12,7 @@ dependencies = [
|
|
|
12
12
|
"httpx>=0.27.0",
|
|
13
13
|
"rich>=13.7.0",
|
|
14
14
|
"PyYAML>=6.0.1",
|
|
15
|
-
"refactorai-core>=3.1.
|
|
15
|
+
"refactorai-core>=3.1.1",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[project.scripts]
|
|
@@ -22,6 +22,8 @@ this design fixes. ``tests/test_cloud_rr_client_boundary.py`` enforces this.
|
|
|
22
22
|
|
|
23
23
|
from __future__ import annotations
|
|
24
24
|
|
|
25
|
+
from collections import deque
|
|
26
|
+
import contextlib
|
|
25
27
|
import hashlib
|
|
26
28
|
import re
|
|
27
29
|
import shlex
|
|
@@ -135,6 +137,55 @@ def _run_test(
|
|
|
135
137
|
return False, (proc.stdout or proc.stderr or "")[-500:]
|
|
136
138
|
|
|
137
139
|
|
|
140
|
+
def _run_command_with_progress(
|
|
141
|
+
project_root: Path,
|
|
142
|
+
command: str,
|
|
143
|
+
*,
|
|
144
|
+
env: dict | None = None,
|
|
145
|
+
progress_cb: Callable[[dict], None] | None = None,
|
|
146
|
+
label: str = "",
|
|
147
|
+
timeout: int = 1800,
|
|
148
|
+
) -> tuple[bool, str]:
|
|
149
|
+
"""Run a host command and stream output lines as progress events.
|
|
150
|
+
|
|
151
|
+
Used for long-running dependency setup steps (e.g. ``npm install``) so the
|
|
152
|
+
CLI does not appear stuck while the process is active.
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
proc = subprocess.Popen(
|
|
156
|
+
command,
|
|
157
|
+
shell=True,
|
|
158
|
+
cwd=str(project_root),
|
|
159
|
+
stdout=subprocess.PIPE,
|
|
160
|
+
stderr=subprocess.STDOUT,
|
|
161
|
+
text=True,
|
|
162
|
+
bufsize=1,
|
|
163
|
+
env=env,
|
|
164
|
+
)
|
|
165
|
+
except (subprocess.SubprocessError, OSError) as exc:
|
|
166
|
+
return False, f"command error: {exc}"
|
|
167
|
+
|
|
168
|
+
tail: deque[str] = deque(maxlen=30)
|
|
169
|
+
try:
|
|
170
|
+
if proc.stdout is not None:
|
|
171
|
+
for raw in iter(proc.stdout.readline, ""):
|
|
172
|
+
line = raw.rstrip()
|
|
173
|
+
if not line:
|
|
174
|
+
continue
|
|
175
|
+
tail.append(line)
|
|
176
|
+
detail = f"{label}: {line}" if label else line
|
|
177
|
+
_emit(progress_cb, phase="rr", event="toolchain_provisioning", detail=detail[:240])
|
|
178
|
+
code = proc.wait(timeout=timeout)
|
|
179
|
+
except subprocess.TimeoutExpired:
|
|
180
|
+
proc.kill()
|
|
181
|
+
with contextlib.suppress(Exception):
|
|
182
|
+
proc.wait(timeout=2)
|
|
183
|
+
return False, f"command timed out after {timeout}s: {command}"
|
|
184
|
+
if code == 0:
|
|
185
|
+
return True, "ok"
|
|
186
|
+
return False, ("\n".join(tail))[-700:]
|
|
187
|
+
|
|
188
|
+
|
|
138
189
|
def _leading_binary(command: str | None) -> str | None:
|
|
139
190
|
"""Return the leading executable of a simple command, or None.
|
|
140
191
|
|
|
@@ -193,6 +244,21 @@ def _managed_toolchain_enabled(config: dict) -> bool:
|
|
|
193
244
|
return True
|
|
194
245
|
|
|
195
246
|
|
|
247
|
+
def _detected_is_provisionable(command: str | None) -> bool:
|
|
248
|
+
"""True when ``command``'s leading binary maps to a managed runtime.
|
|
249
|
+
|
|
250
|
+
Lets the resolver keep a detected command (e.g. ``npm test``) whose runtime
|
|
251
|
+
is missing from the host PATH but which the managed toolchain can download,
|
|
252
|
+
instead of discarding it and deferring the run.
|
|
253
|
+
"""
|
|
254
|
+
from refactor_core.host_toolchain import binary_to_runtime
|
|
255
|
+
|
|
256
|
+
binary = _leading_binary(command)
|
|
257
|
+
if not binary:
|
|
258
|
+
return False
|
|
259
|
+
return binary_to_runtime(binary) is not None
|
|
260
|
+
|
|
261
|
+
|
|
196
262
|
def _provision_toolchain(
|
|
197
263
|
project_root: Path,
|
|
198
264
|
test_command: str | None,
|
|
@@ -251,16 +317,72 @@ def _restore_dependencies(
|
|
|
251
317
|
present. Failures are non-fatal: the test run surfaces the real error.
|
|
252
318
|
"""
|
|
253
319
|
if runtime == "node" and (project_root / "package.json").is_file():
|
|
254
|
-
_emit(
|
|
255
|
-
|
|
320
|
+
_emit(
|
|
321
|
+
progress_cb,
|
|
322
|
+
phase="rr",
|
|
323
|
+
event="toolchain_provisioning",
|
|
324
|
+
detail="step 1/1: npm install (streaming output below)",
|
|
325
|
+
)
|
|
326
|
+
ok, detail = _run_command_with_progress(
|
|
327
|
+
project_root,
|
|
328
|
+
"npm install",
|
|
329
|
+
env=env,
|
|
330
|
+
progress_cb=progress_cb,
|
|
331
|
+
label="npm",
|
|
332
|
+
timeout=1800,
|
|
333
|
+
)
|
|
334
|
+
if not ok:
|
|
335
|
+
_emit(
|
|
336
|
+
progress_cb,
|
|
337
|
+
phase="rr",
|
|
338
|
+
event="toolchain_provisioning",
|
|
339
|
+
detail=f"npm install finished with errors: {detail}",
|
|
340
|
+
)
|
|
256
341
|
elif runtime == "python":
|
|
257
342
|
if (project_root / "requirements.txt").is_file():
|
|
258
|
-
_emit(
|
|
259
|
-
|
|
260
|
-
|
|
343
|
+
_emit(
|
|
344
|
+
progress_cb,
|
|
345
|
+
phase="rr",
|
|
346
|
+
event="toolchain_provisioning",
|
|
347
|
+
detail="step 1/1: pip install -r requirements.txt (streaming output below)",
|
|
348
|
+
)
|
|
349
|
+
ok, detail = _run_command_with_progress(
|
|
350
|
+
project_root,
|
|
351
|
+
"python -m pip install -r requirements.txt",
|
|
352
|
+
env=env,
|
|
353
|
+
progress_cb=progress_cb,
|
|
354
|
+
label="pip",
|
|
355
|
+
timeout=1800,
|
|
356
|
+
)
|
|
357
|
+
if not ok:
|
|
358
|
+
_emit(
|
|
359
|
+
progress_cb,
|
|
360
|
+
phase="rr",
|
|
361
|
+
event="toolchain_provisioning",
|
|
362
|
+
detail=f"pip install finished with errors: {detail}",
|
|
363
|
+
)
|
|
261
364
|
elif (project_root / "pyproject.toml").is_file() or (project_root / "setup.py").is_file():
|
|
262
|
-
_emit(
|
|
263
|
-
|
|
365
|
+
_emit(
|
|
366
|
+
progress_cb,
|
|
367
|
+
phase="rr",
|
|
368
|
+
event="toolchain_provisioning",
|
|
369
|
+
detail="step 1/1: pip install -e . (streaming output below)",
|
|
370
|
+
)
|
|
371
|
+
ok, detail = _run_command_with_progress(
|
|
372
|
+
project_root,
|
|
373
|
+
"python -m pip install -e .",
|
|
374
|
+
env=env,
|
|
375
|
+
progress_cb=progress_cb,
|
|
376
|
+
label="pip",
|
|
377
|
+
timeout=1800,
|
|
378
|
+
)
|
|
379
|
+
if not ok:
|
|
380
|
+
_emit(
|
|
381
|
+
progress_cb,
|
|
382
|
+
phase="rr",
|
|
383
|
+
event="toolchain_provisioning",
|
|
384
|
+
detail=f"pip install finished with errors: {detail}",
|
|
385
|
+
)
|
|
264
386
|
|
|
265
387
|
|
|
266
388
|
def _collect_test_manifests(
|
|
@@ -396,13 +518,22 @@ def _resolve_cloud_test_command(
|
|
|
396
518
|
"toolchain or update verification.tests.command."
|
|
397
519
|
)
|
|
398
520
|
|
|
399
|
-
# 2) Root-marker / monorepo detection
|
|
521
|
+
# 2) Root-marker / monorepo detection. Usable if runnable on the host OR if
|
|
522
|
+
# its runtime can be provisioned by the managed toolchain (the caller then
|
|
523
|
+
# downloads it before the baseline). Only when neither holds do we fall
|
|
524
|
+
# through to server discovery, so we never lock onto a command that is
|
|
525
|
+
# truly un-runnable (e.g. a Tier-3 binary with no managed runtime).
|
|
400
526
|
detected = resolve_test_command(project_root, config) or discover_test_command(project_root)
|
|
401
527
|
if detected:
|
|
402
528
|
if _client_preflight_command(project_root, detected):
|
|
403
529
|
return detected, f"auto-detected test command: {detected}"
|
|
404
|
-
|
|
405
|
-
|
|
530
|
+
if _managed_toolchain_enabled(config) and _detected_is_provisionable(detected):
|
|
531
|
+
return detected, (
|
|
532
|
+
f"auto-detected test command: {detected} "
|
|
533
|
+
"(runtime not on host PATH; will provision a managed toolchain)"
|
|
534
|
+
)
|
|
535
|
+
# Detected but neither runnable nor provisionable -> fall through to
|
|
536
|
+
# server discovery rather than committing to an un-runnable command.
|
|
406
537
|
detected_note = (
|
|
407
538
|
f"auto-detected test command '{detected}' but its toolchain was not "
|
|
408
539
|
"found on the host PATH; trying server discovery."
|
|
@@ -545,6 +676,18 @@ def run_cloud_refactor_requests(
|
|
|
545
676
|
apply_byok_payload=apply_byok_payload,
|
|
546
677
|
)
|
|
547
678
|
|
|
679
|
+
if requests is None:
|
|
680
|
+
requests = pending_requests(project_root)
|
|
681
|
+
|
|
682
|
+
_emit(
|
|
683
|
+
progress_cb,
|
|
684
|
+
phase="rr",
|
|
685
|
+
event="run_start",
|
|
686
|
+
total_requests=len(requests),
|
|
687
|
+
test_command=test_command or "",
|
|
688
|
+
execution_env="cloud",
|
|
689
|
+
)
|
|
690
|
+
|
|
548
691
|
# Managed host toolchain: when the resolved command's runtime is missing from
|
|
549
692
|
# the host PATH, download a pinned runtime into the central store and run
|
|
550
693
|
# tests with it (no container, no system changes). ``toolchain_env`` is None
|
|
@@ -557,18 +700,12 @@ def run_cloud_refactor_requests(
|
|
|
557
700
|
progress_cb=progress_cb,
|
|
558
701
|
)
|
|
559
702
|
|
|
560
|
-
if requests is None:
|
|
561
|
-
requests = pending_requests(project_root)
|
|
562
|
-
|
|
563
703
|
run_store = create_run(project_root)
|
|
564
704
|
run_id = run_store.run_id
|
|
565
705
|
resolved_model = model_hint or str(
|
|
566
706
|
constitution.get_setting("model_id", "managed-default") or "managed-default"
|
|
567
707
|
)
|
|
568
708
|
|
|
569
|
-
_emit(progress_cb, phase="rr", event="run_start", total_requests=len(requests),
|
|
570
|
-
test_command=test_command or "", execution_env="cloud")
|
|
571
|
-
|
|
572
709
|
outcomes: list[dict] = []
|
|
573
710
|
counts = {"applied": 0, "denied": 0, "failed": 0, "deferred": 0}
|
|
574
711
|
notes: list[str] = []
|
|
@@ -464,8 +464,10 @@ def _toolchain_consent_prompt(*, auto_approve: bool) -> Callable[[dict], bool]:
|
|
|
464
464
|
"""First-run consent for downloading a managed language runtime.
|
|
465
465
|
|
|
466
466
|
Managed toolchains are downloaded into the central store (``~/.refactor``),
|
|
467
|
-
never system paths
|
|
468
|
-
|
|
467
|
+
never system paths. To avoid an apparent "stuck" run when Rich status
|
|
468
|
+
spinners are active, interactive runs default to approve after showing an
|
|
469
|
+
informational message (no blocking prompt). ``--auto-approve`` and
|
|
470
|
+
non-interactive terminals proceed silently.
|
|
469
471
|
"""
|
|
470
472
|
|
|
471
473
|
def _ask(info: dict) -> bool:
|
|
@@ -474,11 +476,10 @@ def _toolchain_consent_prompt(*, auto_approve: bool) -> Callable[[dict], bool]:
|
|
|
474
476
|
if auto_approve or not sys.stdin.isatty():
|
|
475
477
|
return True
|
|
476
478
|
console.print(
|
|
477
|
-
f"[dim]
|
|
478
|
-
f"pinned {runtime} runtime
|
|
479
|
+
f"[dim]{binary} not found on your PATH. "
|
|
480
|
+
f"Refactor is downloading a pinned {runtime} runtime for testing.[/dim]"
|
|
479
481
|
)
|
|
480
|
-
|
|
481
|
-
return raw.strip().lower() in {"", "y", "yes"}
|
|
482
|
+
return True
|
|
482
483
|
|
|
483
484
|
return _ask
|
|
484
485
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Summary: Local-first CLI for the refactor platform
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -8,7 +8,7 @@ Requires-Dist: typer>=0.12.0
|
|
|
8
8
|
Requires-Dist: httpx>=0.27.0
|
|
9
9
|
Requires-Dist: rich>=13.7.0
|
|
10
10
|
Requires-Dist: PyYAML>=6.0.1
|
|
11
|
-
Requires-Dist: refactorai-core>=3.1.
|
|
11
|
+
Requires-Dist: refactorai-core>=3.1.1
|
|
12
12
|
|
|
13
13
|
# refactorai-cli
|
|
14
14
|
|
|
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
|
{refactorai_cli-0.3.9 → refactorai_cli-0.3.12}/refactorai_cli/commands/runtime_proxy_cmds.py
RENAMED
|
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
|