feat(pipe): show Copilot CLI install progress during first run

This commit is contained in:
fujie
2026-02-15 00:18:27 +08:00
parent 6bf3656d30
commit 1bbddb2222

View File

@@ -1,7 +1,7 @@
"""
title: GitHub Copilot Official SDK Pipe
author: Fu-Jie
author_url: https://github.com/Fu-Jie/openwebui-extensions
author_url: https://github.com/Fu-Jie/awesome-openwebui
funding_url: https://github.com/open-webui
openwebui_id: ce96f7b4-12fc-4ac3-9a01-875713e69359
description: Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, and frontend debug logging.
@@ -2134,7 +2134,7 @@ class Pipe:
# 1. Environment Setup (Only if needed or not done)
if needs_setup:
self._setup_env(token=token)
self._setup_env(token=token, skip_cli_install=True)
self.__class__._last_update_check = now
else:
# Still inject token for BYOK real-time updates
@@ -2194,7 +2194,7 @@ class Pipe:
logger.info("[Pipes] Refreshing model cache...")
try:
# Use effective token for fetching
self._setup_env(token=token)
self._setup_env(token=token, skip_cli_install=True)
# Fetch BYOK models if configured
byok = []
@@ -2208,9 +2208,11 @@ class Pipe:
byok = await self._fetch_byok_models(uv=uv)
standard = []
if token:
cli_path = os.environ.get("COPILOT_CLI_PATH", "")
cli_ready = bool(cli_path and os.path.exists(cli_path))
if token and cli_ready:
client_config = {
"cli_path": os.environ.get("COPILOT_CLI_PATH"),
"cli_path": cli_path,
"cwd": self._get_workspace_dir(
user_id=user_id, chat_id="listing"
),
@@ -2266,11 +2268,18 @@ class Pipe:
logger.error(f"[Pipes] Error listing models: {e}")
finally:
await c.stop()
elif token and self.valves.DEBUG:
logger.info(
"[Pipes] Copilot CLI not ready during listing. Skip standard model probe to avoid blocking startup."
)
self._model_cache = standard + byok
if not self._model_cache:
return [
{"id": "error", "name": "No models found. Check Token/Network."}
{
"id": "warming_up",
"name": "Copilot CLI is preparing in background. Please retry in a moment.",
}
]
except Exception as e:
return [{"id": "error", "name": f"Error: {e}"}]
@@ -2318,8 +2327,26 @@ class Pipe:
token: str = None,
enable_mcp: bool = True,
enable_cache: bool = True,
skip_cli_install: bool = False,
__event_emitter__=None,
):
"""Setup environment variables and verify Copilot CLI. Dynamic Token Injection."""
def emit_status_sync(description: str, done: bool = False):
if not __event_emitter__:
return
try:
loop = asyncio.get_running_loop()
loop.create_task(
__event_emitter__(
{
"type": "status",
"data": {"description": description, "done": done},
}
)
)
except Exception:
pass
# 1. Real-time Token Injection (Always updates on each call)
effective_token = token or self.valves.GH_TOKEN
if effective_token:
@@ -2418,41 +2445,106 @@ class Pipe:
f"Version mismatch ({current_version} != {target_version})"
)
if should_install:
if should_install and not skip_cli_install:
self._emit_debug_log_sync(
f"Installing/Updating Copilot CLI: {install_reason}...",
__event_call__,
debug_enabled=debug_enabled,
)
emit_status_sync(
"🔧 正在安装/更新 Copilot CLI首次可能需要 1-3 分钟)...",
done=False,
)
try:
env = os.environ.copy()
if target_version:
env["VERSION"] = target_version
subprocess.run(
proc = subprocess.Popen(
"curl -fsSL https://gh.io/copilot-install | bash",
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
env=env,
)
progress_percent = -1
line_count = 0
while True:
raw_line = proc.stdout.readline() if proc.stdout else ""
if raw_line == "" and proc.poll() is not None:
break
line = (raw_line or "").strip()
if not line:
continue
line_count += 1
percent_match = re.search(r"(\d{1,3})%", line)
if percent_match:
try:
pct = int(percent_match.group(1))
if pct >= progress_percent + 5:
progress_percent = pct
emit_status_sync(
f"📦 Copilot CLI 安装中:{pct}%", done=False
)
except Exception:
pass
elif line_count % 20 == 0:
emit_status_sync(
f"📦 Copilot CLI 安装中:{line[:120]}", done=False
)
return_code = proc.wait()
if return_code != 0:
raise subprocess.CalledProcessError(
return_code,
"curl -fsSL https://gh.io/copilot-install | bash",
)
# Re-verify
current_version = get_cli_version(cli_path)
emit_status_sync(
f"✅ Copilot CLI 安装完成v{current_version or target_version or 'latest'}",
done=False,
)
except Exception as e:
self._emit_debug_log_sync(
f"CLI installation failed: {e}",
__event_call__,
debug_enabled=debug_enabled,
)
emit_status_sync(
f"❌ Copilot CLI 安装失败:{str(e)[:120]}",
done=True,
)
elif should_install and skip_cli_install:
self._emit_debug_log_sync(
f"Skipping CLI install during model listing: {install_reason}",
__event_call__,
debug_enabled=debug_enabled,
)
# 4. Finalize
os.environ["COPILOT_CLI_PATH"] = cli_path
self.__class__._env_setup_done = True
cli_ready = bool(cli_path and os.path.exists(cli_path))
if cli_ready:
os.environ["COPILOT_CLI_PATH"] = cli_path
self.__class__._env_setup_done = cli_ready
self.__class__._last_update_check = datetime.now().timestamp()
self._emit_debug_log_sync(
f"Environment setup complete. CLI: {cli_path} (v{current_version})",
f"Environment setup complete. CLI ready={cli_ready}. Path: {cli_path} (v{current_version})",
__event_call__,
debug_enabled=debug_enabled,
)
if not skip_cli_install:
if cli_ready:
emit_status_sync("✅ Copilot CLI 已就绪", done=True)
else:
emit_status_sync("⚠️ Copilot CLI 尚未就绪,请稍后重试。", done=True)
def _process_attachments(
self,
@@ -2737,6 +2829,7 @@ class Pipe:
token=effective_token,
enable_mcp=effective_mcp,
enable_cache=effective_cache,
__event_emitter__=__event_emitter__,
)
cwd = self._get_workspace_dir(user_id=user_id, chat_id=chat_id)