refactorai-cli 0.2.9__tar.gz → 0.2.11__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.9 → refactorai_cli-0.2.11}/PKG-INFO +2 -2
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/README.md +1 -1
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/pyproject.toml +1 -1
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/__init__.py +1 -1
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/run_cmds.py +65 -1
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/control_plane.py +2 -1
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/local_engine_runtime.py +69 -10
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/runtime_manager.py +2 -1
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/PKG-INFO +2 -2
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/auth.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/client.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/__init__.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/auth_cmds.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/engine_cmds.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/model_cmds.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/rules_cmds.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/runtime_cmds.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/commands/setup_cmds.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/credentials.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/local_constitution.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/local_paths.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/main.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/model_policy.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/settings.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli/setup_flow.py +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/SOURCES.txt +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/dependency_links.txt +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/entry_points.txt +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/requires.txt +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/top_level.txt +0 -0
- {refactorai_cli-0.2.9 → refactorai_cli-0.2.11}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.11
|
|
4
4
|
Summary: Local-first CLI for the refactor platform
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -53,6 +53,6 @@ python -m twine upload ./refactorai-cli/dist/*
|
|
|
53
53
|
## Install test (local)
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.
|
|
56
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.11-py3-none-any.whl
|
|
57
57
|
refactor --version
|
|
58
58
|
```
|
|
@@ -42,6 +42,6 @@ python -m twine upload ./refactorai-cli/dist/*
|
|
|
42
42
|
## Install test (local)
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.
|
|
45
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.11-py3-none-any.whl
|
|
46
46
|
refactor --version
|
|
47
47
|
```
|
|
@@ -8,6 +8,7 @@ generation and application.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
|
+
import json
|
|
11
12
|
import re
|
|
12
13
|
import shlex
|
|
13
14
|
import shutil
|
|
@@ -92,10 +93,62 @@ from refactor_core.store import (
|
|
|
92
93
|
from refactor_core.testcmd import resolve_test_command
|
|
93
94
|
|
|
94
95
|
from refactorai_cli.auth import AuthError, ensure_authenticated
|
|
96
|
+
from refactorai_cli.local_engine_runtime import DEFAULT_ENGINE_PORT, read_engine_state
|
|
95
97
|
from refactorai_cli.settings import platform_url
|
|
98
|
+
from refactorai_cli.setup_flow import (
|
|
99
|
+
BACKEND_BYOK,
|
|
100
|
+
BACKEND_LOCAL_SERVER,
|
|
101
|
+
read_setup_state,
|
|
102
|
+
stage_output_path,
|
|
103
|
+
)
|
|
96
104
|
|
|
97
105
|
console = Console()
|
|
98
106
|
|
|
107
|
+
|
|
108
|
+
def _read_stage_output(stage_id: str) -> dict:
|
|
109
|
+
path = stage_output_path(stage_id)
|
|
110
|
+
if not path.is_file():
|
|
111
|
+
return {}
|
|
112
|
+
try:
|
|
113
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
114
|
+
except (json.JSONDecodeError, OSError):
|
|
115
|
+
return {}
|
|
116
|
+
output = payload.get("output")
|
|
117
|
+
return output if isinstance(output, dict) else {}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _setup_recommended_model_id() -> str:
|
|
121
|
+
s5_output = _read_stage_output("S5")
|
|
122
|
+
model_id = str(s5_output.get("recommended_model_id") or "").strip()
|
|
123
|
+
return model_id or "qwen2.5-coder:1.5b-instruct"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _setup_engine_base_url() -> str:
|
|
127
|
+
state = read_engine_state()
|
|
128
|
+
try:
|
|
129
|
+
port = int(state.get("port", DEFAULT_ENGINE_PORT))
|
|
130
|
+
except (TypeError, ValueError):
|
|
131
|
+
port = DEFAULT_ENGINE_PORT
|
|
132
|
+
return f"http://127.0.0.1:{port}/v1"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _default_config_for_init() -> tuple[str, str]:
|
|
136
|
+
setup_state = read_setup_state()
|
|
137
|
+
backend = str(setup_state.get("execution_backend") or "").strip().lower()
|
|
138
|
+
if backend == BACKEND_LOCAL_SERVER:
|
|
139
|
+
model_id = _setup_recommended_model_id()
|
|
140
|
+
base_url = _setup_engine_base_url()
|
|
141
|
+
config_text = DEFAULT_CONFIG.replace("provider: heuristic", "provider: ollama", 1)
|
|
142
|
+
config_text = config_text.replace(
|
|
143
|
+
"model_id: heuristic-local-v1",
|
|
144
|
+
f"model_id: {model_id}\nbase_url: {base_url}",
|
|
145
|
+
1,
|
|
146
|
+
)
|
|
147
|
+
return config_text, "local_server"
|
|
148
|
+
if backend == BACKEND_BYOK:
|
|
149
|
+
return DEFAULT_CONFIG, "byok"
|
|
150
|
+
return DEFAULT_CONFIG, "default"
|
|
151
|
+
|
|
99
152
|
_SEVERITY_STYLE = {
|
|
100
153
|
"critical": "bold red",
|
|
101
154
|
"high": "red",
|
|
@@ -603,12 +656,23 @@ def init(
|
|
|
603
656
|
)
|
|
604
657
|
raise typer.Exit(code=1)
|
|
605
658
|
consti_target.write_text(DEFAULT_CONSTITUTION, encoding="utf-8")
|
|
606
|
-
|
|
659
|
+
config_text, config_profile = _default_config_for_init()
|
|
660
|
+
config_target.write_text(config_text, encoding="utf-8")
|
|
607
661
|
|
|
608
662
|
constitution = load_constitution(consti_target)
|
|
609
663
|
record = register_project(project_root, constitution_hash=constitution.content_hash)
|
|
610
664
|
console.print(f"[green]Created[/green] {consti_target}")
|
|
611
665
|
console.print(f"[green]Created[/green] {config_target}")
|
|
666
|
+
if config_profile == "local_server":
|
|
667
|
+
console.print(
|
|
668
|
+
"[green]Configured[/green] refactor.config for local server setup "
|
|
669
|
+
"(provider=ollama with setup-recommended model)."
|
|
670
|
+
)
|
|
671
|
+
elif config_profile == "byok":
|
|
672
|
+
console.print(
|
|
673
|
+
"[green]Configured[/green] refactor.config for BYOK setup "
|
|
674
|
+
"(provider=heuristic; uncomment a provider block to use your key)."
|
|
675
|
+
)
|
|
612
676
|
console.print(
|
|
613
677
|
f"[green]Registered[/green] project in central store: {project_store_dir(project_root)}"
|
|
614
678
|
)
|
|
@@ -12,6 +12,7 @@ from pathlib import Path
|
|
|
12
12
|
|
|
13
13
|
import httpx
|
|
14
14
|
|
|
15
|
+
from refactorai_cli import __version__
|
|
15
16
|
from refactorai_cli.local_paths import ensure_dir, refactor_home
|
|
16
17
|
|
|
17
18
|
from refactorai_cli.credentials import resolve_developer_key
|
|
@@ -115,7 +116,7 @@ def read_cached_lease() -> LeaseContext | None:
|
|
|
115
116
|
return _from_cached(payload)
|
|
116
117
|
|
|
117
118
|
|
|
118
|
-
def request_lease(*, timeout: float = 20.0, cli_version: str =
|
|
119
|
+
def request_lease(*, timeout: float = 20.0, cli_version: str = __version__) -> LeaseContext:
|
|
119
120
|
resolved = resolve_developer_key()
|
|
120
121
|
if not resolved:
|
|
121
122
|
raise RuntimeError(
|
|
@@ -10,6 +10,9 @@ import json
|
|
|
10
10
|
import re
|
|
11
11
|
import shutil
|
|
12
12
|
import subprocess
|
|
13
|
+
import time
|
|
14
|
+
import urllib.error
|
|
15
|
+
import urllib.request
|
|
13
16
|
from datetime import datetime, timezone
|
|
14
17
|
from pathlib import Path
|
|
15
18
|
|
|
@@ -18,6 +21,8 @@ from refactorai_cli.local_paths import ensure_dir, refactor_home
|
|
|
18
21
|
DEFAULT_ENGINE_IMAGE = "docker.io/ollama/ollama:latest"
|
|
19
22
|
DEFAULT_ENGINE_CONTAINER = "refactor-engine"
|
|
20
23
|
DEFAULT_ENGINE_PORT = 17777
|
|
24
|
+
# Ollama serves inside the container on 11434.
|
|
25
|
+
ENGINE_API_PORT = 11434
|
|
21
26
|
DEFAULT_ENGINE_PROFILE = "cpu-small"
|
|
22
27
|
ENGINE_DIR = "engine"
|
|
23
28
|
ENGINE_STATE_FILE = "engine.json"
|
|
@@ -105,12 +110,56 @@ def _running(runtime: str, name: str) -> bool:
|
|
|
105
110
|
return proc.returncode == 0 and proc.stdout.strip().lower() == "true"
|
|
106
111
|
|
|
107
112
|
|
|
108
|
-
def
|
|
113
|
+
def _http_health(host_port: int, *, retries: int = 8, delay_s: float = 0.25) -> bool:
|
|
114
|
+
url = f"http://127.0.0.1:{int(host_port)}/api/tags"
|
|
115
|
+
for attempt in range(max(1, int(retries))):
|
|
116
|
+
try:
|
|
117
|
+
with urllib.request.urlopen(url, timeout=2.0) as response:
|
|
118
|
+
code = int(getattr(response, "status", 0) or 0)
|
|
119
|
+
if 200 <= code < 500:
|
|
120
|
+
return True
|
|
121
|
+
except (urllib.error.URLError, OSError, ValueError):
|
|
122
|
+
if attempt + 1 < retries:
|
|
123
|
+
time.sleep(delay_s)
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _health(runtime: str, name: str, *, host_port: int | None = None) -> bool:
|
|
109
128
|
has_binary = _run([runtime, "exec", name, "sh", "-lc", "command -v ollama >/dev/null 2>&1"])
|
|
110
129
|
if has_binary.returncode != 0:
|
|
111
130
|
return False
|
|
112
131
|
proc = _run([runtime, "exec", name, "sh", "-lc", "ollama list >/dev/null 2>&1"])
|
|
113
|
-
|
|
132
|
+
if proc.returncode != 0:
|
|
133
|
+
return False
|
|
134
|
+
if host_port is None:
|
|
135
|
+
return True
|
|
136
|
+
return _http_health(int(host_port))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _published_host_port(runtime: str, name: str, container_port: int = ENGINE_API_PORT) -> int | None:
|
|
140
|
+
proc = _run([runtime, "port", name, str(int(container_port))])
|
|
141
|
+
if proc.returncode != 0:
|
|
142
|
+
return None
|
|
143
|
+
line = (proc.stdout or "").strip().splitlines()
|
|
144
|
+
if not line:
|
|
145
|
+
return None
|
|
146
|
+
candidate = line[0].strip()
|
|
147
|
+
# Typical outputs:
|
|
148
|
+
# - "127.0.0.1:17777"
|
|
149
|
+
# - "[::1]:17777"
|
|
150
|
+
# - "0.0.0.0:17777"
|
|
151
|
+
if ":" not in candidate:
|
|
152
|
+
return None
|
|
153
|
+
port_text = candidate.rsplit(":", 1)[-1].strip()
|
|
154
|
+
try:
|
|
155
|
+
return int(port_text)
|
|
156
|
+
except (TypeError, ValueError):
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _port_mapping_matches(runtime: str, name: str, host_port: int) -> bool:
|
|
161
|
+
published = _published_host_port(runtime, name, ENGINE_API_PORT)
|
|
162
|
+
return published == int(host_port)
|
|
114
163
|
|
|
115
164
|
|
|
116
165
|
def _run_args_for_image(image: str) -> list[str]:
|
|
@@ -131,11 +180,17 @@ def ensure_engine_up(
|
|
|
131
180
|
port: int = DEFAULT_ENGINE_PORT,
|
|
132
181
|
rebuild: bool = False,
|
|
133
182
|
) -> tuple[bool, str, dict]:
|
|
183
|
+
resolved_port = int(port)
|
|
134
184
|
models_dir = ensure_dir(refactor_home() / ENGINE_MODELS_DIR)
|
|
135
185
|
cache_dir = ensure_dir(refactor_home() / ENGINE_CACHE_DIR)
|
|
136
|
-
|
|
186
|
+
exists = _exists(runtime, container_name)
|
|
187
|
+
if rebuild and exists:
|
|
137
188
|
_run([runtime, "rm", "-f", container_name])
|
|
138
|
-
|
|
189
|
+
exists = False
|
|
190
|
+
if exists and not _port_mapping_matches(runtime, container_name, resolved_port):
|
|
191
|
+
_run([runtime, "rm", "-f", container_name])
|
|
192
|
+
exists = False
|
|
193
|
+
if not exists:
|
|
139
194
|
command_args = _run_args_for_image(image)
|
|
140
195
|
proc = _run(
|
|
141
196
|
[
|
|
@@ -145,7 +200,7 @@ def ensure_engine_up(
|
|
|
145
200
|
"--name",
|
|
146
201
|
container_name,
|
|
147
202
|
"-p",
|
|
148
|
-
f"127.0.0.1:{
|
|
203
|
+
f"127.0.0.1:{resolved_port}:{ENGINE_API_PORT}",
|
|
149
204
|
"-v",
|
|
150
205
|
f"{models_dir}:/root/.ollama/models:Z",
|
|
151
206
|
"-v",
|
|
@@ -162,16 +217,19 @@ def ensure_engine_up(
|
|
|
162
217
|
if proc.returncode != 0:
|
|
163
218
|
detail = (proc.stderr or proc.stdout or "").strip()
|
|
164
219
|
return False, f"Failed to start engine container: {detail}", {}
|
|
220
|
+
status = "ready" if _health(runtime, container_name, host_port=resolved_port) else "degraded"
|
|
165
221
|
state = {
|
|
166
222
|
"runtime": runtime,
|
|
167
223
|
"container_name": container_name,
|
|
168
224
|
"image": image,
|
|
169
|
-
"port":
|
|
225
|
+
"port": resolved_port,
|
|
170
226
|
"profile": profile,
|
|
171
|
-
"status":
|
|
227
|
+
"status": status,
|
|
172
228
|
}
|
|
173
229
|
write_engine_state(state)
|
|
174
|
-
|
|
230
|
+
if status == "ready":
|
|
231
|
+
return True, "Engine ready.", state
|
|
232
|
+
return False, "Engine container is running but local API health checks failed.", state
|
|
175
233
|
|
|
176
234
|
|
|
177
235
|
def stop_engine(*, runtime: str, container_name: str = DEFAULT_ENGINE_CONTAINER) -> tuple[bool, str]:
|
|
@@ -192,6 +250,7 @@ def stop_engine(*, runtime: str, container_name: str = DEFAULT_ENGINE_CONTAINER)
|
|
|
192
250
|
def engine_status(*, runtime: str | None = None, container_name: str = DEFAULT_ENGINE_CONTAINER) -> dict:
|
|
193
251
|
state = read_engine_state()
|
|
194
252
|
resolved_runtime = runtime or str(state.get("runtime") or "podman")
|
|
253
|
+
resolved_port = int(state.get("port", DEFAULT_ENGINE_PORT))
|
|
195
254
|
exists = False
|
|
196
255
|
running = False
|
|
197
256
|
healthy = False
|
|
@@ -199,7 +258,7 @@ def engine_status(*, runtime: str | None = None, container_name: str = DEFAULT_E
|
|
|
199
258
|
try:
|
|
200
259
|
exists = _exists(resolved_runtime, container_name)
|
|
201
260
|
running = _running(resolved_runtime, container_name) if exists else False
|
|
202
|
-
healthy = _health(resolved_runtime, container_name) if running else False
|
|
261
|
+
healthy = _health(resolved_runtime, container_name, host_port=resolved_port) if running else False
|
|
203
262
|
if running and healthy:
|
|
204
263
|
status = "ready"
|
|
205
264
|
elif running and not healthy:
|
|
@@ -212,7 +271,7 @@ def engine_status(*, runtime: str | None = None, container_name: str = DEFAULT_E
|
|
|
212
271
|
"runtime": resolved_runtime,
|
|
213
272
|
"container_name": container_name,
|
|
214
273
|
"image": state.get("image", DEFAULT_ENGINE_IMAGE),
|
|
215
|
-
"port":
|
|
274
|
+
"port": resolved_port,
|
|
216
275
|
"profile": state.get("profile", DEFAULT_ENGINE_PROFILE),
|
|
217
276
|
"status": status,
|
|
218
277
|
"exists": exists,
|
|
@@ -17,6 +17,7 @@ from pathlib import Path
|
|
|
17
17
|
|
|
18
18
|
import httpx
|
|
19
19
|
|
|
20
|
+
from refactorai_cli import __version__
|
|
20
21
|
from refactorai_cli.local_paths import ensure_dir, refactor_home
|
|
21
22
|
|
|
22
23
|
from refactorai_cli.control_plane import ensure_lease
|
|
@@ -129,7 +130,7 @@ def resolve_runtime_manifest(*, channel: str = "stable", timeout: float = 20.0)
|
|
|
129
130
|
os_name = str(platform.system() or "linux").lower()
|
|
130
131
|
arch = str(platform.machine() or "x86_64").lower()
|
|
131
132
|
payload = {
|
|
132
|
-
"cli_version":
|
|
133
|
+
"cli_version": __version__,
|
|
133
134
|
"os": os_name,
|
|
134
135
|
"arch": arch,
|
|
135
136
|
"channel": channel,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refactorai-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.11
|
|
4
4
|
Summary: Local-first CLI for the refactor platform
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -53,6 +53,6 @@ python -m twine upload ./refactorai-cli/dist/*
|
|
|
53
53
|
## Install test (local)
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.
|
|
56
|
+
python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.11-py3-none-any.whl
|
|
57
57
|
refactor --version
|
|
58
58
|
```
|
|
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.2.9 → refactorai_cli-0.2.11}/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
|