refactorai-cli 0.2.10__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.
Files changed (32) hide show
  1. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/PKG-INFO +2 -2
  2. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/README.md +1 -1
  3. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/pyproject.toml +1 -1
  4. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/__init__.py +1 -1
  5. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/control_plane.py +2 -1
  6. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/local_engine_runtime.py +69 -10
  7. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/runtime_manager.py +2 -1
  8. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/PKG-INFO +2 -2
  9. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/auth.py +0 -0
  10. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/client.py +0 -0
  11. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/__init__.py +0 -0
  12. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/auth_cmds.py +0 -0
  13. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/engine_cmds.py +0 -0
  14. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/model_cmds.py +0 -0
  15. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/rules_cmds.py +0 -0
  16. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/run_cmds.py +0 -0
  17. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/runtime_cmds.py +0 -0
  18. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
  19. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/commands/setup_cmds.py +0 -0
  20. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/credentials.py +0 -0
  21. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/local_constitution.py +0 -0
  22. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/local_paths.py +0 -0
  23. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/main.py +0 -0
  24. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/model_policy.py +0 -0
  25. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/settings.py +0 -0
  26. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli/setup_flow.py +0 -0
  27. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/SOURCES.txt +0 -0
  28. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/dependency_links.txt +0 -0
  29. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/entry_points.txt +0 -0
  30. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/requires.txt +0 -0
  31. {refactorai_cli-0.2.10 → refactorai_cli-0.2.11}/refactorai_cli.egg-info/top_level.txt +0 -0
  32. {refactorai_cli-0.2.10 → 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.10
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.1.0-py3-none-any.whl
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.1.0-py3-none-any.whl
45
+ python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.11-py3-none-any.whl
46
46
  refactor --version
47
47
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "refactorai-cli"
3
- version = "0.2.10"
3
+ version = "0.2.11"
4
4
  description = "Local-first CLI for the refactor platform"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -5,4 +5,4 @@ the shared `refactor_core` pipeline from a project folder while staying
5
5
  authenticated to the hosted platform via a developer key.
6
6
  """
7
7
 
8
- __version__ = "0.2.10"
8
+ __version__ = "0.2.11"
@@ -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 = "0.1.0") -> LeaseContext:
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 _health(runtime: str, name: str) -> bool:
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
- return proc.returncode == 0
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
- if rebuild and _exists(runtime, container_name):
186
+ exists = _exists(runtime, container_name)
187
+ if rebuild and exists:
137
188
  _run([runtime, "rm", "-f", container_name])
138
- if not _exists(runtime, container_name):
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:{int(port)}:{int(port)}",
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": int(port),
225
+ "port": resolved_port,
170
226
  "profile": profile,
171
- "status": "ready" if _health(runtime, container_name) else "degraded",
227
+ "status": status,
172
228
  }
173
229
  write_engine_state(state)
174
- return True, "Engine ready.", state
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": int(state.get("port", DEFAULT_ENGINE_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": "0.1.0",
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.10
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.1.0-py3-none-any.whl
56
+ python -m pip install ./refactorai-cli/dist/refactorai_cli-0.2.11-py3-none-any.whl
57
57
  refactor --version
58
58
  ```