gobby 0.2.8__py3-none-any.whl → 0.2.11__py3-none-any.whl
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.
- gobby/__init__.py +1 -1
- gobby/adapters/__init__.py +6 -0
- gobby/adapters/base.py +11 -2
- gobby/adapters/claude_code.py +5 -28
- gobby/adapters/codex_impl/adapter.py +38 -43
- gobby/adapters/copilot.py +324 -0
- gobby/adapters/cursor.py +373 -0
- gobby/adapters/gemini.py +2 -26
- gobby/adapters/windsurf.py +359 -0
- gobby/agents/definitions.py +162 -2
- gobby/agents/isolation.py +33 -1
- gobby/agents/pty_reader.py +192 -0
- gobby/agents/registry.py +10 -1
- gobby/agents/runner.py +24 -8
- gobby/agents/sandbox.py +8 -3
- gobby/agents/session.py +4 -0
- gobby/agents/spawn.py +9 -2
- gobby/agents/spawn_executor.py +49 -61
- gobby/agents/spawners/command_builder.py +4 -4
- gobby/app_context.py +64 -0
- gobby/cli/__init__.py +4 -0
- gobby/cli/install.py +259 -4
- gobby/cli/installers/__init__.py +12 -0
- gobby/cli/installers/copilot.py +242 -0
- gobby/cli/installers/cursor.py +244 -0
- gobby/cli/installers/shared.py +3 -0
- gobby/cli/installers/windsurf.py +242 -0
- gobby/cli/pipelines.py +639 -0
- gobby/cli/sessions.py +3 -1
- gobby/cli/skills.py +209 -0
- gobby/cli/tasks/crud.py +6 -5
- gobby/cli/tasks/search.py +1 -1
- gobby/cli/ui.py +116 -0
- gobby/cli/utils.py +5 -17
- gobby/cli/workflows.py +38 -17
- gobby/config/app.py +5 -0
- gobby/config/features.py +0 -20
- gobby/config/skills.py +23 -2
- gobby/config/tasks.py +4 -0
- gobby/hooks/broadcaster.py +9 -0
- gobby/hooks/event_handlers/__init__.py +155 -0
- gobby/hooks/event_handlers/_agent.py +175 -0
- gobby/hooks/event_handlers/_base.py +92 -0
- gobby/hooks/event_handlers/_misc.py +66 -0
- gobby/hooks/event_handlers/_session.py +487 -0
- gobby/hooks/event_handlers/_tool.py +196 -0
- gobby/hooks/events.py +48 -0
- gobby/hooks/hook_manager.py +27 -3
- gobby/install/copilot/hooks/hook_dispatcher.py +203 -0
- gobby/install/cursor/hooks/hook_dispatcher.py +203 -0
- gobby/install/gemini/hooks/hook_dispatcher.py +8 -0
- gobby/install/windsurf/hooks/hook_dispatcher.py +205 -0
- gobby/llm/__init__.py +14 -1
- gobby/llm/claude.py +594 -43
- gobby/llm/service.py +149 -0
- gobby/mcp_proxy/importer.py +4 -41
- gobby/mcp_proxy/instructions.py +9 -27
- gobby/mcp_proxy/manager.py +13 -3
- gobby/mcp_proxy/models.py +1 -0
- gobby/mcp_proxy/registries.py +66 -5
- gobby/mcp_proxy/server.py +6 -2
- gobby/mcp_proxy/services/recommendation.py +2 -28
- gobby/mcp_proxy/services/tool_filter.py +7 -0
- gobby/mcp_proxy/services/tool_proxy.py +19 -1
- gobby/mcp_proxy/stdio.py +37 -21
- gobby/mcp_proxy/tools/agents.py +7 -0
- gobby/mcp_proxy/tools/artifacts.py +3 -3
- gobby/mcp_proxy/tools/hub.py +30 -1
- gobby/mcp_proxy/tools/orchestration/cleanup.py +5 -5
- gobby/mcp_proxy/tools/orchestration/monitor.py +1 -1
- gobby/mcp_proxy/tools/orchestration/orchestrate.py +8 -3
- gobby/mcp_proxy/tools/orchestration/review.py +17 -4
- gobby/mcp_proxy/tools/orchestration/wait.py +7 -7
- gobby/mcp_proxy/tools/pipelines/__init__.py +254 -0
- gobby/mcp_proxy/tools/pipelines/_discovery.py +67 -0
- gobby/mcp_proxy/tools/pipelines/_execution.py +281 -0
- gobby/mcp_proxy/tools/sessions/_crud.py +4 -4
- gobby/mcp_proxy/tools/sessions/_handoff.py +1 -1
- gobby/mcp_proxy/tools/skills/__init__.py +184 -30
- gobby/mcp_proxy/tools/spawn_agent.py +229 -14
- gobby/mcp_proxy/tools/task_readiness.py +27 -4
- gobby/mcp_proxy/tools/tasks/_context.py +8 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +27 -1
- gobby/mcp_proxy/tools/tasks/_helpers.py +1 -1
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +125 -8
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +2 -1
- gobby/mcp_proxy/tools/tasks/_search.py +1 -1
- gobby/mcp_proxy/tools/workflows/__init__.py +273 -0
- gobby/mcp_proxy/tools/workflows/_artifacts.py +225 -0
- gobby/mcp_proxy/tools/workflows/_import.py +112 -0
- gobby/mcp_proxy/tools/workflows/_lifecycle.py +332 -0
- gobby/mcp_proxy/tools/workflows/_query.py +226 -0
- gobby/mcp_proxy/tools/workflows/_resolution.py +78 -0
- gobby/mcp_proxy/tools/workflows/_terminal.py +175 -0
- gobby/mcp_proxy/tools/worktrees.py +54 -15
- gobby/memory/components/__init__.py +0 -0
- gobby/memory/components/ingestion.py +98 -0
- gobby/memory/components/search.py +108 -0
- gobby/memory/context.py +5 -5
- gobby/memory/manager.py +16 -25
- gobby/paths.py +51 -0
- gobby/prompts/loader.py +1 -35
- gobby/runner.py +131 -16
- gobby/servers/http.py +193 -150
- gobby/servers/routes/__init__.py +2 -0
- gobby/servers/routes/admin.py +56 -0
- gobby/servers/routes/mcp/endpoints/execution.py +33 -32
- gobby/servers/routes/mcp/endpoints/registry.py +8 -8
- gobby/servers/routes/mcp/hooks.py +10 -1
- gobby/servers/routes/pipelines.py +227 -0
- gobby/servers/websocket.py +314 -1
- gobby/sessions/analyzer.py +89 -3
- gobby/sessions/manager.py +5 -5
- gobby/sessions/transcripts/__init__.py +3 -0
- gobby/sessions/transcripts/claude.py +5 -0
- gobby/sessions/transcripts/codex.py +5 -0
- gobby/sessions/transcripts/gemini.py +5 -0
- gobby/skills/hubs/__init__.py +25 -0
- gobby/skills/hubs/base.py +234 -0
- gobby/skills/hubs/claude_plugins.py +328 -0
- gobby/skills/hubs/clawdhub.py +289 -0
- gobby/skills/hubs/github_collection.py +465 -0
- gobby/skills/hubs/manager.py +263 -0
- gobby/skills/hubs/skillhub.py +342 -0
- gobby/skills/parser.py +23 -0
- gobby/skills/sync.py +5 -4
- gobby/storage/artifacts.py +19 -0
- gobby/storage/memories.py +4 -4
- gobby/storage/migrations.py +118 -3
- gobby/storage/pipelines.py +367 -0
- gobby/storage/sessions.py +23 -4
- gobby/storage/skills.py +48 -8
- gobby/storage/tasks/_aggregates.py +2 -2
- gobby/storage/tasks/_lifecycle.py +4 -4
- gobby/storage/tasks/_models.py +7 -1
- gobby/storage/tasks/_queries.py +3 -3
- gobby/sync/memories.py +4 -3
- gobby/tasks/commits.py +48 -17
- gobby/tasks/external_validator.py +4 -17
- gobby/tasks/validation.py +13 -87
- gobby/tools/summarizer.py +18 -51
- gobby/utils/status.py +13 -0
- gobby/workflows/actions.py +80 -0
- gobby/workflows/context_actions.py +265 -27
- gobby/workflows/definitions.py +119 -1
- gobby/workflows/detection_helpers.py +23 -11
- gobby/workflows/enforcement/__init__.py +11 -1
- gobby/workflows/enforcement/blocking.py +96 -0
- gobby/workflows/enforcement/handlers.py +35 -1
- gobby/workflows/enforcement/task_policy.py +18 -0
- gobby/workflows/engine.py +26 -4
- gobby/workflows/evaluator.py +8 -5
- gobby/workflows/lifecycle_evaluator.py +59 -27
- gobby/workflows/loader.py +567 -30
- gobby/workflows/lobster_compat.py +147 -0
- gobby/workflows/pipeline_executor.py +801 -0
- gobby/workflows/pipeline_state.py +172 -0
- gobby/workflows/pipeline_webhooks.py +206 -0
- gobby/workflows/premature_stop.py +5 -0
- gobby/worktrees/git.py +135 -20
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/METADATA +56 -22
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/RECORD +166 -122
- gobby/hooks/event_handlers.py +0 -1008
- gobby/mcp_proxy/tools/workflows.py +0 -1023
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/WHEEL +0 -0
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/top_level.txt +0 -0
gobby/runner.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import asyncio
|
|
2
4
|
import logging
|
|
3
5
|
import os
|
|
4
6
|
import signal
|
|
5
7
|
import sys
|
|
6
8
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
8
10
|
|
|
9
11
|
import uvicorn
|
|
10
12
|
|
|
11
13
|
from gobby.agents.runner import AgentRunner
|
|
14
|
+
from gobby.app_context import ServiceContainer
|
|
12
15
|
from gobby.config.app import load_config
|
|
13
16
|
from gobby.llm import LLMService, create_llm_service
|
|
14
17
|
from gobby.llm.resolver import ExecutorRegistry
|
|
@@ -36,6 +39,12 @@ from gobby.worktrees.git import WorktreeGitManager
|
|
|
36
39
|
|
|
37
40
|
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
38
41
|
|
|
42
|
+
# Type hints for pipeline components (imported lazily at runtime)
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from gobby.storage.pipelines import LocalPipelineExecutionManager
|
|
45
|
+
from gobby.workflows.loader import WorkflowLoader
|
|
46
|
+
from gobby.workflows.pipeline_executor import PipelineExecutor
|
|
47
|
+
|
|
39
48
|
logger = logging.getLogger(__name__)
|
|
40
49
|
|
|
41
50
|
|
|
@@ -181,6 +190,36 @@ class GobbyRunner:
|
|
|
181
190
|
except Exception as e:
|
|
182
191
|
logger.debug(f"Could not initialize git manager: {e}")
|
|
183
192
|
|
|
193
|
+
# Initialize Pipeline Components
|
|
194
|
+
self.workflow_loader: WorkflowLoader | None = None
|
|
195
|
+
self.pipeline_execution_manager: LocalPipelineExecutionManager | None = None
|
|
196
|
+
self.pipeline_executor: PipelineExecutor | None = None
|
|
197
|
+
try:
|
|
198
|
+
from gobby.storage.pipelines import LocalPipelineExecutionManager
|
|
199
|
+
from gobby.workflows.loader import WorkflowLoader
|
|
200
|
+
from gobby.workflows.pipeline_executor import PipelineExecutor
|
|
201
|
+
|
|
202
|
+
self.workflow_loader = WorkflowLoader()
|
|
203
|
+
if self.project_id:
|
|
204
|
+
self.pipeline_execution_manager = LocalPipelineExecutionManager(
|
|
205
|
+
db=self.database,
|
|
206
|
+
project_id=self.project_id,
|
|
207
|
+
)
|
|
208
|
+
if self.llm_service:
|
|
209
|
+
self.pipeline_executor = PipelineExecutor(
|
|
210
|
+
db=self.database,
|
|
211
|
+
execution_manager=self.pipeline_execution_manager,
|
|
212
|
+
llm_service=self.llm_service,
|
|
213
|
+
loader=self.workflow_loader,
|
|
214
|
+
)
|
|
215
|
+
logger.debug("Pipeline executor initialized")
|
|
216
|
+
else:
|
|
217
|
+
logger.debug("Pipeline executor not initialized: LLM service not available")
|
|
218
|
+
else:
|
|
219
|
+
logger.debug("Pipeline execution manager not initialized: no project context")
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.warning(f"Failed to initialize pipeline components: {e}")
|
|
222
|
+
|
|
184
223
|
# Initialize Agent Runner (Phase 7 - Subagents)
|
|
185
224
|
# Create executor registry for lazy executor creation
|
|
186
225
|
self.executor_registry = ExecutorRegistry(config=self.config)
|
|
@@ -212,29 +251,41 @@ class GobbyRunner:
|
|
|
212
251
|
)
|
|
213
252
|
|
|
214
253
|
# HTTP Server
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
test_mode=self.config.test_mode,
|
|
218
|
-
mcp_manager=self.mcp_proxy,
|
|
219
|
-
mcp_db_manager=self.mcp_db_manager,
|
|
254
|
+
# Bundle services into container
|
|
255
|
+
services = ServiceContainer(
|
|
220
256
|
config=self.config,
|
|
257
|
+
database=self.database,
|
|
221
258
|
session_manager=self.session_manager,
|
|
222
259
|
task_manager=self.task_manager,
|
|
223
260
|
task_sync_manager=self.task_sync_manager,
|
|
224
|
-
|
|
261
|
+
memory_sync_manager=self.memory_sync_manager,
|
|
225
262
|
memory_manager=self.memory_manager,
|
|
226
263
|
llm_service=self.llm_service,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
task_validator=self.task_validator,
|
|
264
|
+
mcp_manager=self.mcp_proxy,
|
|
265
|
+
mcp_db_manager=self.mcp_db_manager,
|
|
230
266
|
metrics_manager=self.metrics_manager,
|
|
231
267
|
agent_runner=self.agent_runner,
|
|
268
|
+
message_processor=self.message_processor,
|
|
269
|
+
message_manager=self.message_manager,
|
|
270
|
+
task_validator=self.task_validator,
|
|
232
271
|
worktree_storage=self.worktree_storage,
|
|
233
272
|
clone_storage=self.clone_storage,
|
|
234
273
|
git_manager=self.git_manager,
|
|
235
274
|
project_id=self.project_id,
|
|
275
|
+
pipeline_executor=self.pipeline_executor,
|
|
276
|
+
workflow_loader=self.workflow_loader,
|
|
277
|
+
pipeline_execution_manager=self.pipeline_execution_manager,
|
|
236
278
|
)
|
|
237
279
|
|
|
280
|
+
self.http_server = HTTPServer(
|
|
281
|
+
services=services,
|
|
282
|
+
port=self.config.daemon_port,
|
|
283
|
+
test_mode=self.config.test_mode,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
# Inject server into container for circular ref if needed later
|
|
287
|
+
# self.http_server.services = services
|
|
288
|
+
|
|
238
289
|
# Ensure message_processor property is set (redundant but explicit):
|
|
239
290
|
self.http_server.message_processor = self.message_processor
|
|
240
291
|
|
|
@@ -242,7 +293,7 @@ class GobbyRunner:
|
|
|
242
293
|
self.websocket_server: WebSocketServer | None = None
|
|
243
294
|
if self.config.websocket and getattr(self.config.websocket, "enabled", True):
|
|
244
295
|
websocket_config = WebSocketConfig(
|
|
245
|
-
host=
|
|
296
|
+
host=self.config.bind_host,
|
|
246
297
|
port=self.config.websocket.port,
|
|
247
298
|
ping_interval=self.config.websocket.ping_interval,
|
|
248
299
|
ping_timeout=self.config.websocket.ping_timeout,
|
|
@@ -250,6 +301,7 @@ class GobbyRunner:
|
|
|
250
301
|
self.websocket_server = WebSocketServer(
|
|
251
302
|
config=websocket_config,
|
|
252
303
|
mcp_manager=self.mcp_proxy,
|
|
304
|
+
llm_service=self.llm_service,
|
|
253
305
|
)
|
|
254
306
|
# Pass WebSocket server reference to HTTP server for broadcasting
|
|
255
307
|
self.http_server.websocket_server = self.websocket_server
|
|
@@ -263,6 +315,9 @@ class GobbyRunner:
|
|
|
263
315
|
# Register agent event callback for WebSocket broadcasting
|
|
264
316
|
self._setup_agent_event_broadcasting()
|
|
265
317
|
|
|
318
|
+
# Register pipeline event callback for WebSocket broadcasting
|
|
319
|
+
self._setup_pipeline_event_broadcasting()
|
|
320
|
+
|
|
266
321
|
def _init_database(self) -> DatabaseProtocol:
|
|
267
322
|
"""Initialize hub database."""
|
|
268
323
|
hub_db_path = Path(self.config.database_path).expanduser()
|
|
@@ -277,13 +332,23 @@ class GobbyRunner:
|
|
|
277
332
|
return hub_db
|
|
278
333
|
|
|
279
334
|
def _setup_agent_event_broadcasting(self) -> None:
|
|
280
|
-
"""Set up WebSocket broadcasting for agent lifecycle events."""
|
|
335
|
+
"""Set up WebSocket broadcasting for agent lifecycle events and PTY reading."""
|
|
336
|
+
from gobby.agents.pty_reader import get_pty_reader_manager
|
|
281
337
|
from gobby.agents.registry import get_running_agent_registry
|
|
282
338
|
|
|
283
339
|
if not self.websocket_server:
|
|
284
340
|
return
|
|
285
341
|
|
|
286
342
|
registry = get_running_agent_registry()
|
|
343
|
+
pty_manager = get_pty_reader_manager()
|
|
344
|
+
|
|
345
|
+
# Set up PTY output callback to broadcast via WebSocket
|
|
346
|
+
async def broadcast_terminal_output(run_id: str, data: str) -> None:
|
|
347
|
+
"""Broadcast terminal output via WebSocket."""
|
|
348
|
+
if self.websocket_server:
|
|
349
|
+
await self.websocket_server.broadcast_terminal_output(run_id, data)
|
|
350
|
+
|
|
351
|
+
pty_manager.set_output_callback(broadcast_terminal_output)
|
|
287
352
|
|
|
288
353
|
def broadcast_agent_event(event_type: str, run_id: str, data: dict[str, Any]) -> None:
|
|
289
354
|
"""Broadcast agent events via WebSocket (non-blocking)."""
|
|
@@ -299,6 +364,32 @@ class GobbyRunner:
|
|
|
299
364
|
except Exception as e:
|
|
300
365
|
logger.warning(f"Failed to broadcast agent event {event_type}: {e}")
|
|
301
366
|
|
|
367
|
+
# Handle PTY reader start/stop for embedded agents
|
|
368
|
+
if event_type == "agent_started" and data.get("mode") == "embedded":
|
|
369
|
+
# Start PTY reader for embedded agents
|
|
370
|
+
agent = registry.get(run_id)
|
|
371
|
+
if agent and agent.master_fd is not None:
|
|
372
|
+
|
|
373
|
+
async def start_pty_reader() -> None:
|
|
374
|
+
await pty_manager.start_reader(agent)
|
|
375
|
+
|
|
376
|
+
task = asyncio.create_task(start_pty_reader())
|
|
377
|
+
task.add_done_callback(_log_broadcast_exception)
|
|
378
|
+
|
|
379
|
+
elif event_type in (
|
|
380
|
+
"agent_completed",
|
|
381
|
+
"agent_failed",
|
|
382
|
+
"agent_cancelled",
|
|
383
|
+
"agent_timeout",
|
|
384
|
+
):
|
|
385
|
+
# Stop PTY reader when agent finishes
|
|
386
|
+
|
|
387
|
+
async def stop_pty_reader() -> None:
|
|
388
|
+
await pty_manager.stop_reader(run_id)
|
|
389
|
+
|
|
390
|
+
task = asyncio.create_task(stop_pty_reader())
|
|
391
|
+
task.add_done_callback(_log_broadcast_exception)
|
|
392
|
+
|
|
302
393
|
# Create async task to broadcast and attach exception callback
|
|
303
394
|
task = asyncio.create_task(
|
|
304
395
|
self.websocket_server.broadcast_agent_event(
|
|
@@ -314,7 +405,29 @@ class GobbyRunner:
|
|
|
314
405
|
task.add_done_callback(_log_broadcast_exception)
|
|
315
406
|
|
|
316
407
|
registry.add_event_callback(broadcast_agent_event)
|
|
317
|
-
logger.debug("Agent event broadcasting enabled")
|
|
408
|
+
logger.debug("Agent event broadcasting and PTY reading enabled")
|
|
409
|
+
|
|
410
|
+
def _setup_pipeline_event_broadcasting(self) -> None:
|
|
411
|
+
"""Set up WebSocket broadcasting for pipeline execution events."""
|
|
412
|
+
if not self.websocket_server:
|
|
413
|
+
return
|
|
414
|
+
|
|
415
|
+
if not self.pipeline_executor:
|
|
416
|
+
logger.debug("Pipeline event broadcasting skipped: no pipeline executor")
|
|
417
|
+
return
|
|
418
|
+
|
|
419
|
+
async def broadcast_pipeline_event(event: str, execution_id: str, **kwargs: Any) -> None:
|
|
420
|
+
"""Broadcast pipeline events via WebSocket."""
|
|
421
|
+
if self.websocket_server:
|
|
422
|
+
await self.websocket_server.broadcast_pipeline_event(
|
|
423
|
+
event=event,
|
|
424
|
+
execution_id=execution_id,
|
|
425
|
+
**kwargs,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Set the callback on the pipeline executor
|
|
429
|
+
self.pipeline_executor.event_callback = broadcast_pipeline_event
|
|
430
|
+
logger.debug("Pipeline event broadcasting enabled")
|
|
318
431
|
|
|
319
432
|
async def _metrics_cleanup_loop(self) -> None:
|
|
320
433
|
"""Background loop for periodic metrics cleanup (every 24 hours)."""
|
|
@@ -423,13 +536,14 @@ class GobbyRunner:
|
|
|
423
536
|
websocket_task = asyncio.create_task(self.websocket_server.start())
|
|
424
537
|
|
|
425
538
|
# Start HTTP server
|
|
426
|
-
|
|
539
|
+
graceful_shutdown_timeout = 15
|
|
427
540
|
config = uvicorn.Config(
|
|
428
541
|
self.http_server.app,
|
|
429
|
-
host=
|
|
542
|
+
host=self.config.bind_host,
|
|
430
543
|
port=self.http_server.port,
|
|
431
544
|
log_level="warning",
|
|
432
545
|
access_log=False,
|
|
546
|
+
timeout_graceful_shutdown=graceful_shutdown_timeout,
|
|
433
547
|
)
|
|
434
548
|
server = uvicorn.Server(config)
|
|
435
549
|
server_task = asyncio.create_task(server.serve())
|
|
@@ -439,9 +553,10 @@ class GobbyRunner:
|
|
|
439
553
|
await asyncio.sleep(0.5)
|
|
440
554
|
|
|
441
555
|
# Cleanup with timeouts to prevent hanging
|
|
556
|
+
# Use timeout slightly longer than uvicorn's graceful shutdown to let it finish
|
|
442
557
|
server.should_exit = True
|
|
443
558
|
try:
|
|
444
|
-
await asyncio.wait_for(server_task, timeout=
|
|
559
|
+
await asyncio.wait_for(server_task, timeout=graceful_shutdown_timeout + 5)
|
|
445
560
|
except TimeoutError:
|
|
446
561
|
logger.warning("HTTP server shutdown timed out")
|
|
447
562
|
|