baserun-cli 0.1.0__tar.gz → 0.1.1__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.
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/PKG-INFO +1 -1
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/_vendored/cli.py +28 -12
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/channel.py +16 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/runner.py +7 -6
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli.egg-info/PKG-INFO +1 -1
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/pyproject.toml +1 -1
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/README.md +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/__init__.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/_vendored/__init__.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/_vendored/base.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/_vendored/parsers/__init__.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/_vendored/parsers/base.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/_vendored/parsers/bash_agent.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/_vendored/parsers/claude.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/_vendored/parsers/codex.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli/main.py +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli.egg-info/SOURCES.txt +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli.egg-info/dependency_links.txt +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli.egg-info/entry_points.txt +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli.egg-info/requires.txt +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/baserun_cli.egg-info/top_level.txt +0 -0
- {baserun_cli-0.1.0 → baserun_cli-0.1.1}/setup.cfg +0 -0
|
@@ -101,20 +101,36 @@ class CLIAgentConnector(AgentConnector):
|
|
|
101
101
|
) -> AsyncIterator[ConnectorEvent]:
|
|
102
102
|
import logging as _log
|
|
103
103
|
_dbg = _log.getLogger("baserun_cli._vendored.cli")
|
|
104
|
-
cli = shutil.which(self.spec.bin)
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
cli = shutil.which(self.spec.bin)
|
|
105
|
+
if not cli:
|
|
106
|
+
message = f"CLI not found: {self.spec.bin}. Please install it or configure cli_spec.bin with the correct executable path."
|
|
107
|
+
_dbg.error(message)
|
|
108
|
+
yield ConnectorEvent(ConnectorEventType.ERROR, {"message": message})
|
|
109
|
+
return
|
|
110
|
+
|
|
107
111
|
args = self._build_args(prompt, session_id, fork)
|
|
108
112
|
_dbg.info("spawning: %s %s (cwd=%s)", cli, " ".join(args[:6]) + ("..." if len(args) > 6 else ""), self.spec.workdir)
|
|
109
113
|
|
|
110
|
-
proc =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
proc = None
|
|
115
|
+
try:
|
|
116
|
+
proc = await asyncio.create_subprocess_exec(
|
|
117
|
+
cli,
|
|
118
|
+
*args,
|
|
119
|
+
stdout=asyncio.subprocess.PIPE,
|
|
120
|
+
stderr=asyncio.subprocess.PIPE,
|
|
121
|
+
env=self._env(),
|
|
122
|
+
cwd=self.spec.workdir,
|
|
123
|
+
)
|
|
124
|
+
except FileNotFoundError:
|
|
125
|
+
message = f"CLI not found: {self.spec.bin}. Please install it or configure cli_spec.bin with the correct executable path."
|
|
126
|
+
_dbg.exception(message)
|
|
127
|
+
yield ConnectorEvent(ConnectorEventType.ERROR, {"message": message})
|
|
128
|
+
return
|
|
129
|
+
except Exception as e:
|
|
130
|
+
message = f"failed to start CLI {self.spec.bin}: {e}"
|
|
131
|
+
_dbg.exception(message)
|
|
132
|
+
yield ConnectorEvent(ConnectorEventType.ERROR, {"message": message})
|
|
133
|
+
return
|
|
118
134
|
_dbg.info("pid=%s started", proc.pid)
|
|
119
135
|
|
|
120
136
|
resolved_session_id: str | None = session_id
|
|
@@ -196,7 +212,7 @@ class CLIAgentConnector(AgentConnector):
|
|
|
196
212
|
_dbg.exception("pid=%s _run exception", proc.pid)
|
|
197
213
|
yield ConnectorEvent(ConnectorEventType.ERROR, {"message": str(e)})
|
|
198
214
|
finally:
|
|
199
|
-
if proc.returncode is None:
|
|
215
|
+
if proc is not None and proc.returncode is None:
|
|
200
216
|
_dbg.warning("pid=%s still running, killing", proc.pid)
|
|
201
217
|
proc.kill()
|
|
202
218
|
await proc.wait()
|
|
@@ -133,6 +133,22 @@ class ChannelClient:
|
|
|
133
133
|
run_id, payload.get("data", {}).get("seq"), max_retries)
|
|
134
134
|
return False
|
|
135
135
|
|
|
136
|
+
async def publish_event_reliably(self, run_id: str, payload: dict[str, Any]) -> bool:
|
|
137
|
+
"""Publish a critical run event, falling back to HTTP if WS fails."""
|
|
138
|
+
if await self.publish_event(run_id, payload):
|
|
139
|
+
return True
|
|
140
|
+
try:
|
|
141
|
+
await self._http_publish(run_id, payload)
|
|
142
|
+
return True
|
|
143
|
+
except Exception as e:
|
|
144
|
+
log.error(
|
|
145
|
+
"http publish fallback failed for run %s seq=%s: %s",
|
|
146
|
+
run_id,
|
|
147
|
+
payload.get("data", {}).get("seq"),
|
|
148
|
+
e,
|
|
149
|
+
)
|
|
150
|
+
return False
|
|
151
|
+
|
|
136
152
|
async def _open_run_pub(self, run_id: str) -> Any:
|
|
137
153
|
"""Open a WS publisher connection for run:{run_id}."""
|
|
138
154
|
base = self.nchan_url
|
|
@@ -204,7 +204,10 @@ class TaskRunner:
|
|
|
204
204
|
log.debug("run %s event seq=%d kind=%s", run_id, seq, kind)
|
|
205
205
|
|
|
206
206
|
# publish
|
|
207
|
-
|
|
207
|
+
if is_terminal:
|
|
208
|
+
await self.channel.publish_event_reliably(run_id, payload)
|
|
209
|
+
else:
|
|
210
|
+
await self.channel.publish_event(run_id, payload)
|
|
208
211
|
|
|
209
212
|
if is_terminal:
|
|
210
213
|
saw_terminal = True
|
|
@@ -225,9 +228,7 @@ class TaskRunner:
|
|
|
225
228
|
}
|
|
226
229
|
log_file.write(json.dumps(payload["data"], ensure_ascii=False) + "\n")
|
|
227
230
|
log_file.flush()
|
|
228
|
-
|
|
229
|
-
if not ok:
|
|
230
|
-
failed_events.append(payload)
|
|
231
|
+
await self.channel.publish_event_reliably(run_id, payload)
|
|
231
232
|
log.info("run %s synthesized terminal (seq=%d)", run_id, seq)
|
|
232
233
|
|
|
233
234
|
# verify-and-replay:对比 nchan 实际状态,精确补发缺失事件
|
|
@@ -249,7 +250,7 @@ class TaskRunner:
|
|
|
249
250
|
|
|
250
251
|
except Exception as e:
|
|
251
252
|
log.exception("run %s failed at seq=%d", run_id, seq)
|
|
252
|
-
# publish error terminal
|
|
253
|
+
# publish error terminal reliably
|
|
253
254
|
seq += 1
|
|
254
255
|
error_payload = {
|
|
255
256
|
"type": "result",
|
|
@@ -266,7 +267,7 @@ class TaskRunner:
|
|
|
266
267
|
log_file.write(json.dumps(error_payload["data"], ensure_ascii=False) + "\n")
|
|
267
268
|
except Exception:
|
|
268
269
|
pass
|
|
269
|
-
await self.channel.
|
|
270
|
+
await self.channel.publish_event_reliably(run_id, error_payload)
|
|
270
271
|
return True, "error", seq
|
|
271
272
|
|
|
272
273
|
finally:
|
|
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
|