@hunyed15/codecgc 0.2.10 → 0.2.11
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.
|
@@ -198,6 +198,41 @@ def _terminate_process_tree(process: subprocess.Popen[str]) -> None:
|
|
|
198
198
|
process.kill()
|
|
199
199
|
|
|
200
200
|
|
|
201
|
+
CODEX_JS_RELATIVE = Path("node_modules") / "@openai" / "codex" / "bin" / "codex.js"
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def resolve_cli_command(cli_name: str) -> list[str]:
|
|
205
|
+
"""Resolve a CLI command to a cross-platform executable command list.
|
|
206
|
+
|
|
207
|
+
On Windows, prefers direct node invocation (node cli.js) to avoid .cmd shim
|
|
208
|
+
issues. Falls back to cmd.exe wrapper if .js file is not found.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
cli_name: The CLI command name (e.g., "codex")
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Command list ready for subprocess.Popen, e.g.:
|
|
215
|
+
- Linux/Mac: ["/usr/local/bin/codex"]
|
|
216
|
+
- Windows (node): ["node", "C:\\...\\codex.js"]
|
|
217
|
+
- Windows (.cmd): ["cmd.exe", "/s", "/c", "C:\\...\\codex.CMD"]
|
|
218
|
+
"""
|
|
219
|
+
resolved_path = shutil.which(cli_name) or cli_name
|
|
220
|
+
|
|
221
|
+
if os.name == "nt":
|
|
222
|
+
# Prefer direct node invocation on Windows (avoids .cmd shim complexity)
|
|
223
|
+
shim_dir = Path(resolved_path).parent
|
|
224
|
+
cli_js = shim_dir / CODEX_JS_RELATIVE
|
|
225
|
+
if cli_js.is_file():
|
|
226
|
+
node_path = shutil.which("node") or "node"
|
|
227
|
+
return [node_path, str(cli_js)]
|
|
228
|
+
|
|
229
|
+
# Fallback: wrap .cmd/.bat with cmd.exe
|
|
230
|
+
if resolved_path.lower().endswith((".cmd", ".bat")):
|
|
231
|
+
return ["cmd.exe", "/s", "/c", resolved_path]
|
|
232
|
+
|
|
233
|
+
return [resolved_path]
|
|
234
|
+
|
|
235
|
+
|
|
201
236
|
def run_shell_command(cmd: list[str], timeout_seconds: int = DEFAULT_CODEX_TIMEOUT_SECONDS) -> Generator[str, None, None]:
|
|
202
237
|
"""Execute a command and stream its output line-by-line.
|
|
203
238
|
|
|
@@ -207,14 +242,12 @@ def run_shell_command(cmd: list[str], timeout_seconds: int = DEFAULT_CODEX_TIMEO
|
|
|
207
242
|
Yields:
|
|
208
243
|
Output lines from the command
|
|
209
244
|
"""
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
popen_cmd[0] = codex_path
|
|
245
|
+
# Resolve cmd[0] (e.g., "codex") to a platform-appropriate executable command,
|
|
246
|
+
# wrapping .cmd/.bat shims with cmd.exe on Windows. The remaining args are
|
|
247
|
+
# passed through unchanged.
|
|
248
|
+
popen_cmd = resolve_cli_command(cmd[0]) + cmd[1:]
|
|
215
249
|
|
|
216
250
|
# Ensure Codex CLI inherits the same Python environment as this MCP server
|
|
217
|
-
# This is critical for Python 3.11+ syntax compatibility (e.g., except*)
|
|
218
251
|
env = os.environ.copy()
|
|
219
252
|
python_executable = sys.executable
|
|
220
253
|
if python_executable:
|
|
@@ -160,23 +160,32 @@ GEMINI_JS_RELATIVE = Path("node_modules") / "@google" / "gemini-cli" / "bundle"
|
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
def _resolve_gemini_command(cmd: list[str]) -> list[str]:
|
|
163
|
-
"""Resolve the gemini CLI command
|
|
163
|
+
"""Resolve the gemini CLI command to a cross-platform executable command list.
|
|
164
|
+
|
|
165
|
+
On Windows, prefers direct node invocation (node gemini.js) to avoid .cmd shim
|
|
166
|
+
issues. Falls back to cmd.exe wrapper if gemini.js is not found.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
cmd: Command list starting with "gemini", e.g., ["gemini", "-o", "stream-json", ...]
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Platform-appropriate command list ready for subprocess.Popen
|
|
173
|
+
"""
|
|
164
174
|
gemini_shim = shutil.which("gemini") or cmd[0]
|
|
165
175
|
|
|
166
176
|
if os.name == "nt":
|
|
177
|
+
# Prefer direct node invocation on Windows (avoids .cmd shim complexity)
|
|
167
178
|
shim_dir = Path(gemini_shim).parent
|
|
168
179
|
gemini_js = shim_dir / GEMINI_JS_RELATIVE
|
|
169
180
|
if gemini_js.is_file():
|
|
170
181
|
node_path = shutil.which("node") or "node"
|
|
171
182
|
return [node_path, str(gemini_js)] + cmd[1:]
|
|
172
183
|
|
|
184
|
+
# Fallback: wrap .cmd/.bat with cmd.exe
|
|
173
185
|
if gemini_shim.lower().endswith((".cmd", ".bat")):
|
|
174
|
-
|
|
175
|
-
cmd[0] = gemini_shim
|
|
176
|
-
return ["cmd.exe", "/s", "/c", list2cmdline(cmd)]
|
|
186
|
+
return ["cmd.exe", "/s", "/c", gemini_shim] + cmd[1:]
|
|
177
187
|
|
|
178
|
-
|
|
179
|
-
return cmd
|
|
188
|
+
return [gemini_shim] + cmd[1:]
|
|
180
189
|
|
|
181
190
|
|
|
182
191
|
def run_shell_command(
|
package/package.json
CHANGED
|
@@ -294,9 +294,22 @@ async def execute_payload(payload: dict[str, Any], timeout_seconds: int) -> dict
|
|
|
294
294
|
payload["tool_args"],
|
|
295
295
|
read_timeout_seconds=datetime.timedelta(seconds=timeout_seconds),
|
|
296
296
|
)
|
|
297
|
-
except
|
|
298
|
-
#
|
|
299
|
-
|
|
297
|
+
except ExceptionGroup as eg:
|
|
298
|
+
# 递归展开 TaskGroup 中所有叶子异常
|
|
299
|
+
def _flatten(exc: BaseException) -> list[str]:
|
|
300
|
+
if isinstance(exc, ExceptionGroup):
|
|
301
|
+
result = []
|
|
302
|
+
for sub in exc.exceptions:
|
|
303
|
+
result.extend(_flatten(sub))
|
|
304
|
+
return result
|
|
305
|
+
return [f"{type(exc).__name__}: {exc}"]
|
|
306
|
+
sub_errors = _flatten(eg)
|
|
307
|
+
if raw_result is None:
|
|
308
|
+
raise RuntimeError(f"MCP call failed with TaskGroup errors: {'; '.join(sub_errors)}") from eg
|
|
309
|
+
except Exception as _exc:
|
|
310
|
+
# 只有在已获得结果的情况下才忽略清理阶段异常;否则保留真实错误
|
|
311
|
+
if raw_result is None:
|
|
312
|
+
raise RuntimeError(f"MCP call failed: {type(_exc).__name__}: {_exc}") from _exc
|
|
300
313
|
|
|
301
314
|
if raw_result is None:
|
|
302
315
|
raise RuntimeError("MCP call failed before producing a result.")
|