code-puppy 0.0.341__py3-none-any.whl → 0.0.348__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.
- code_puppy/agents/base_agent.py +17 -248
- code_puppy/agents/event_stream_handler.py +257 -0
- code_puppy/cli_runner.py +4 -3
- code_puppy/command_line/add_model_menu.py +8 -9
- code_puppy/command_line/mcp/catalog_server_installer.py +5 -6
- code_puppy/command_line/mcp/custom_server_form.py +54 -19
- code_puppy/command_line/mcp/custom_server_installer.py +8 -9
- code_puppy/command_line/mcp/handler.py +0 -2
- code_puppy/command_line/mcp/help_command.py +1 -5
- code_puppy/command_line/mcp/start_command.py +36 -18
- code_puppy/command_line/onboarding_slides.py +0 -1
- code_puppy/command_line/prompt_toolkit_completion.py +16 -10
- code_puppy/command_line/utils.py +54 -0
- code_puppy/mcp_/async_lifecycle.py +35 -4
- code_puppy/mcp_/managed_server.py +49 -20
- code_puppy/mcp_/manager.py +81 -52
- code_puppy/messaging/message_queue.py +11 -23
- code_puppy/tools/agent_tools.py +66 -13
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.348.dist-info}/METADATA +1 -1
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.348.dist-info}/RECORD +25 -25
- code_puppy/command_line/mcp/add_command.py +0 -170
- {code_puppy-0.0.341.data → code_puppy-0.0.348.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.341.data → code_puppy-0.0.348.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.348.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.348.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.348.dist-info}/licenses/LICENSE +0 -0
code_puppy/mcp_/manager.py
CHANGED
|
@@ -469,41 +469,57 @@ class MCPManager:
|
|
|
469
469
|
def start_server_sync(self, server_id: str) -> bool:
|
|
470
470
|
"""
|
|
471
471
|
Synchronous wrapper for start_server.
|
|
472
|
+
|
|
473
|
+
IMPORTANT: This schedules the server start as a background task.
|
|
474
|
+
The server subprocess will start asynchronously - it may not be
|
|
475
|
+
immediately ready when this function returns.
|
|
472
476
|
"""
|
|
473
477
|
try:
|
|
474
|
-
asyncio.get_running_loop()
|
|
475
|
-
# We're in an async context
|
|
476
|
-
#
|
|
478
|
+
loop = asyncio.get_running_loop()
|
|
479
|
+
# We're in an async context - schedule the server start as a background task
|
|
480
|
+
# DO NOT use blocking time.sleep() here as it freezes the event loop!
|
|
481
|
+
|
|
482
|
+
# First, enable the server immediately so it's recognized as "starting"
|
|
483
|
+
managed_server = self._managed_servers.get(server_id)
|
|
484
|
+
if managed_server:
|
|
485
|
+
managed_server.enable()
|
|
486
|
+
self.status_tracker.set_status(server_id, ServerState.STARTING)
|
|
487
|
+
self.status_tracker.record_start_time(server_id)
|
|
477
488
|
|
|
478
|
-
#
|
|
479
|
-
|
|
480
|
-
|
|
489
|
+
# Schedule the async start_server to run in the background
|
|
490
|
+
# This will properly start the subprocess and lifecycle task
|
|
491
|
+
async def start_server_background():
|
|
492
|
+
try:
|
|
493
|
+
result = await self.start_server(server_id)
|
|
494
|
+
if result:
|
|
495
|
+
logger.info(f"Background server start completed: {server_id}")
|
|
496
|
+
else:
|
|
497
|
+
logger.warning(f"Background server start failed: {server_id}")
|
|
498
|
+
return result
|
|
499
|
+
except Exception as e:
|
|
500
|
+
logger.error(f"Background server start error for {server_id}: {e}")
|
|
501
|
+
self.status_tracker.set_status(server_id, ServerState.ERROR)
|
|
502
|
+
return False
|
|
481
503
|
|
|
482
|
-
#
|
|
483
|
-
task =
|
|
504
|
+
# Create the task - it will run when the event loop gets control
|
|
505
|
+
task = loop.create_task(
|
|
506
|
+
start_server_background(), name=f"start_server_{server_id}"
|
|
507
|
+
)
|
|
484
508
|
|
|
485
|
-
#
|
|
486
|
-
|
|
509
|
+
# Store task reference to prevent garbage collection
|
|
510
|
+
if not hasattr(self, "_pending_start_tasks"):
|
|
511
|
+
self._pending_start_tasks = {}
|
|
512
|
+
self._pending_start_tasks[server_id] = task
|
|
487
513
|
|
|
488
|
-
|
|
514
|
+
# Add callback to clean up task reference when done
|
|
515
|
+
def cleanup_task(t):
|
|
516
|
+
if hasattr(self, "_pending_start_tasks"):
|
|
517
|
+
self._pending_start_tasks.pop(server_id, None)
|
|
489
518
|
|
|
490
|
-
|
|
491
|
-
if task.done():
|
|
492
|
-
try:
|
|
493
|
-
result = task.result()
|
|
494
|
-
return result
|
|
495
|
-
except Exception:
|
|
496
|
-
pass
|
|
519
|
+
task.add_done_callback(cleanup_task)
|
|
497
520
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if managed_server:
|
|
501
|
-
managed_server.enable()
|
|
502
|
-
self.status_tracker.set_status(server_id, ServerState.RUNNING)
|
|
503
|
-
self.status_tracker.record_start_time(server_id)
|
|
504
|
-
logger.info(f"Enabled server synchronously: {server_id}")
|
|
505
|
-
return True
|
|
506
|
-
return False
|
|
521
|
+
logger.info(f"Scheduled background start for server: {server_id}")
|
|
522
|
+
return True # Return immediately - server will start in background
|
|
507
523
|
|
|
508
524
|
except RuntimeError:
|
|
509
525
|
# No async loop, just enable the server
|
|
@@ -582,39 +598,52 @@ class MCPManager:
|
|
|
582
598
|
def stop_server_sync(self, server_id: str) -> bool:
|
|
583
599
|
"""
|
|
584
600
|
Synchronous wrapper for stop_server.
|
|
601
|
+
|
|
602
|
+
IMPORTANT: This schedules the server stop as a background task.
|
|
603
|
+
The server subprocess will stop asynchronously.
|
|
585
604
|
"""
|
|
586
605
|
try:
|
|
587
|
-
asyncio.get_running_loop()
|
|
606
|
+
loop = asyncio.get_running_loop()
|
|
607
|
+
# We're in an async context - schedule the server stop as a background task
|
|
608
|
+
# DO NOT use blocking time.sleep() here as it freezes the event loop!
|
|
588
609
|
|
|
589
|
-
#
|
|
590
|
-
|
|
591
|
-
|
|
610
|
+
# First, disable the server immediately
|
|
611
|
+
managed_server = self._managed_servers.get(server_id)
|
|
612
|
+
if managed_server:
|
|
613
|
+
managed_server.disable()
|
|
614
|
+
self.status_tracker.set_status(server_id, ServerState.STOPPING)
|
|
615
|
+
self.status_tracker.record_stop_time(server_id)
|
|
592
616
|
|
|
593
|
-
# Schedule the
|
|
594
|
-
|
|
617
|
+
# Schedule the async stop_server to run in the background
|
|
618
|
+
async def stop_server_background():
|
|
619
|
+
try:
|
|
620
|
+
result = await self.stop_server(server_id)
|
|
621
|
+
if result:
|
|
622
|
+
logger.info(f"Background server stop completed: {server_id}")
|
|
623
|
+
return result
|
|
624
|
+
except Exception as e:
|
|
625
|
+
logger.error(f"Background server stop error for {server_id}: {e}")
|
|
626
|
+
return False
|
|
595
627
|
|
|
596
|
-
#
|
|
597
|
-
|
|
628
|
+
# Create the task - it will run when the event loop gets control
|
|
629
|
+
task = loop.create_task(
|
|
630
|
+
stop_server_background(), name=f"stop_server_{server_id}"
|
|
631
|
+
)
|
|
598
632
|
|
|
599
|
-
|
|
633
|
+
# Store task reference to prevent garbage collection
|
|
634
|
+
if not hasattr(self, "_pending_stop_tasks"):
|
|
635
|
+
self._pending_stop_tasks = {}
|
|
636
|
+
self._pending_stop_tasks[server_id] = task
|
|
600
637
|
|
|
601
|
-
#
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
return result
|
|
606
|
-
except Exception:
|
|
607
|
-
pass
|
|
638
|
+
# Add callback to clean up task reference when done
|
|
639
|
+
def cleanup_task(t):
|
|
640
|
+
if hasattr(self, "_pending_stop_tasks"):
|
|
641
|
+
self._pending_stop_tasks.pop(server_id, None)
|
|
608
642
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
self.status_tracker.set_status(server_id, ServerState.STOPPED)
|
|
614
|
-
self.status_tracker.record_stop_time(server_id)
|
|
615
|
-
logger.info(f"Disabled server synchronously: {server_id}")
|
|
616
|
-
return True
|
|
617
|
-
return False
|
|
643
|
+
task.add_done_callback(cleanup_task)
|
|
644
|
+
|
|
645
|
+
logger.info(f"Scheduled background stop for server: {server_id}")
|
|
646
|
+
return True # Return immediately - server will stop in background
|
|
618
647
|
|
|
619
648
|
except RuntimeError:
|
|
620
649
|
# No async loop, just disable the server
|
|
@@ -329,31 +329,19 @@ def emit_divider(content: str = "─" * 100 + "\n", **metadata):
|
|
|
329
329
|
|
|
330
330
|
|
|
331
331
|
def emit_prompt(prompt_text: str, timeout: float = None) -> str:
|
|
332
|
-
"""Emit a human input request and wait for response.
|
|
333
|
-
# TUI mode has been removed, always use interactive mode input
|
|
334
|
-
if True:
|
|
335
|
-
# Emit the prompt as a message for display
|
|
336
|
-
from code_puppy.messaging import emit_info
|
|
332
|
+
"""Emit a human input request and wait for response.
|
|
337
333
|
|
|
338
|
-
|
|
334
|
+
Uses safe_input for cross-platform compatibility, especially on Windows
|
|
335
|
+
where raw input() can fail after prompt_toolkit Applications.
|
|
336
|
+
"""
|
|
337
|
+
from code_puppy.command_line.utils import safe_input
|
|
338
|
+
from code_puppy.messaging import emit_info
|
|
339
339
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
console = Console()
|
|
346
|
-
response = console.input("[cyan]>>> [/cyan]")
|
|
347
|
-
return response
|
|
348
|
-
except Exception:
|
|
349
|
-
# Fallback to basic input
|
|
350
|
-
response = input(">>> ")
|
|
351
|
-
return response
|
|
352
|
-
|
|
353
|
-
# In TUI mode, use the queue system
|
|
354
|
-
queue = get_global_queue()
|
|
355
|
-
prompt_id = queue.create_prompt_request(prompt_text)
|
|
356
|
-
return queue.wait_for_prompt_response(prompt_id, timeout)
|
|
340
|
+
emit_info(prompt_text)
|
|
341
|
+
|
|
342
|
+
# Use safe_input which resets Windows console state before reading
|
|
343
|
+
response = safe_input(">>> ")
|
|
344
|
+
return response
|
|
357
345
|
|
|
358
346
|
|
|
359
347
|
def provide_prompt_response(prompt_id: str, response: str):
|
code_puppy/tools/agent_tools.py
CHANGED
|
@@ -21,6 +21,7 @@ from code_puppy.config import (
|
|
|
21
21
|
DATA_DIR,
|
|
22
22
|
get_message_limit,
|
|
23
23
|
get_use_dbos,
|
|
24
|
+
get_value,
|
|
24
25
|
)
|
|
25
26
|
from code_puppy.messaging import (
|
|
26
27
|
SubAgentInvocationMessage,
|
|
@@ -471,39 +472,90 @@ def register_invoke_agent(agent):
|
|
|
471
472
|
subagent_name = f"temp-invoke-agent-{session_id}"
|
|
472
473
|
model_settings = make_model_settings(model_name)
|
|
473
474
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
instructions=instructions,
|
|
477
|
-
output_type=str,
|
|
478
|
-
retries=3,
|
|
479
|
-
history_processors=[agent_config.message_history_accumulator],
|
|
480
|
-
model_settings=model_settings,
|
|
481
|
-
)
|
|
475
|
+
# Get MCP servers for sub-agents (same as main agent)
|
|
476
|
+
from code_puppy.mcp_ import get_mcp_manager
|
|
482
477
|
|
|
483
|
-
|
|
484
|
-
|
|
478
|
+
mcp_servers = []
|
|
479
|
+
mcp_disabled = get_value("disable_mcp_servers")
|
|
480
|
+
if not (
|
|
481
|
+
mcp_disabled and str(mcp_disabled).lower() in ("1", "true", "yes", "on")
|
|
482
|
+
):
|
|
483
|
+
manager = get_mcp_manager()
|
|
484
|
+
mcp_servers = manager.get_servers_for_agent()
|
|
485
485
|
|
|
486
|
-
|
|
487
|
-
|
|
486
|
+
# Get the event_stream_handler for streaming output
|
|
487
|
+
from code_puppy.agents.event_stream_handler import event_stream_handler
|
|
488
488
|
|
|
489
489
|
if get_use_dbos():
|
|
490
490
|
from pydantic_ai.durable_exec.dbos import DBOSAgent
|
|
491
491
|
|
|
492
|
-
|
|
492
|
+
# For DBOS, create agent without MCP servers (to avoid serialization issues)
|
|
493
|
+
# and add them at runtime
|
|
494
|
+
temp_agent = Agent(
|
|
495
|
+
model=model,
|
|
496
|
+
instructions=instructions,
|
|
497
|
+
output_type=str,
|
|
498
|
+
retries=3,
|
|
499
|
+
toolsets=[], # MCP servers added separately for DBOS
|
|
500
|
+
history_processors=[agent_config.message_history_accumulator],
|
|
501
|
+
model_settings=model_settings,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
# Register the tools that the agent needs
|
|
505
|
+
from code_puppy.tools import register_tools_for_agent
|
|
506
|
+
|
|
507
|
+
agent_tools = agent_config.get_available_tools()
|
|
508
|
+
register_tools_for_agent(temp_agent, agent_tools)
|
|
509
|
+
|
|
510
|
+
# Wrap with DBOS - pass event_stream_handler for streaming output
|
|
511
|
+
dbos_agent = DBOSAgent(
|
|
512
|
+
temp_agent,
|
|
513
|
+
name=subagent_name,
|
|
514
|
+
event_stream_handler=event_stream_handler,
|
|
515
|
+
)
|
|
493
516
|
temp_agent = dbos_agent
|
|
494
517
|
|
|
518
|
+
# Store MCP servers to add at runtime
|
|
519
|
+
subagent_mcp_servers = mcp_servers
|
|
520
|
+
else:
|
|
521
|
+
# Non-DBOS path - include MCP servers directly in the agent
|
|
522
|
+
temp_agent = Agent(
|
|
523
|
+
model=model,
|
|
524
|
+
instructions=instructions,
|
|
525
|
+
output_type=str,
|
|
526
|
+
retries=3,
|
|
527
|
+
toolsets=mcp_servers,
|
|
528
|
+
history_processors=[agent_config.message_history_accumulator],
|
|
529
|
+
model_settings=model_settings,
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
# Register the tools that the agent needs
|
|
533
|
+
from code_puppy.tools import register_tools_for_agent
|
|
534
|
+
|
|
535
|
+
agent_tools = agent_config.get_available_tools()
|
|
536
|
+
register_tools_for_agent(temp_agent, agent_tools)
|
|
537
|
+
|
|
538
|
+
subagent_mcp_servers = None
|
|
539
|
+
|
|
495
540
|
# Run the temporary agent with the provided prompt as an asyncio task
|
|
496
541
|
# Pass the message_history from the session to continue the conversation
|
|
497
542
|
workflow_id = None # Track for potential cancellation
|
|
498
543
|
if get_use_dbos():
|
|
499
544
|
# Generate a unique workflow ID for DBOS - ensures no collisions in back-to-back calls
|
|
500
545
|
workflow_id = _generate_dbos_workflow_id(group_id)
|
|
546
|
+
|
|
547
|
+
# Add MCP servers to the DBOS agent's toolsets
|
|
548
|
+
# (temp_agent is discarded after this invocation, so no need to restore)
|
|
549
|
+
if subagent_mcp_servers:
|
|
550
|
+
temp_agent._toolsets = temp_agent._toolsets + subagent_mcp_servers
|
|
551
|
+
|
|
501
552
|
with SetWorkflowID(workflow_id):
|
|
502
553
|
task = asyncio.create_task(
|
|
503
554
|
temp_agent.run(
|
|
504
555
|
prompt,
|
|
505
556
|
message_history=message_history,
|
|
506
557
|
usage_limits=UsageLimits(request_limit=get_message_limit()),
|
|
558
|
+
event_stream_handler=event_stream_handler,
|
|
507
559
|
)
|
|
508
560
|
)
|
|
509
561
|
_active_subagent_tasks.add(task)
|
|
@@ -513,6 +565,7 @@ def register_invoke_agent(agent):
|
|
|
513
565
|
prompt,
|
|
514
566
|
message_history=message_history,
|
|
515
567
|
usage_limits=UsageLimits(request_limit=get_message_limit()),
|
|
568
|
+
event_stream_handler=event_stream_handler,
|
|
516
569
|
)
|
|
517
570
|
)
|
|
518
571
|
_active_subagent_tasks.add(task)
|
|
@@ -3,7 +3,7 @@ code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
|
|
|
3
3
|
code_puppy/callbacks.py,sha256=hqTV--dNxG5vwWWm3MrEjmb8MZuHFFdmHePl23NXPHk,8621
|
|
4
4
|
code_puppy/chatgpt_codex_client.py,sha256=Om0ANB_kpHubhCwNzF9ENf8RvKBqs0IYzBLl_SNw0Vk,9833
|
|
5
5
|
code_puppy/claude_cache_client.py,sha256=MLIRSJP428r9IK_aV6XyCXrCfQnNti32U60psPymLM4,14860
|
|
6
|
-
code_puppy/cli_runner.py,sha256=
|
|
6
|
+
code_puppy/cli_runner.py,sha256=w5CLKgQYYaT7My3Cga2StXYol-u6DBxNzzUuhhsfhsA,34952
|
|
7
7
|
code_puppy/config.py,sha256=RlnrLkyFXm7h2Htf8rQA7vqoAyzLPMrESle417uLmFw,52373
|
|
8
8
|
code_puppy/error_logging.py,sha256=a80OILCUtJhexI6a9GM-r5LqIdjvSRzggfgPp2jv1X0,3297
|
|
9
9
|
code_puppy/gemini_code_assist.py,sha256=KGS7sO5OLc83nDF3xxS-QiU6vxW9vcm6hmzilu79Ef8,13867
|
|
@@ -40,11 +40,12 @@ code_puppy/agents/agent_qa_expert.py,sha256=5Ikb4U3SZQknUEfwlHZiyZXKqnffnOTQagr_
|
|
|
40
40
|
code_puppy/agents/agent_qa_kitten.py,sha256=5PeFFSwCFlTUvP6h5bGntx0xv5NmRwBiw0HnMqY8nLI,9107
|
|
41
41
|
code_puppy/agents/agent_security_auditor.py,sha256=SpiYNA0XAsIwBj7S2_EQPRslRUmF_-b89pIJyW7DYtY,12022
|
|
42
42
|
code_puppy/agents/agent_typescript_reviewer.py,sha256=vsnpp98xg6cIoFAEJrRTUM_i4wLEWGm5nJxs6fhHobM,10275
|
|
43
|
-
code_puppy/agents/base_agent.py,sha256=
|
|
43
|
+
code_puppy/agents/base_agent.py,sha256=zX7XPNgveBoBCm-SoRncwabnU0uSyEwtG3q-x8JFkiU,73256
|
|
44
|
+
code_puppy/agents/event_stream_handler.py,sha256=C1TDkp9eTHEFvnTQzaGFh_q9izL1r-EnCRTez9kqO2Y,11438
|
|
44
45
|
code_puppy/agents/json_agent.py,sha256=lhopDJDoiSGHvD8A6t50hi9ZBoNRKgUywfxd0Po_Dzc,4886
|
|
45
46
|
code_puppy/agents/prompt_reviewer.py,sha256=JJrJ0m5q0Puxl8vFsyhAbY9ftU9n6c6UxEVdNct1E-Q,5558
|
|
46
47
|
code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
|
|
47
|
-
code_puppy/command_line/add_model_menu.py,sha256=
|
|
48
|
+
code_puppy/command_line/add_model_menu.py,sha256=CpURhxPvUhLHLBV_uwH1ODfJ-WAcGklvlsjEf5Vfvg4,43255
|
|
48
49
|
code_puppy/command_line/attachments.py,sha256=4Q5I2Es4j0ltnz5wjw2z0QXMsiMJvEfWRkPf_lJeITM,13093
|
|
49
50
|
code_puppy/command_line/autosave_menu.py,sha256=de7nOmFmEH6x5T7C95U8N8xgxxeF-l5lgaJzGJsF3ZY,19824
|
|
50
51
|
code_puppy/command_line/clipboard.py,sha256=oe9bfAX5RnT81FiYrDmhvHaePS1tAT-NFG1fSXubSD4,16869
|
|
@@ -60,21 +61,20 @@ code_puppy/command_line/mcp_completion.py,sha256=eKzW2O7gun7HoHekOW0XVXhNS5J2xCt
|
|
|
60
61
|
code_puppy/command_line/model_picker_completion.py,sha256=nDnlf0qFCG2zAm_mWW2eMYwVC7eROVQrFe92hZqOKa8,6810
|
|
61
62
|
code_puppy/command_line/model_settings_menu.py,sha256=AI97IusDgMmWoCOp7C0Yrk_Uy6M9cmVhoZfVWgFWwXg,32392
|
|
62
63
|
code_puppy/command_line/motd.py,sha256=XuIk3UTLawwVFM-NfoaJGU5F2hPLASTFXq84UdDMT0Q,2408
|
|
63
|
-
code_puppy/command_line/onboarding_slides.py,sha256=
|
|
64
|
+
code_puppy/command_line/onboarding_slides.py,sha256=itqAsuHzjHpD_XNz6FniBIYr6dNyP1AW_XQZQ6SbVek,7125
|
|
64
65
|
code_puppy/command_line/onboarding_wizard.py,sha256=U5lV_1P3IwDYZUHar0zKgdp121zzkvOwwORvdCZwFcw,10241
|
|
65
66
|
code_puppy/command_line/pin_command_completion.py,sha256=juSvdqRpk7AdfkPy1DJx5NzfEUU5KYGlChvP0hisM18,11667
|
|
66
|
-
code_puppy/command_line/prompt_toolkit_completion.py,sha256=
|
|
67
|
+
code_puppy/command_line/prompt_toolkit_completion.py,sha256=49GM3jVE89G1M3XroMZk2LhGgXpOO8XZ0Sg8h4a6LLw,32806
|
|
67
68
|
code_puppy/command_line/session_commands.py,sha256=Jh8GGfhlfBAEVfucKLbcZjNaXYd0twImiOwq2ZnGdQQ,9902
|
|
68
|
-
code_puppy/command_line/utils.py,sha256=
|
|
69
|
+
code_puppy/command_line/utils.py,sha256=_upMrrmDp5hteUjrRiEgVR6SoeNEPNZGXb5lOWYqcUQ,2952
|
|
69
70
|
code_puppy/command_line/mcp/__init__.py,sha256=0-OQuwjq_pLiTVJ1_NrirVwdRerghyKs_MTZkwPC7YY,315
|
|
70
|
-
code_puppy/command_line/mcp/add_command.py,sha256=iWqHReWbVOO3kuPE4NTMs3dv_BluxTBaasDPm9P1lU0,5892
|
|
71
71
|
code_puppy/command_line/mcp/base.py,sha256=pPeNnSyM0GGqD6mhYN-qA22rAT9bEapxliwH_YiIu3Q,823
|
|
72
|
-
code_puppy/command_line/mcp/catalog_server_installer.py,sha256=
|
|
73
|
-
code_puppy/command_line/mcp/custom_server_form.py,sha256=
|
|
74
|
-
code_puppy/command_line/mcp/custom_server_installer.py,sha256=
|
|
72
|
+
code_puppy/command_line/mcp/catalog_server_installer.py,sha256=G9FvQPTBB4LeJ4cOR9DvIkp82mClKRKI35KELbFLCKU,6181
|
|
73
|
+
code_puppy/command_line/mcp/custom_server_form.py,sha256=z0hsqXY1_ScJoacneyfrFHeVUlU3ZUHYt6CXV1wnJ0Y,23733
|
|
74
|
+
code_puppy/command_line/mcp/custom_server_installer.py,sha256=oLB5j07XKApdTrCDlY97GxE1NHrqupsX6DOCXzFj3TE,5744
|
|
75
75
|
code_puppy/command_line/mcp/edit_command.py,sha256=_WxxpaTgxo9pbvMogG9yvh2mcLE5SYf0Qbi8a8IpZ0k,4603
|
|
76
|
-
code_puppy/command_line/mcp/handler.py,sha256=
|
|
77
|
-
code_puppy/command_line/mcp/help_command.py,sha256=
|
|
76
|
+
code_puppy/command_line/mcp/handler.py,sha256=jwhcLi28QQ6IuE6E5IsMbU67jMZChfZW96hG9ezYgN4,4547
|
|
77
|
+
code_puppy/command_line/mcp/help_command.py,sha256=sTWecPqmmq6vmLVgZVNjBXKnziCLoJSKsugHkxiJlTI,5285
|
|
78
78
|
code_puppy/command_line/mcp/install_command.py,sha256=lmUyMUWtkGuy1SOQRHjQgt8mD3t1agVMQfEL5_TOzTM,8364
|
|
79
79
|
code_puppy/command_line/mcp/install_menu.py,sha256=GVNR7SJbheGLFc_r9N3CT1AT024ptzsEcj1cRnp4U3g,24769
|
|
80
80
|
code_puppy/command_line/mcp/list_command.py,sha256=UKQFPlhT9qGMCyG5VKjvnSMzDDtfAhIaKU_eErgZJDg,3181
|
|
@@ -83,7 +83,7 @@ code_puppy/command_line/mcp/remove_command.py,sha256=hyU_tKJWfyLnmufrFVLwlF0qFEb
|
|
|
83
83
|
code_puppy/command_line/mcp/restart_command.py,sha256=w5EcDac09iCvPBAR0u2M5KSIhASqTu5uZwsjCJ4JLhk,3588
|
|
84
84
|
code_puppy/command_line/mcp/search_command.py,sha256=mDkSz_KjPbvlO9U7oYUKJlqqY4QM90gWKO2xsH2i3SA,4244
|
|
85
85
|
code_puppy/command_line/mcp/start_all_command.py,sha256=_TVrjRR_oqJVS6qQaHssS0V_3342av1h9gz-Fxwa-Dw,4667
|
|
86
|
-
code_puppy/command_line/mcp/start_command.py,sha256=
|
|
86
|
+
code_puppy/command_line/mcp/start_command.py,sha256=XhuhyMpuo2Bkp53qxvZBSM8yRGatcwY8m-DXcQBHAZc,4344
|
|
87
87
|
code_puppy/command_line/mcp/status_command.py,sha256=NCyjFlBMURQ39T3G2dTPbyJdNC6X14FyWFzVh4BBnig,6657
|
|
88
88
|
code_puppy/command_line/mcp/stop_all_command.py,sha256=33mRvxd2cBbTXE6BSkSzFmdDOT4yCxwGEDrHczqpavw,3864
|
|
89
89
|
code_puppy/command_line/mcp/stop_command.py,sha256=iMzk9h6NuUDg0hhI5eDLem5VS8IwBC7xg8AU-7jsmBE,2679
|
|
@@ -91,7 +91,7 @@ code_puppy/command_line/mcp/test_command.py,sha256=eV8u5KKClRK1M2_os1zA78b9TDuYU
|
|
|
91
91
|
code_puppy/command_line/mcp/utils.py,sha256=0Wt4ttYgSlVvtusYmBLKXSkjAjcsDiUxcZQAoFLUNnE,3625
|
|
92
92
|
code_puppy/command_line/mcp/wizard_utils.py,sha256=M5X8RchkQujKYKORsXJnUq2kJHzmNfutIUrsHmfzi7k,11126
|
|
93
93
|
code_puppy/mcp_/__init__.py,sha256=P9bmVX5UDmzQDqHMylOxuo5Hi82E30pPSMOYw8lEx7Q,1781
|
|
94
|
-
code_puppy/mcp_/async_lifecycle.py,sha256=
|
|
94
|
+
code_puppy/mcp_/async_lifecycle.py,sha256=XVB73WOc2_sWi0ySWh_h6n5a-xtt72BGvtajyeoKW20,9014
|
|
95
95
|
code_puppy/mcp_/blocking_startup.py,sha256=27R2wwLVDu5i19IP90KmCn_zHrvVcHVNU8d9c8EcTeI,14780
|
|
96
96
|
code_puppy/mcp_/captured_stdio_server.py,sha256=t_mnCjtiopRsyi4Aa97rFzDVxEQmb-u94sWJsj2FP8k,8925
|
|
97
97
|
code_puppy/mcp_/circuit_breaker.py,sha256=a83YwXux9h4R6zBWBUrCIqtp2ffyl7JZEoK2tErG_0I,8601
|
|
@@ -99,8 +99,8 @@ code_puppy/mcp_/config_wizard.py,sha256=JNNpgnSD6PFSyS3pTdEdD164oXd2VKp4VHLSz3To
|
|
|
99
99
|
code_puppy/mcp_/dashboard.py,sha256=VtaFxLtPnbM_HL2TXRDAg6IqcM-EcFkoghGgkfhMrKI,9417
|
|
100
100
|
code_puppy/mcp_/error_isolation.py,sha256=mpPBiH17zTXPsOEAn9WmkbwQwnt4gmgiaWv87JBJbUo,12426
|
|
101
101
|
code_puppy/mcp_/health_monitor.py,sha256=n5R6EeYOYbUucUFe74qGWCU3g6Mep5UEQbLF0wbT0dU,19688
|
|
102
|
-
code_puppy/mcp_/managed_server.py,sha256=
|
|
103
|
-
code_puppy/mcp_/manager.py,sha256=
|
|
102
|
+
code_puppy/mcp_/managed_server.py,sha256=e-DetKb3bVgdLYw7miTptLAbqhRgZ1cNoTGS0fh4Noo,15371
|
|
103
|
+
code_puppy/mcp_/manager.py,sha256=_2ZRTLS8Sf3SMgEpAHl-wXBDYVqg2l-j9uI9EkfCNaE,31062
|
|
104
104
|
code_puppy/mcp_/mcp_logs.py,sha256=o4pSHwELWIjEjqhfaMMEGrBvb159-VIgUp21E707BPo,6264
|
|
105
105
|
code_puppy/mcp_/registry.py,sha256=U_t12WQ-En-KGyZoiTYdqlhp9NkDTWafu8g5InvF2NM,15774
|
|
106
106
|
code_puppy/mcp_/retry_manager.py,sha256=evVxbtrsHNyo8UoI7zpO-NVDegibn82RLlgN8VKewA8,10665
|
|
@@ -112,7 +112,7 @@ code_puppy/messaging/__init__.py,sha256=THJQDdRub3jiWIRPqF34VggXem3Y2tuUFAJGdDAL
|
|
|
112
112
|
code_puppy/messaging/bus.py,sha256=TbdltJ0D5tqnaE4irq1fcXllDYm-mQ_SiX1IFm-S4sw,21406
|
|
113
113
|
code_puppy/messaging/commands.py,sha256=77CtKVNaF5KS3Xyzd0ccDAisZWQxL3weVEt3J-SfYxo,5464
|
|
114
114
|
code_puppy/messaging/markdown_patches.py,sha256=dMIJozzJChuHa8QNMSEz_kC-dyt7kZiDLZ7rjthbcmg,1626
|
|
115
|
-
code_puppy/messaging/message_queue.py,sha256=
|
|
115
|
+
code_puppy/messaging/message_queue.py,sha256=1-5NFWIes5kpecsKnhuQQJPeT0-X102Xi1-IwXUM5_Y,11430
|
|
116
116
|
code_puppy/messaging/messages.py,sha256=F7RwMHeQrIk-8kuSSBU76wBq1NGuLb2H5cJrSMTC3XM,16464
|
|
117
117
|
code_puppy/messaging/queue_console.py,sha256=T0U_V1tdN6hd9DLokp-HCk0mhu8Ivpfajha368CBZrU,9983
|
|
118
118
|
code_puppy/messaging/renderers.py,sha256=GHVtMnxE1pJ-yrcRjacY81JcjlHRz3UVHzp-ohN-CGE,12058
|
|
@@ -159,7 +159,7 @@ code_puppy/plugins/shell_safety/command_cache.py,sha256=adYtSPNVOZfW_6dQdtEihO6E
|
|
|
159
159
|
code_puppy/plugins/shell_safety/register_callbacks.py,sha256=W3v664RR48Fdbbbltf_NnX22_Ahw2AvAOtvXvWc7KxQ,7322
|
|
160
160
|
code_puppy/prompts/codex_system_prompt.md,sha256=hEFTCziroLqZmqNle5kG34A8kvTteOWezCiVrAEKhE0,24400
|
|
161
161
|
code_puppy/tools/__init__.py,sha256=BVTZ85jLHgDANwOnUSOz3UDlp8VQDq4DoGF23BRlyWw,6032
|
|
162
|
-
code_puppy/tools/agent_tools.py,sha256=
|
|
162
|
+
code_puppy/tools/agent_tools.py,sha256=pRIzGH8jJjlg1XiMrQn_kn0OzUfsCq4EWTuISD2D6hQ,23393
|
|
163
163
|
code_puppy/tools/command_runner.py,sha256=3qXVnVTaBPia6y2D29As47_TRKgpyCj82yMFK-8UUYc,44954
|
|
164
164
|
code_puppy/tools/common.py,sha256=IYf-KOcP5eN2MwTlpULSXNATn7GzloAKl7_M1Uyfe4Y,40360
|
|
165
165
|
code_puppy/tools/file_modifications.py,sha256=vz9n7R0AGDSdLUArZr_55yJLkyI30M8zreAppxIx02M,29380
|
|
@@ -175,10 +175,10 @@ code_puppy/tools/browser/browser_scripts.py,sha256=sNb8eLEyzhasy5hV4B9OjM8yIVMLV
|
|
|
175
175
|
code_puppy/tools/browser/browser_workflows.py,sha256=nitW42vCf0ieTX1gLabozTugNQ8phtoFzZbiAhw1V90,6491
|
|
176
176
|
code_puppy/tools/browser/camoufox_manager.py,sha256=RZjGOEftE5sI_tsercUyXFSZI2wpStXf-q0PdYh2G3I,8680
|
|
177
177
|
code_puppy/tools/browser/vqa_agent.py,sha256=DBn9HKloILqJSTSdNZzH_PYWT0B2h9VwmY6akFQI_uU,2913
|
|
178
|
-
code_puppy-0.0.
|
|
179
|
-
code_puppy-0.0.
|
|
180
|
-
code_puppy-0.0.
|
|
181
|
-
code_puppy-0.0.
|
|
182
|
-
code_puppy-0.0.
|
|
183
|
-
code_puppy-0.0.
|
|
184
|
-
code_puppy-0.0.
|
|
178
|
+
code_puppy-0.0.348.data/data/code_puppy/models.json,sha256=FMQdE_yvP_8y0xxt3K918UkFL9cZMYAqW1SfXcQkU_k,3105
|
|
179
|
+
code_puppy-0.0.348.data/data/code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
|
|
180
|
+
code_puppy-0.0.348.dist-info/METADATA,sha256=jDNlXNr6nSsNNf8O2QJCuRlD9rZWGOV-fU-iZPt4kF0,27550
|
|
181
|
+
code_puppy-0.0.348.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
182
|
+
code_puppy-0.0.348.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
|
|
183
|
+
code_puppy-0.0.348.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
|
|
184
|
+
code_puppy-0.0.348.dist-info/RECORD,,
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
MCP Add Command - Adds new MCP servers from JSON configuration or wizard.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import logging
|
|
7
|
-
import os
|
|
8
|
-
from typing import List, Optional
|
|
9
|
-
|
|
10
|
-
from code_puppy.messaging import emit_error, emit_info
|
|
11
|
-
|
|
12
|
-
from .base import MCPCommandBase
|
|
13
|
-
from .wizard_utils import run_interactive_install_wizard
|
|
14
|
-
|
|
15
|
-
# Configure logging
|
|
16
|
-
logger = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class AddCommand(MCPCommandBase):
|
|
20
|
-
"""
|
|
21
|
-
Command handler for adding MCP servers.
|
|
22
|
-
|
|
23
|
-
Adds new MCP servers from JSON configuration or interactive wizard.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
27
|
-
"""
|
|
28
|
-
Add a new MCP server from JSON configuration or launch wizard.
|
|
29
|
-
|
|
30
|
-
Usage:
|
|
31
|
-
/mcp add - Launch interactive wizard
|
|
32
|
-
/mcp add <json> - Add server from JSON config
|
|
33
|
-
|
|
34
|
-
Example JSON:
|
|
35
|
-
/mcp add {"name": "test", "type": "stdio", "command": "echo", "args": ["hello"]}
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
args: Command arguments - JSON config or empty for wizard
|
|
39
|
-
group_id: Optional message group ID for grouping related messages
|
|
40
|
-
"""
|
|
41
|
-
if group_id is None:
|
|
42
|
-
group_id = self.generate_group_id()
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
if args:
|
|
46
|
-
# Parse JSON from arguments
|
|
47
|
-
json_str = " ".join(args)
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
config_dict = json.loads(json_str)
|
|
51
|
-
except json.JSONDecodeError as e:
|
|
52
|
-
emit_info(f"Invalid JSON: {e}", message_group=group_id)
|
|
53
|
-
emit_info(
|
|
54
|
-
"Usage: /mcp add <json> or /mcp add (for wizard)",
|
|
55
|
-
message_group=group_id,
|
|
56
|
-
)
|
|
57
|
-
emit_info(
|
|
58
|
-
'Example: /mcp add {"name": "test", "type": "stdio", "command": "echo"}',
|
|
59
|
-
message_group=group_id,
|
|
60
|
-
)
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
# Validate required fields
|
|
64
|
-
if "name" not in config_dict:
|
|
65
|
-
emit_info("Missing required field: 'name'", message_group=group_id)
|
|
66
|
-
return
|
|
67
|
-
if "type" not in config_dict:
|
|
68
|
-
emit_info("Missing required field: 'type'", message_group=group_id)
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
# Add the server
|
|
72
|
-
success = self._add_server_from_json(config_dict, group_id)
|
|
73
|
-
|
|
74
|
-
if success:
|
|
75
|
-
# Reload MCP servers
|
|
76
|
-
try:
|
|
77
|
-
from code_puppy.agent import reload_mcp_servers
|
|
78
|
-
|
|
79
|
-
reload_mcp_servers()
|
|
80
|
-
except ImportError:
|
|
81
|
-
pass
|
|
82
|
-
|
|
83
|
-
emit_info(
|
|
84
|
-
"Use '/mcp list' to see all servers", message_group=group_id
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
else:
|
|
88
|
-
# No arguments - launch interactive wizard with server templates
|
|
89
|
-
success = run_interactive_install_wizard(self.manager, group_id)
|
|
90
|
-
|
|
91
|
-
if success:
|
|
92
|
-
# Reload the agent to pick up new server
|
|
93
|
-
try:
|
|
94
|
-
from code_puppy.agent import reload_mcp_servers
|
|
95
|
-
|
|
96
|
-
reload_mcp_servers()
|
|
97
|
-
except ImportError:
|
|
98
|
-
pass
|
|
99
|
-
|
|
100
|
-
except ImportError as e:
|
|
101
|
-
logger.error(f"Failed to import: {e}")
|
|
102
|
-
emit_info("Required module not available", message_group=group_id)
|
|
103
|
-
except Exception as e:
|
|
104
|
-
logger.error(f"Error in add command: {e}")
|
|
105
|
-
emit_error(f"Error adding server: {e}", message_group=group_id)
|
|
106
|
-
|
|
107
|
-
def _add_server_from_json(self, config_dict: dict, group_id: str) -> bool:
|
|
108
|
-
"""
|
|
109
|
-
Add a server from JSON configuration.
|
|
110
|
-
|
|
111
|
-
Args:
|
|
112
|
-
config_dict: Server configuration dictionary
|
|
113
|
-
group_id: Message group ID
|
|
114
|
-
|
|
115
|
-
Returns:
|
|
116
|
-
True if successful, False otherwise
|
|
117
|
-
"""
|
|
118
|
-
try:
|
|
119
|
-
from code_puppy.config import MCP_SERVERS_FILE
|
|
120
|
-
from code_puppy.mcp_.managed_server import ServerConfig
|
|
121
|
-
|
|
122
|
-
# Extract required fields
|
|
123
|
-
name = config_dict.pop("name")
|
|
124
|
-
server_type = config_dict.pop("type")
|
|
125
|
-
enabled = config_dict.pop("enabled", True)
|
|
126
|
-
|
|
127
|
-
# Everything else goes into config
|
|
128
|
-
server_config = ServerConfig(
|
|
129
|
-
id=f"{name}_{hash(name)}",
|
|
130
|
-
name=name,
|
|
131
|
-
type=server_type,
|
|
132
|
-
enabled=enabled,
|
|
133
|
-
config=config_dict, # Remaining fields are server-specific config
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
# Register the server
|
|
137
|
-
server_id = self.manager.register_server(server_config)
|
|
138
|
-
|
|
139
|
-
if not server_id:
|
|
140
|
-
emit_info(f"Failed to add server '{name}'", message_group=group_id)
|
|
141
|
-
return False
|
|
142
|
-
|
|
143
|
-
emit_info(
|
|
144
|
-
f"✅ Added server '{name}' (ID: {server_id})", message_group=group_id
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
# Save to mcp_servers.json for persistence
|
|
148
|
-
if os.path.exists(MCP_SERVERS_FILE):
|
|
149
|
-
with open(MCP_SERVERS_FILE, "r") as f:
|
|
150
|
-
data = json.load(f)
|
|
151
|
-
servers = data.get("mcp_servers", {})
|
|
152
|
-
else:
|
|
153
|
-
servers = {}
|
|
154
|
-
data = {"mcp_servers": servers}
|
|
155
|
-
|
|
156
|
-
# Add new server
|
|
157
|
-
servers[name] = config_dict.copy()
|
|
158
|
-
servers[name]["type"] = server_type
|
|
159
|
-
|
|
160
|
-
# Save back
|
|
161
|
-
os.makedirs(os.path.dirname(MCP_SERVERS_FILE), exist_ok=True)
|
|
162
|
-
with open(MCP_SERVERS_FILE, "w") as f:
|
|
163
|
-
json.dump(data, f, indent=2)
|
|
164
|
-
|
|
165
|
-
return True
|
|
166
|
-
|
|
167
|
-
except Exception as e:
|
|
168
|
-
logger.error(f"Error adding server from JSON: {e}")
|
|
169
|
-
emit_error(f"Failed to add server: {e}", message_group=group_id)
|
|
170
|
-
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|