devduck 0.1.0__py3-none-any.whl → 0.1.1766644714__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.
Potentially problematic release.
This version of devduck might be problematic. Click here for more details.
- devduck/__init__.py +1439 -483
- devduck/__main__.py +7 -0
- devduck/_version.py +34 -0
- devduck/agentcore_handler.py +76 -0
- devduck/test_redduck.py +0 -1
- devduck/tools/__init__.py +47 -0
- devduck/tools/_ambient_input.py +423 -0
- devduck/tools/_tray_app.py +530 -0
- devduck/tools/agentcore_agents.py +197 -0
- devduck/tools/agentcore_config.py +441 -0
- devduck/tools/agentcore_invoke.py +423 -0
- devduck/tools/agentcore_logs.py +320 -0
- devduck/tools/ambient.py +157 -0
- devduck/tools/create_subagent.py +659 -0
- devduck/tools/fetch_github_tool.py +201 -0
- devduck/tools/install_tools.py +409 -0
- devduck/tools/ipc.py +546 -0
- devduck/tools/mcp_server.py +600 -0
- devduck/tools/scraper.py +935 -0
- devduck/tools/speech_to_speech.py +850 -0
- devduck/tools/state_manager.py +292 -0
- devduck/tools/store_in_kb.py +187 -0
- devduck/tools/system_prompt.py +608 -0
- devduck/tools/tcp.py +263 -94
- devduck/tools/tray.py +247 -0
- devduck/tools/use_github.py +438 -0
- devduck/tools/websocket.py +498 -0
- devduck-0.1.1766644714.dist-info/METADATA +717 -0
- devduck-0.1.1766644714.dist-info/RECORD +33 -0
- {devduck-0.1.0.dist-info → devduck-0.1.1766644714.dist-info}/entry_points.txt +1 -0
- devduck-0.1.1766644714.dist-info/licenses/LICENSE +201 -0
- devduck/install.sh +0 -42
- devduck-0.1.0.dist-info/METADATA +0 -106
- devduck-0.1.0.dist-info/RECORD +0 -11
- devduck-0.1.0.dist-info/licenses/LICENSE +0 -21
- {devduck-0.1.0.dist-info → devduck-0.1.1766644714.dist-info}/WHEEL +0 -0
- {devduck-0.1.0.dist-info → devduck-0.1.1766644714.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""AgentCore Agents Tool - List and manage deployed DevDuck agents."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
from strands import tool
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@tool
|
|
9
|
+
def agentcore_agents(
|
|
10
|
+
action: str = "list",
|
|
11
|
+
agent_id: Optional[str] = None,
|
|
12
|
+
agent_name: Optional[str] = None,
|
|
13
|
+
max_results: int = 100,
|
|
14
|
+
region: str = "us-west-2",
|
|
15
|
+
) -> Dict[str, Any]:
|
|
16
|
+
"""Manage and discover Bedrock AgentCore agent runtimes.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
action: Agent operation (list, get, find_by_name)
|
|
20
|
+
agent_id: Agent runtime ID (for "get" action)
|
|
21
|
+
agent_name: Agent name to search (for "find_by_name")
|
|
22
|
+
max_results: Max results for list (default: 100)
|
|
23
|
+
region: AWS region (default: us-west-2)
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Dict with status and agent information
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
# List all agents
|
|
30
|
+
agentcore_agents(action="list")
|
|
31
|
+
|
|
32
|
+
# Get specific agent
|
|
33
|
+
agentcore_agents(action="get", agent_id="devduck-UrvQvkH6H7")
|
|
34
|
+
|
|
35
|
+
# Find by name
|
|
36
|
+
agentcore_agents(action="find_by_name", agent_name="devduck")
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
import boto3
|
|
40
|
+
from botocore.exceptions import ClientError
|
|
41
|
+
|
|
42
|
+
client = boto3.client("bedrock-agentcore-control", region_name=region)
|
|
43
|
+
|
|
44
|
+
if action == "list":
|
|
45
|
+
all_agents = []
|
|
46
|
+
next_token = None
|
|
47
|
+
|
|
48
|
+
while True:
|
|
49
|
+
params = {"maxResults": min(max_results - len(all_agents), 100)}
|
|
50
|
+
if next_token:
|
|
51
|
+
params["nextToken"] = next_token
|
|
52
|
+
|
|
53
|
+
response = client.list_agent_runtimes(**params)
|
|
54
|
+
agents = response.get("agentRuntimes", [])
|
|
55
|
+
all_agents.extend(agents)
|
|
56
|
+
|
|
57
|
+
if len(all_agents) >= max_results:
|
|
58
|
+
all_agents = all_agents[:max_results]
|
|
59
|
+
break
|
|
60
|
+
|
|
61
|
+
next_token = response.get("nextToken")
|
|
62
|
+
if not next_token:
|
|
63
|
+
break
|
|
64
|
+
|
|
65
|
+
# Format output
|
|
66
|
+
agent_list = []
|
|
67
|
+
for agent in all_agents:
|
|
68
|
+
agent_list.append(
|
|
69
|
+
{
|
|
70
|
+
"name": agent.get("agentRuntimeName"),
|
|
71
|
+
"id": agent.get("agentRuntimeId"),
|
|
72
|
+
"arn": agent.get("agentRuntimeArn"),
|
|
73
|
+
"created": str(agent.get("createdAt")),
|
|
74
|
+
"updated": str(agent.get("lastUpdatedAt")),
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
"status": "success",
|
|
80
|
+
"content": [
|
|
81
|
+
{"text": f"**Found {len(agent_list)} agents:**\n"},
|
|
82
|
+
{"text": json.dumps(agent_list, indent=2)},
|
|
83
|
+
],
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
elif action == "get":
|
|
87
|
+
if not agent_id:
|
|
88
|
+
return {
|
|
89
|
+
"status": "error",
|
|
90
|
+
"content": [{"text": "agent_id required for get action"}],
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
response = client.get_agent_runtime(agentRuntimeId=agent_id)
|
|
94
|
+
|
|
95
|
+
agent_info = {
|
|
96
|
+
"id": response.get("agentRuntimeId"),
|
|
97
|
+
"arn": response.get("agentRuntimeArn"),
|
|
98
|
+
"name": response.get("agentRuntimeName"),
|
|
99
|
+
"status": response.get("status"),
|
|
100
|
+
"roleArn": response.get("roleArn"),
|
|
101
|
+
"created": str(response.get("createdAt")),
|
|
102
|
+
"updated": str(response.get("lastUpdatedAt")),
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Add container info
|
|
106
|
+
if "agentRuntimeArtifact" in response:
|
|
107
|
+
container = response["agentRuntimeArtifact"].get(
|
|
108
|
+
"containerConfiguration", {}
|
|
109
|
+
)
|
|
110
|
+
if container:
|
|
111
|
+
agent_info["containerUri"] = container.get("containerUri")
|
|
112
|
+
|
|
113
|
+
# Add network info
|
|
114
|
+
if "networkConfiguration" in response:
|
|
115
|
+
agent_info["networkMode"] = response["networkConfiguration"].get(
|
|
116
|
+
"networkMode"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
"status": "success",
|
|
121
|
+
"content": [
|
|
122
|
+
{"text": "**Agent Details:**\n"},
|
|
123
|
+
{"text": json.dumps(agent_info, indent=2)},
|
|
124
|
+
],
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
elif action == "find_by_name":
|
|
128
|
+
if not agent_name:
|
|
129
|
+
return {
|
|
130
|
+
"status": "error",
|
|
131
|
+
"content": [
|
|
132
|
+
{"text": "agent_name required for find_by_name action"}
|
|
133
|
+
],
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# List and search
|
|
137
|
+
all_agents = []
|
|
138
|
+
next_token = None
|
|
139
|
+
|
|
140
|
+
while True:
|
|
141
|
+
params = {"maxResults": 100}
|
|
142
|
+
if next_token:
|
|
143
|
+
params["nextToken"] = next_token
|
|
144
|
+
|
|
145
|
+
response = client.list_agent_runtimes(**params)
|
|
146
|
+
agents = response.get("agentRuntimes", [])
|
|
147
|
+
all_agents.extend(agents)
|
|
148
|
+
|
|
149
|
+
next_token = response.get("nextToken")
|
|
150
|
+
if not next_token:
|
|
151
|
+
break
|
|
152
|
+
|
|
153
|
+
# Find match
|
|
154
|
+
matching = None
|
|
155
|
+
for agent in all_agents:
|
|
156
|
+
if agent.get("agentRuntimeName") == agent_name:
|
|
157
|
+
matching = agent
|
|
158
|
+
break
|
|
159
|
+
|
|
160
|
+
if matching:
|
|
161
|
+
return {
|
|
162
|
+
"status": "success",
|
|
163
|
+
"content": [
|
|
164
|
+
{"text": f"✅ **Found: {agent_name}**\n"},
|
|
165
|
+
{"text": json.dumps(matching, indent=2, default=str)},
|
|
166
|
+
],
|
|
167
|
+
}
|
|
168
|
+
else:
|
|
169
|
+
return {
|
|
170
|
+
"status": "error",
|
|
171
|
+
"content": [{"text": f"Agent not found: {agent_name}"}],
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
else:
|
|
175
|
+
return {
|
|
176
|
+
"status": "error",
|
|
177
|
+
"content": [
|
|
178
|
+
{
|
|
179
|
+
"text": f"Unknown action: {action}. Valid: list, get, find_by_name"
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
except ClientError as e:
|
|
185
|
+
error_code = e.response.get("Error", {}).get("Code", "Unknown")
|
|
186
|
+
error_message = e.response.get("Error", {}).get("Message", str(e))
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
"status": "error",
|
|
190
|
+
"content": [
|
|
191
|
+
{"text": f"**AWS Error ({error_code}):** {error_message}"},
|
|
192
|
+
{"text": f"**Region:** {region}"},
|
|
193
|
+
],
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
except Exception as e:
|
|
197
|
+
return {"status": "error", "content": [{"text": f"Error: {str(e)}"}]}
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
"""AgentCore Configuration and Launch Tool"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, Any
|
|
7
|
+
from strands import tool
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@tool
|
|
11
|
+
def agentcore_config(
|
|
12
|
+
action: str,
|
|
13
|
+
agent_name: str = "devduck",
|
|
14
|
+
handler_path: str = None,
|
|
15
|
+
region: str = "us-west-2",
|
|
16
|
+
model_id: str = "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
17
|
+
max_tokens: int = 60000,
|
|
18
|
+
tools: str = None,
|
|
19
|
+
idle_timeout: int = 900,
|
|
20
|
+
max_lifetime: int = 28800,
|
|
21
|
+
auto_launch: bool = False,
|
|
22
|
+
) -> Dict[str, Any]:
|
|
23
|
+
"""
|
|
24
|
+
Configure and launch DevDuck agents on Bedrock AgentCore.
|
|
25
|
+
|
|
26
|
+
**Hardcoded for DevDuck:**
|
|
27
|
+
- deployment_type: "direct_code_deploy" (fast, no Docker)
|
|
28
|
+
- protocol: "HTTP" (standard API)
|
|
29
|
+
- runtime: "PYTHON_3_13" (latest)
|
|
30
|
+
|
|
31
|
+
**Quick Start:**
|
|
32
|
+
```python
|
|
33
|
+
# Configure and launch in one step
|
|
34
|
+
agentcore_config(
|
|
35
|
+
action="configure",
|
|
36
|
+
agent_name="my-agent",
|
|
37
|
+
auto_launch=True
|
|
38
|
+
)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
action: Action to perform (generate, configure, launch, status)
|
|
43
|
+
agent_name: Name for the agent (default: devduck)
|
|
44
|
+
- Hyphens auto-converted to underscores
|
|
45
|
+
- Example: "test-agent" → "test_agent"
|
|
46
|
+
handler_path: Path to handler.py (default: auto-copy from devduck)
|
|
47
|
+
region: AWS region (default: us-west-2)
|
|
48
|
+
model_id: Bedrock model ID (default: Claude Sonnet 4.5)
|
|
49
|
+
max_tokens: Max tokens to generate (default: 60000)
|
|
50
|
+
tools: Tool configuration (package:tool1,tool2)
|
|
51
|
+
Example: "strands_tools:shell,editor,file_read"
|
|
52
|
+
idle_timeout: Idle timeout in seconds (default: 900)
|
|
53
|
+
max_lifetime: Max lifetime in seconds (default: 28800)
|
|
54
|
+
auto_launch: Auto launch after configure (default: False)
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Dict with status and instructions
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
# Configure and launch
|
|
61
|
+
agentcore_config(
|
|
62
|
+
action="configure",
|
|
63
|
+
agent_name="my-agent",
|
|
64
|
+
tools="strands_tools:shell,editor,file_read",
|
|
65
|
+
auto_launch=True
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Check status
|
|
69
|
+
agentcore_config(action="status", agent_name="my-agent")
|
|
70
|
+
|
|
71
|
+
# Generate commands only
|
|
72
|
+
agentcore_config(action="generate", agent_name="my-agent")
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
# Hardcoded values for DevDuck deployment
|
|
76
|
+
protocol = "HTTP"
|
|
77
|
+
deployment_type = "direct_code_deploy"
|
|
78
|
+
runtime = "PYTHON_3_13"
|
|
79
|
+
|
|
80
|
+
# Validate agent name
|
|
81
|
+
import re
|
|
82
|
+
|
|
83
|
+
if not re.match(r"^[a-zA-Z][a-zA-Z0-9_]{0,47}$", agent_name):
|
|
84
|
+
# Auto-fix: replace hyphens with underscores
|
|
85
|
+
fixed_name = agent_name.replace("-", "_")
|
|
86
|
+
if not re.match(r"^[a-zA-Z][a-zA-Z0-9_]{0,47}$", fixed_name):
|
|
87
|
+
return {
|
|
88
|
+
"status": "error",
|
|
89
|
+
"content": [
|
|
90
|
+
{
|
|
91
|
+
"text": f"Invalid agent name: {agent_name}\nMust start with letter, contain only letters/numbers/underscores, 1-48 chars"
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
}
|
|
95
|
+
agent_name = fixed_name
|
|
96
|
+
|
|
97
|
+
# Auto-determine handler path
|
|
98
|
+
if not handler_path:
|
|
99
|
+
try:
|
|
100
|
+
import devduck
|
|
101
|
+
|
|
102
|
+
devduck_dir = Path(devduck.__file__).parent
|
|
103
|
+
source_handler = devduck_dir / "agentcore_handler.py"
|
|
104
|
+
|
|
105
|
+
if source_handler.exists():
|
|
106
|
+
# Copy to current directory with agent-specific name
|
|
107
|
+
local_handler = Path.cwd() / "agentcore_handler.py"
|
|
108
|
+
import shutil
|
|
109
|
+
|
|
110
|
+
shutil.copy(str(source_handler), str(local_handler))
|
|
111
|
+
handler_path = "./agentcore_handler.py"
|
|
112
|
+
|
|
113
|
+
# Create requirements.txt with devduck if it doesn't exist
|
|
114
|
+
requirements_path = Path.cwd() / "requirements.txt"
|
|
115
|
+
if not requirements_path.exists():
|
|
116
|
+
with open(requirements_path, "w") as f:
|
|
117
|
+
f.write("devduck\n")
|
|
118
|
+
else:
|
|
119
|
+
return {
|
|
120
|
+
"status": "error",
|
|
121
|
+
"content": [{"text": f"Handler not found: {source_handler}"}],
|
|
122
|
+
}
|
|
123
|
+
except Exception as e:
|
|
124
|
+
return {
|
|
125
|
+
"status": "error",
|
|
126
|
+
"content": [{"text": f"Failed to locate handler: {e}"}],
|
|
127
|
+
}
|
|
128
|
+
else:
|
|
129
|
+
# Ensure handler_path is local
|
|
130
|
+
handler_file = Path(handler_path)
|
|
131
|
+
if not handler_file.is_absolute():
|
|
132
|
+
# Already relative, make sure it starts with ./
|
|
133
|
+
if not handler_path.startswith("./"):
|
|
134
|
+
handler_path = f"./{handler_path}"
|
|
135
|
+
else:
|
|
136
|
+
# Absolute path - copy to current directory
|
|
137
|
+
local_handler = Path.cwd() / handler_file.name
|
|
138
|
+
import shutil
|
|
139
|
+
|
|
140
|
+
shutil.copy(str(handler_file), str(local_handler))
|
|
141
|
+
handler_path = f"./{handler_file.name}"
|
|
142
|
+
|
|
143
|
+
if action == "generate":
|
|
144
|
+
# Generate configuration commands
|
|
145
|
+
commands = f"""
|
|
146
|
+
# DevDuck AgentCore Configuration for: {agent_name}
|
|
147
|
+
|
|
148
|
+
# Step 1: Configure
|
|
149
|
+
agentcore configure \\
|
|
150
|
+
-e {handler_path} \\
|
|
151
|
+
-n {agent_name} \\
|
|
152
|
+
-r {region} \\
|
|
153
|
+
-p {protocol} \\
|
|
154
|
+
-dt {deployment_type} \\
|
|
155
|
+
-rt {runtime} \\
|
|
156
|
+
--idle-timeout {idle_timeout} \\
|
|
157
|
+
--max-lifetime {max_lifetime}
|
|
158
|
+
|
|
159
|
+
# Step 2: Launch
|
|
160
|
+
agentcore launch -a {agent_name} --auto-update-on-conflict
|
|
161
|
+
|
|
162
|
+
# Step 3: Check Status
|
|
163
|
+
agentcore status -a {agent_name}
|
|
164
|
+
|
|
165
|
+
# Step 4: Invoke
|
|
166
|
+
agentcore invoke {agent_name} "What are your capabilities?"
|
|
167
|
+
|
|
168
|
+
# Python Invocation:
|
|
169
|
+
from devduck import devduck
|
|
170
|
+
devduck.agent.tool.agentcore_invoke(
|
|
171
|
+
prompt="test query",
|
|
172
|
+
agent_name="{agent_name}"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Environment Variables for Handler:
|
|
176
|
+
export MODEL_PROVIDER=bedrock
|
|
177
|
+
export STRANDS_PROVIDER=bedrock
|
|
178
|
+
export STRANDS_MODEL_ID={model_id}
|
|
179
|
+
export STRANDS_MAX_TOKENS={max_tokens}
|
|
180
|
+
export AWS_REGION={region}
|
|
181
|
+
export DEVDUCK_AUTO_START_SERVERS=false
|
|
182
|
+
"""
|
|
183
|
+
if tools:
|
|
184
|
+
commands += f"export DEVDUCK_TOOLS={tools}\n"
|
|
185
|
+
else:
|
|
186
|
+
commands += "# export DEVDUCK_TOOLS=strands_tools:shell,editor,file_read # Limit tools for sub-agents\n"
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
"status": "success",
|
|
190
|
+
"content": [{"text": f"Generated configuration:\n{commands}"}],
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
elif action == "configure":
|
|
194
|
+
# Run agentcore configure command with automated inputs
|
|
195
|
+
try:
|
|
196
|
+
# Set environment variables for handler
|
|
197
|
+
env = os.environ.copy()
|
|
198
|
+
env["DEVDUCK_AUTO_START_SERVERS"] = "false"
|
|
199
|
+
if tools:
|
|
200
|
+
env["DEVDUCK_TOOLS"] = tools
|
|
201
|
+
|
|
202
|
+
cmd = [
|
|
203
|
+
"agentcore",
|
|
204
|
+
"configure",
|
|
205
|
+
"-e",
|
|
206
|
+
handler_path,
|
|
207
|
+
"-n",
|
|
208
|
+
agent_name,
|
|
209
|
+
"-r",
|
|
210
|
+
region,
|
|
211
|
+
"-p",
|
|
212
|
+
protocol,
|
|
213
|
+
"-dt",
|
|
214
|
+
deployment_type,
|
|
215
|
+
"-rt",
|
|
216
|
+
runtime,
|
|
217
|
+
"--idle-timeout",
|
|
218
|
+
str(idle_timeout),
|
|
219
|
+
"--max-lifetime",
|
|
220
|
+
str(max_lifetime),
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
# Automated inputs for interactive prompts:
|
|
224
|
+
# 1. Requirements file path (Enter)
|
|
225
|
+
# 2. Execution role (Enter for auto-create)
|
|
226
|
+
# 3. S3 bucket (Enter for auto-create)
|
|
227
|
+
# 4. OAuth config (no/Enter)
|
|
228
|
+
# 5. Request headers (no/Enter)
|
|
229
|
+
# 6. Memory selection (s to skip)
|
|
230
|
+
automated_inputs = "\n\n\n\n\ns\n"
|
|
231
|
+
|
|
232
|
+
process = subprocess.Popen(
|
|
233
|
+
cmd,
|
|
234
|
+
stdin=subprocess.PIPE,
|
|
235
|
+
stdout=subprocess.PIPE,
|
|
236
|
+
stderr=subprocess.PIPE,
|
|
237
|
+
text=True,
|
|
238
|
+
env=env,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
stdout, stderr = process.communicate(input=automated_inputs, timeout=300)
|
|
242
|
+
|
|
243
|
+
if process.returncode != 0:
|
|
244
|
+
return {
|
|
245
|
+
"status": "error",
|
|
246
|
+
"content": [{"text": f"Configure failed:\n{stderr}\n{stdout}"}],
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
output = f"✅ Configured: {agent_name}\n{stdout}"
|
|
250
|
+
|
|
251
|
+
# Auto-launch if requested
|
|
252
|
+
agent_arn = None
|
|
253
|
+
agent_id = None
|
|
254
|
+
|
|
255
|
+
if auto_launch:
|
|
256
|
+
launch_cmd = [
|
|
257
|
+
"agentcore",
|
|
258
|
+
"launch",
|
|
259
|
+
"-a",
|
|
260
|
+
agent_name,
|
|
261
|
+
"--auto-update-on-conflict",
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
output += f"\n\n🚀 Launching {agent_name}...\n"
|
|
265
|
+
output += "=" * 50 + "\n"
|
|
266
|
+
|
|
267
|
+
# Stream output to BOTH terminal AND agent
|
|
268
|
+
import sys
|
|
269
|
+
import re
|
|
270
|
+
|
|
271
|
+
launch_process = subprocess.Popen(
|
|
272
|
+
launch_cmd,
|
|
273
|
+
stdin=subprocess.PIPE,
|
|
274
|
+
stdout=subprocess.PIPE,
|
|
275
|
+
stderr=subprocess.STDOUT,
|
|
276
|
+
text=True,
|
|
277
|
+
bufsize=1,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# Send automated inputs
|
|
281
|
+
launch_inputs = "\n\n\n"
|
|
282
|
+
launch_process.stdin.write(launch_inputs)
|
|
283
|
+
launch_process.stdin.close()
|
|
284
|
+
|
|
285
|
+
# Stream output line by line
|
|
286
|
+
launch_output = []
|
|
287
|
+
try:
|
|
288
|
+
for line in launch_process.stdout:
|
|
289
|
+
print(line, end="", flush=True) # Terminal
|
|
290
|
+
launch_output.append(line) # Agent
|
|
291
|
+
|
|
292
|
+
launch_process.wait(timeout=600)
|
|
293
|
+
|
|
294
|
+
full_output = "".join(launch_output)
|
|
295
|
+
if launch_process.returncode != 0:
|
|
296
|
+
output += f"\n❌ Launch failed with code {launch_process.returncode}\n{full_output}"
|
|
297
|
+
else:
|
|
298
|
+
output += f"\n✅ Launched: {agent_name}\n{full_output}"
|
|
299
|
+
|
|
300
|
+
# Extract ARN after streaming is complete
|
|
301
|
+
arn_match = re.search(
|
|
302
|
+
r"arn:aws:bedrock-agentcore:[^:]+:[^:]+:runtime/([^\s\n]+)",
|
|
303
|
+
full_output,
|
|
304
|
+
)
|
|
305
|
+
if arn_match:
|
|
306
|
+
agent_arn = arn_match.group(0)
|
|
307
|
+
agent_id = arn_match.group(1)
|
|
308
|
+
except subprocess.TimeoutExpired:
|
|
309
|
+
launch_process.kill()
|
|
310
|
+
output += f"\n❌ Launch timed out"
|
|
311
|
+
|
|
312
|
+
# Return structured response with agent_id for direct invocation
|
|
313
|
+
result = {"status": "success", "content": [{"text": output}]}
|
|
314
|
+
|
|
315
|
+
# Add structured metadata in content for easy access
|
|
316
|
+
if agent_arn and agent_id:
|
|
317
|
+
result["content"].append({"text": f"\n📋 **Agent ARN:** {agent_arn}"})
|
|
318
|
+
result["content"].append({"text": f"🆔 **Agent ID:** {agent_id}"})
|
|
319
|
+
result["content"].append(
|
|
320
|
+
{
|
|
321
|
+
"text": f"\n💡 **Invoke directly:** agentcore_invoke(agent_id='{agent_id}', prompt='your query')"
|
|
322
|
+
}
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return result
|
|
326
|
+
|
|
327
|
+
except FileNotFoundError:
|
|
328
|
+
return {
|
|
329
|
+
"status": "error",
|
|
330
|
+
"content": [
|
|
331
|
+
{
|
|
332
|
+
"text": "agentcore CLI not found. Install: pip install bedrock-agentcore-starter-toolkit"
|
|
333
|
+
}
|
|
334
|
+
],
|
|
335
|
+
}
|
|
336
|
+
except subprocess.TimeoutExpired:
|
|
337
|
+
return {
|
|
338
|
+
"status": "error",
|
|
339
|
+
"content": [{"text": f"Configuration timed out for {agent_name}"}],
|
|
340
|
+
}
|
|
341
|
+
except Exception as e:
|
|
342
|
+
return {
|
|
343
|
+
"status": "error",
|
|
344
|
+
"content": [{"text": f"Configuration error: {e}"}],
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
elif action == "launch":
|
|
348
|
+
# Run agentcore launch command with streaming output
|
|
349
|
+
try:
|
|
350
|
+
import sys
|
|
351
|
+
import re
|
|
352
|
+
|
|
353
|
+
cmd = ["agentcore", "launch", "-a", agent_name, "--auto-update-on-conflict"]
|
|
354
|
+
|
|
355
|
+
print(f"🚀 Launching {agent_name}...")
|
|
356
|
+
print("=" * 50)
|
|
357
|
+
|
|
358
|
+
# Stream to BOTH terminal AND agent
|
|
359
|
+
process = subprocess.Popen(
|
|
360
|
+
cmd,
|
|
361
|
+
stdout=subprocess.PIPE,
|
|
362
|
+
stderr=subprocess.STDOUT,
|
|
363
|
+
text=True,
|
|
364
|
+
bufsize=1,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
# Capture output
|
|
368
|
+
output_lines = []
|
|
369
|
+
for line in process.stdout:
|
|
370
|
+
print(line, end="", flush=True) # Terminal
|
|
371
|
+
output_lines.append(line) # Agent
|
|
372
|
+
|
|
373
|
+
process.wait()
|
|
374
|
+
full_output = "".join(output_lines)
|
|
375
|
+
|
|
376
|
+
# Extract ARN after streaming is complete
|
|
377
|
+
agent_arn = None
|
|
378
|
+
agent_id = None
|
|
379
|
+
arn_match = re.search(
|
|
380
|
+
r"arn:aws:bedrock-agentcore:[^:]+:[^:]+:runtime/([^\s\n]+)", full_output
|
|
381
|
+
)
|
|
382
|
+
if arn_match:
|
|
383
|
+
agent_arn = arn_match.group(0)
|
|
384
|
+
agent_id = arn_match.group(1)
|
|
385
|
+
|
|
386
|
+
if process.returncode != 0:
|
|
387
|
+
return {
|
|
388
|
+
"status": "error",
|
|
389
|
+
"content": [
|
|
390
|
+
{
|
|
391
|
+
"text": f"❌ Launch failed with code {process.returncode}\n{full_output}"
|
|
392
|
+
}
|
|
393
|
+
],
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
result = {
|
|
397
|
+
"status": "success",
|
|
398
|
+
"content": [{"text": f"✅ Launched: {agent_name}\n{full_output}"}],
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
# Add structured metadata in content for easy access
|
|
402
|
+
if agent_arn and agent_id:
|
|
403
|
+
result["content"].append({"text": f"\n📋 **Agent ARN:** {agent_arn}"})
|
|
404
|
+
result["content"].append({"text": f"🆔 **Agent ID:** {agent_id}"})
|
|
405
|
+
result["content"].append(
|
|
406
|
+
{
|
|
407
|
+
"text": f"\n💡 **Invoke directly:** agentcore_invoke(agent_id='{agent_id}', prompt='your query')"
|
|
408
|
+
}
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
return result
|
|
412
|
+
|
|
413
|
+
except Exception as e:
|
|
414
|
+
return {"status": "error", "content": [{"text": f"Launch error: {e}"}]}
|
|
415
|
+
|
|
416
|
+
elif action == "status":
|
|
417
|
+
# Check agent status
|
|
418
|
+
try:
|
|
419
|
+
cmd = ["agentcore", "status", "-a", agent_name]
|
|
420
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
"status": "success",
|
|
424
|
+
"content": [{"text": f"Status for {agent_name}:\n{result.stdout}"}],
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
except Exception as e:
|
|
428
|
+
return {
|
|
429
|
+
"status": "error",
|
|
430
|
+
"content": [{"text": f"Status check error: {e}"}],
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
else:
|
|
434
|
+
return {
|
|
435
|
+
"status": "error",
|
|
436
|
+
"content": [
|
|
437
|
+
{
|
|
438
|
+
"text": f"Unknown action: {action}. Use: generate, configure, launch, status"
|
|
439
|
+
}
|
|
440
|
+
],
|
|
441
|
+
}
|