Add native boto3 AWS tools, remove broken AWS MCP client
This commit is contained in:
@@ -63,25 +63,6 @@ class _SigV4HttpxAuth(httpx.Auth):
|
|||||||
if self._actor_id:
|
if self._actor_id:
|
||||||
request.headers['x-actor-id'] = self._actor_id
|
request.headers['x-actor-id'] = self._actor_id
|
||||||
yield request
|
yield request
|
||||||
class _AwsMcpSigV4Auth(httpx.Auth):
|
|
||||||
"""SigV4 auth for AWS MCP Server (service: aws-mcp)."""
|
|
||||||
def auth_flow(self, request):
|
|
||||||
creds = boto3.Session().get_credentials().get_frozen_credentials()
|
|
||||||
parsed = _urlparse(str(request.url))
|
|
||||||
aws_req = botocore.awsrequest.AWSRequest(
|
|
||||||
method=request.method,
|
|
||||||
url=str(request.url),
|
|
||||||
data=request.content or b'',
|
|
||||||
headers={
|
|
||||||
'Host': parsed.hostname,
|
|
||||||
'Content-Type': request.headers.get('content-type', 'application/json'),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
botocore.auth.SigV4Auth(creds, 'aws-mcp', 'us-east-1').add_auth(aws_req)
|
|
||||||
for k, v in aws_req.headers.items():
|
|
||||||
request.headers[k] = v
|
|
||||||
yield request
|
|
||||||
|
|
||||||
|
|
||||||
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig
|
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig
|
||||||
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
|
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
|
||||||
@@ -334,6 +315,63 @@ def apply_iam_permission(action: str, resource: str, policy_name: str) -> str:
|
|||||||
return f"Applied policy '{policy_name}': Allow {action} on {resource}."
|
return f"Applied policy '{policy_name}': Allow {action} on {resource}."
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def aws_list_lambda_functions(region: str = "us-east-1") -> str:
|
||||||
|
"""List AWS Lambda functions in the specified region. Uses execution role credentials directly via boto3."""
|
||||||
|
import boto3
|
||||||
|
client = boto3.client("lambda", region_name=region)
|
||||||
|
paginator = client.get_paginator("list_functions")
|
||||||
|
functions = []
|
||||||
|
for page in paginator.paginate():
|
||||||
|
for fn in page["Functions"]:
|
||||||
|
functions.append(f"{fn['FunctionName']} ({fn['Runtime']})")
|
||||||
|
return f"{len(functions)} Lambda functions in {region}:\n" + "\n".join(functions)
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def aws_get_cost_and_usage(start_date: str, end_date: str, granularity: str = "MONTHLY") -> str:
|
||||||
|
"""Get AWS Cost and Usage report. start_date and end_date in YYYY-MM-DD format. Uses execution role credentials."""
|
||||||
|
import boto3
|
||||||
|
client = boto3.client("ce", region_name="us-east-1")
|
||||||
|
response = client.get_cost_and_usage(
|
||||||
|
TimePeriod={"Start": start_date, "End": end_date},
|
||||||
|
Granularity=granularity,
|
||||||
|
Metrics=["UnblendedCost"]
|
||||||
|
)
|
||||||
|
lines = []
|
||||||
|
for result in response["ResultsByTime"]:
|
||||||
|
period = f"{result['TimePeriod']['Start']} to {result['TimePeriod']['End']}"
|
||||||
|
cost = result["Total"]["UnblendedCost"]["Amount"]
|
||||||
|
unit = result["Total"]["UnblendedCost"]["Unit"]
|
||||||
|
lines.append(f"{period}: {cost} {unit}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def aws_describe_service(service: str, region: str = "us-east-1") -> str:
|
||||||
|
"""Describe an AWS service. service can be: lambda, s3, cloudformation, dynamodb, sqs. Returns summary of key resources."""
|
||||||
|
import boto3
|
||||||
|
session = boto3.Session(region_name=region)
|
||||||
|
if service == "s3":
|
||||||
|
client = session.client("s3")
|
||||||
|
buckets = client.list_buckets()["Buckets"]
|
||||||
|
return f"{len(buckets)} S3 buckets: " + ", ".join(b["Name"] for b in buckets[:20])
|
||||||
|
elif service == "cloudformation":
|
||||||
|
client = session.client("cloudformation")
|
||||||
|
stacks = client.list_stacks(StackStatusFilter=["CREATE_COMPLETE", "UPDATE_COMPLETE", "ROLLBACK_COMPLETE"])["StackSummaries"]
|
||||||
|
return f"{len(stacks)} stacks: " + ", ".join(s["StackName"] for s in stacks[:20])
|
||||||
|
elif service == "dynamodb":
|
||||||
|
client = session.client("dynamodb")
|
||||||
|
tables = client.list_tables()["TableNames"]
|
||||||
|
return f"{len(tables)} DynamoDB tables: " + ", ".join(tables[:20])
|
||||||
|
elif service == "sqs":
|
||||||
|
client = session.client("sqs")
|
||||||
|
queues = client.list_queues().get("QueueUrls", [])
|
||||||
|
return f"{len(queues)} SQS queues: " + ", ".join(q.split("/")[-1] for q in queues[:20])
|
||||||
|
else:
|
||||||
|
return f"Service {service} not yet implemented. Try: lambda, s3, cloudformation, dynamodb, sqs"
|
||||||
|
|
||||||
|
|
||||||
# ── Entrypoint ────────────────────────────────────────────────────────────
|
# ── Entrypoint ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# Module-level actor_id for tool closures (set per-invocation)
|
# Module-level actor_id for tool closures (set per-invocation)
|
||||||
@@ -443,6 +481,7 @@ async def main(payload: dict, context):
|
|||||||
_now = datetime.now(_tz)
|
_now = datetime.now(_tz)
|
||||||
_time_str = _now.strftime('%A, %B %d, %Y %I:%M %p %Z')
|
_time_str = _now.strftime('%A, %B %d, %Y %I:%M %p %Z')
|
||||||
system_prompt = system_prompt + f'\n\nCurrent date/time: {_time_str}'
|
system_prompt = system_prompt + f'\n\nCurrent date/time: {_time_str}'
|
||||||
|
system_prompt = system_prompt + '\n\nNative AWS tools available: aws_list_lambda_functions, aws_get_cost_and_usage, aws_describe_service. Use these directly for AWS API calls — NEVER use run_code for AWS queries.'
|
||||||
print(f'[main] System prompt time injection: {_time_str}')
|
print(f'[main] System prompt time injection: {_time_str}')
|
||||||
|
|
||||||
# Model: claude-sonnet-4-6 via cross-region inference
|
# Model: claude-sonnet-4-6 via cross-region inference
|
||||||
@@ -458,29 +497,14 @@ async def main(payload: dict, context):
|
|||||||
home_assistant, connect_google_account, list_google_accounts, remove_google_account,
|
home_assistant, connect_google_account, list_google_accounts, remove_google_account,
|
||||||
manage_service, manage_mcp_connection, schedule_reminder, list_reminders, cancel_reminder,
|
manage_service, manage_mcp_connection, schedule_reminder, list_reminders, cancel_reminder,
|
||||||
list_calendars, get_calendar_events, list_gmail_messages, get_gmail_message,
|
list_calendars, get_calendar_events, list_gmail_messages, get_gmail_message,
|
||||||
run_code, send_file, request_iam_permission, apply_iam_permission]
|
run_code, send_file, request_iam_permission, apply_iam_permission,
|
||||||
|
aws_list_lambda_functions, aws_get_cost_and_usage, aws_describe_service]
|
||||||
|
|
||||||
# Load user's dynamic MCP connections
|
# Load user's dynamic MCP connections
|
||||||
mcp_connections = services.get('mcp_connections', [])
|
mcp_connections = services.get('mcp_connections', [])
|
||||||
mcp_clients, _mcp_to_close = mcp_loader.load_mcp_tools(mcp_connections, actor_id)
|
mcp_clients, _mcp_to_close = mcp_loader.load_mcp_tools(mcp_connections, actor_id)
|
||||||
|
|
||||||
# AWS MCP Server connection (system-level, SigV4 auth)
|
all_tools = base_tools + mcp_clients
|
||||||
_aws_mcp_client = None
|
|
||||||
_aws_mcp_tools = []
|
|
||||||
try:
|
|
||||||
from strands.tools.mcp import MCPClient
|
|
||||||
from mcp.client.streamable_http import streamablehttp_client
|
|
||||||
_aws_mcp_client = MCPClient(
|
|
||||||
lambda: streamablehttp_client(config.AWS_MCP_URL, auth=_AwsMcpSigV4Auth())
|
|
||||||
)
|
|
||||||
_aws_mcp_client.start()
|
|
||||||
_aws_mcp_tools = [_aws_mcp_client]
|
|
||||||
except Exception as _e:
|
|
||||||
import traceback
|
|
||||||
print(f"[main] AWS MCP client failed to start: {type(_e).__name__}: {_e}")
|
|
||||||
print(traceback.format_exc())
|
|
||||||
|
|
||||||
all_tools = base_tools + mcp_clients + _aws_mcp_tools
|
|
||||||
|
|
||||||
agent = Agent(
|
agent = Agent(
|
||||||
model=model,
|
model=model,
|
||||||
@@ -505,11 +529,6 @@ async def main(payload: dict, context):
|
|||||||
_typing_active = False
|
_typing_active = False
|
||||||
session_manager.close()
|
session_manager.close()
|
||||||
mcp_loader.close_mcp_clients(_mcp_to_close)
|
mcp_loader.close_mcp_clients(_mcp_to_close)
|
||||||
if _aws_mcp_client:
|
|
||||||
try:
|
|
||||||
_aws_mcp_client.stop()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
# Check if session exceeds window — flag for compaction on next invocation
|
# Check if session exceeds window — flag for compaction on next invocation
|
||||||
memory_manager.check_window_and_flag(actor_id, session_id)
|
memory_manager.check_window_and_flag(actor_id, session_id)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,91 +1,46 @@
|
|||||||
{
|
{
|
||||||
"version": "53.0.0",
|
"version": "53.0.0",
|
||||||
"files": {
|
"files": {
|
||||||
"e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d": {
|
"e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2": {
|
||||||
"displayName": "WorkspaceFiles/AwsCliLayer/Code",
|
|
||||||
"source": {
|
|
||||||
"path": "asset.e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip",
|
|
||||||
"packaging": "file"
|
|
||||||
},
|
|
||||||
"destinations": {
|
|
||||||
"495395224548-us-east-1-b19c5879": {
|
|
||||||
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
|
||||||
"objectKey": "e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9": {
|
|
||||||
"displayName": "Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code",
|
|
||||||
"source": {
|
|
||||||
"path": "asset.3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9",
|
|
||||||
"packaging": "zip"
|
|
||||||
},
|
|
||||||
"destinations": {
|
|
||||||
"495395224548-us-east-1-12f29a1a": {
|
|
||||||
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
|
||||||
"objectKey": "3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9.zip",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"0feea8d997b96e31a1bd7dd049faf8ee17babeb6d2f5b663ba7e3a70387302e0": {
|
|
||||||
"displayName": "WorkspaceFiles/Asset1",
|
|
||||||
"source": {
|
|
||||||
"path": "asset.0feea8d997b96e31a1bd7dd049faf8ee17babeb6d2f5b663ba7e3a70387302e0",
|
|
||||||
"packaging": "zip"
|
|
||||||
},
|
|
||||||
"destinations": {
|
|
||||||
"495395224548-us-east-1-2e3561b9": {
|
|
||||||
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
|
||||||
"objectKey": "0feea8d997b96e31a1bd7dd049faf8ee17babeb6d2f5b663ba7e3a70387302e0.zip",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"f4461651bfa7d2822e3f36525ace7882e1610dcdaf85e052e1907241e25491d6": {
|
|
||||||
"displayName": "TgIngest/Code",
|
"displayName": "TgIngest/Code",
|
||||||
"source": {
|
"source": {
|
||||||
"path": "asset.f4461651bfa7d2822e3f36525ace7882e1610dcdaf85e052e1907241e25491d6",
|
"path": "asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2",
|
||||||
"packaging": "zip"
|
"packaging": "zip"
|
||||||
},
|
},
|
||||||
"destinations": {
|
"destinations": {
|
||||||
"495395224548-us-east-1-2abe2e26": {
|
"495395224548-us-east-1-351c433c": {
|
||||||
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"objectKey": "f4461651bfa7d2822e3f36525ace7882e1610dcdaf85e052e1907241e25491d6.zip",
|
"objectKey": "e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2.zip",
|
||||||
"region": "us-east-1",
|
"region": "us-east-1",
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"e8d92532d2cb081ba122764c803acc80aaa41350d3497665468ca165dd5ff799": {
|
"59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848": {
|
||||||
"displayName": "AgentRunner/Code",
|
"displayName": "AgentRunner/Code",
|
||||||
"source": {
|
"source": {
|
||||||
"path": "asset.e8d92532d2cb081ba122764c803acc80aaa41350d3497665468ca165dd5ff799",
|
"path": "asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848",
|
||||||
"packaging": "zip"
|
"packaging": "zip"
|
||||||
},
|
},
|
||||||
"destinations": {
|
"destinations": {
|
||||||
"495395224548-us-east-1-7c682050": {
|
"495395224548-us-east-1-16e7a6a4": {
|
||||||
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"objectKey": "e8d92532d2cb081ba122764c803acc80aaa41350d3497665468ca165dd5ff799.zip",
|
"objectKey": "59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848.zip",
|
||||||
"region": "us-east-1",
|
"region": "us-east-1",
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"99aabce70089266e2352cb313d55ee18b849e39c418e8e9cd25dea8c4bf85fc4": {
|
"6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e": {
|
||||||
"displayName": "OAuthHandler/Code",
|
"displayName": "OAuthHandler/Code",
|
||||||
"source": {
|
"source": {
|
||||||
"path": "asset.99aabce70089266e2352cb313d55ee18b849e39c418e8e9cd25dea8c4bf85fc4",
|
"path": "asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e",
|
||||||
"packaging": "zip"
|
"packaging": "zip"
|
||||||
},
|
},
|
||||||
"destinations": {
|
"destinations": {
|
||||||
"495395224548-us-east-1-793899ae": {
|
"495395224548-us-east-1-fffb41e6": {
|
||||||
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"objectKey": "99aabce70089266e2352cb313d55ee18b849e39c418e8e9cd25dea8c4bf85fc4.zip",
|
"objectKey": "6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e.zip",
|
||||||
"region": "us-east-1",
|
"region": "us-east-1",
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
||||||
}
|
}
|
||||||
@@ -106,31 +61,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"8e7324457a5952eb51f04a34fbc5ba853252e7157d8d8958ac5fda92e72edb1f": {
|
"1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b": {
|
||||||
"displayName": "Scheduler/Code",
|
"displayName": "Scheduler/Code",
|
||||||
"source": {
|
"source": {
|
||||||
"path": "asset.8e7324457a5952eb51f04a34fbc5ba853252e7157d8d8958ac5fda92e72edb1f",
|
"path": "asset.1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b",
|
||||||
"packaging": "zip"
|
"packaging": "zip"
|
||||||
},
|
},
|
||||||
"destinations": {
|
"destinations": {
|
||||||
"495395224548-us-east-1-89bca2fb": {
|
"495395224548-us-east-1-e6bab83a": {
|
||||||
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"objectKey": "8e7324457a5952eb51f04a34fbc5ba853252e7157d8d8958ac5fda92e72edb1f.zip",
|
"objectKey": "1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b.zip",
|
||||||
"region": "us-east-1",
|
"region": "us-east-1",
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"78c322849179468ec994f5c3e550a0db4961592ea962b9cf484463e5f04b5a70": {
|
"9c45c012ea9c045aa771b1c3049eadcb15fe66ca16d02e617d50ee9745fa967a": {
|
||||||
"displayName": "AgentClawStack Template",
|
"displayName": "AgentClawStack Template",
|
||||||
"source": {
|
"source": {
|
||||||
"path": "AgentClawStack.template.json",
|
"path": "AgentClawStack.template.json",
|
||||||
"packaging": "file"
|
"packaging": "file"
|
||||||
},
|
},
|
||||||
"destinations": {
|
"destinations": {
|
||||||
"495395224548-us-east-1-02b0125a": {
|
"495395224548-us-east-1-0ef056b9": {
|
||||||
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"bucketName": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"objectKey": "78c322849179468ec994f5c3e550a0db4961592ea962b9cf484463e5f04b5a70.json",
|
"objectKey": "9c45c012ea9c045aa771b1c3049eadcb15fe66ca16d02e617d50ee9745fa967a.json",
|
||||||
"region": "us-east-1",
|
"region": "us-east-1",
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-file-publishing-role-495395224548-us-east-1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"/AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C": [
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:is-custom-resource-handler-singleton",
|
|
||||||
"data": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:is-custom-resource-handler-runtime-family",
|
|
||||||
"data": 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"/AgentClawStack/SessionStore": [
|
"/AgentClawStack/SessionStore": [
|
||||||
{
|
{
|
||||||
"type": "aws:cdk:hasPhysicalName",
|
"type": "aws:cdk:hasPhysicalName",
|
||||||
@@ -42,7 +32,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:378:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:421:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -56,7 +46,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:382:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:425:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -70,7 +60,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:386:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:429:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -84,7 +74,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:391:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:434:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -98,7 +88,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:396:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:439:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -112,7 +102,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:401:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:444:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -126,7 +116,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:406:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:449:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -140,7 +130,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:411:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:454:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -154,7 +144,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:416:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:459:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -168,7 +158,7 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:421:5)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:464:5)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -200,36 +190,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"/AgentClawStack/WorkspaceBucket/Resource": [
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:logicalId",
|
|
||||||
"data": "WorkspaceBucket53E30B92"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:creationStack",
|
|
||||||
"data": [
|
|
||||||
"...new Bucket2 in aws-cdk-lib...",
|
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:46:9)",
|
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"/AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource": [
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:logicalId",
|
|
||||||
"data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:creationStack",
|
|
||||||
"data": [
|
|
||||||
"...new BucketDeployment2 in aws-cdk-lib...",
|
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:55:7)",
|
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"/AgentClawStack/SessionStore/Resource": [
|
"/AgentClawStack/SessionStore/Resource": [
|
||||||
{
|
{
|
||||||
"type": "aws:cdk:logicalId",
|
"type": "aws:cdk:logicalId",
|
||||||
@@ -239,7 +199,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Table2 in aws-cdk-lib...",
|
"...new Table2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:62:26)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:71:26)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -254,7 +214,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Table2 in aws-cdk-lib...",
|
"...new Table2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:71:24)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:80:24)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -269,7 +229,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Queue2 in aws-cdk-lib...",
|
"...new Queue2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:79:26)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:88:26)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -284,7 +244,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:88:24)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:97:24)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -299,7 +259,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:105:27)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:116:27)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -314,7 +274,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new HttpApi2 in aws-cdk-lib...",
|
"...new HttpApi2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:143:21)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:153:21)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -329,7 +289,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Role2 in aws-cdk-lib...",
|
"...new Role2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:161:26)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:171:26)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -344,7 +304,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:243:28)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:248:28)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -359,7 +319,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:314:31)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:312:31)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -374,7 +334,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Rule2 in aws-cdk-lib...",
|
"...new Rule2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:330:27)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:328:27)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -389,7 +349,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:334:19)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:332:19)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -404,7 +364,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:337:25)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:335:25)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -419,7 +379,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...WrappedClass.addPermission in aws-cdk-lib...",
|
"...WrappedClass.addPermission in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:350:17)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:348:17)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -438,51 +398,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"/AgentClawStack/WorkspaceFiles/AwsCliLayer/Resource": [
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:logicalId",
|
|
||||||
"data": "WorkspaceFilesAwsCliLayer50B6E9D8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:creationStack",
|
|
||||||
"data": [
|
|
||||||
"...new BucketDeployment2 in aws-cdk-lib...",
|
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:55:7)",
|
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"/AgentClawStack/WorkspaceFiles/CustomResource/Default": [
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:logicalId",
|
|
||||||
"data": "WorkspaceFilesCustomResourceA7FC771F"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:creationStack",
|
|
||||||
"data": [
|
|
||||||
"...new BucketDeployment2 in aws-cdk-lib...",
|
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:55:7)",
|
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"/AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource": [
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:logicalId",
|
|
||||||
"data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:creationStack",
|
|
||||||
"data": [
|
|
||||||
"...new BucketDeployment2 in aws-cdk-lib...",
|
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:55:7)",
|
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"/AgentClawStack/TgIngest/ServiceRole/Resource": [
|
"/AgentClawStack/TgIngest/ServiceRole/Resource": [
|
||||||
{
|
{
|
||||||
"type": "aws:cdk:logicalId",
|
"type": "aws:cdk:logicalId",
|
||||||
@@ -492,7 +407,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:88:24)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:97:24)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -507,7 +422,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:105:27)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:116:27)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -522,7 +437,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...WrappedClass.addEventSource in aws-cdk-lib...",
|
"...WrappedClass.addEventSource in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:137:19)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:147:19)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -537,7 +452,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new HttpApi2 in aws-cdk-lib...",
|
"...new HttpApi2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:143:21)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:153:21)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -554,7 +469,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:147:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:157:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -571,7 +486,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:147:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:157:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -588,7 +503,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:279:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:277:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -605,7 +520,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:279:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:277:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -622,7 +537,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:286:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:284:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -639,7 +554,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:286:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:284:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -656,7 +571,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:295:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:293:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -673,7 +588,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:295:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:293:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -688,7 +603,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:165:18)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:175:18)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -702,8 +617,8 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...environmentFromArn.grantRead in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:201:29)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:207:45)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -718,7 +633,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:243:28)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:248:28)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -733,7 +648,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:314:31)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:312:31)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -748,24 +663,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...new Function2 in aws-cdk-lib...",
|
"...new Function2 in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:337:25)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:335:25)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"/AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource": [
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:logicalId",
|
|
||||||
"data": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "aws:cdk:creationStack",
|
|
||||||
"data": [
|
|
||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.js:1:71 in aws-cdk-lib...",
|
|
||||||
"Array.map (:)",
|
|
||||||
"...new BucketDeployment2 in aws-cdk-lib...",
|
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:55:7)",
|
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -780,7 +678,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...WrappedClass.grantSendMessages in aws-cdk-lib...",
|
"...WrappedClass.grantSendMessages in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:101:18)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:111:18)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -795,7 +693,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...WrappedClass.grantReadWriteData in aws-cdk-lib...",
|
"...WrappedClass.grantReadWriteData in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:122:18)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:133:18)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -812,7 +710,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:147:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:157:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -829,7 +727,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:279:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:277:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -846,7 +744,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:286:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:284:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -863,7 +761,7 @@
|
|||||||
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
".../Users/daniel/agent-claw/cdk/node_modules/aws-cdk-lib/aws-apigatewayv2/lib/http/api.js:1:96 in aws-cdk-lib...",
|
||||||
"Array.map (:)",
|
"Array.map (:)",
|
||||||
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
"...WrappedClass.<anonymous> in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:295:13)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:293:13)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -877,8 +775,8 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...environmentFromArn.grantRead in aws-cdk-lib...",
|
"...WrappedClass.addToRolePolicy in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:258:29)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:263:20)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -893,7 +791,7 @@
|
|||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...WrappedClass.grantSendMessages in aws-cdk-lib...",
|
"...WrappedClass.grantSendMessages in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:326:18)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:324:18)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
@@ -907,8 +805,8 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:cdk:creationStack",
|
"type": "aws:cdk:creationStack",
|
||||||
"data": [
|
"data": [
|
||||||
"...environmentFromArn.grantRead in aws-cdk-lib...",
|
"...WrappedClass.addToRolePolicy in aws-cdk-lib...",
|
||||||
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:348:20)",
|
"new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:346:17)",
|
||||||
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
"<anonymous> (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)",
|
||||||
"...node internals, ts-node, ts-node, ts-node..."
|
"...node internals, ts-node, ts-node, ts-node..."
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,238 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Description": "agent-claw: serverless personal assistant on AgentCore",
|
"Description": "agent-claw: serverless personal assistant on AgentCore",
|
||||||
"Resources": {
|
"Resources": {
|
||||||
"WorkspaceBucket53E30B92": {
|
|
||||||
"Type": "AWS::S3::Bucket",
|
|
||||||
"Properties": {
|
|
||||||
"BucketEncryption": {
|
|
||||||
"ServerSideEncryptionConfiguration": [
|
|
||||||
{
|
|
||||||
"ServerSideEncryptionByDefault": {
|
|
||||||
"SSEAlgorithm": "AES256"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"BucketName": "agent-claw-workspace-495395224548",
|
|
||||||
"Tags": [
|
|
||||||
{
|
|
||||||
"Key": "aws-cdk:cr-owned:254e75d0",
|
|
||||||
"Value": "true"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"UpdateReplacePolicy": "Retain",
|
|
||||||
"DeletionPolicy": "Retain",
|
|
||||||
"Metadata": {
|
|
||||||
"aws:cdk:path": "AgentClawStack/WorkspaceBucket/Resource"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"WorkspaceFilesAwsCliLayer50B6E9D8": {
|
|
||||||
"Type": "AWS::Lambda::LayerVersion",
|
|
||||||
"Properties": {
|
|
||||||
"Content": {
|
|
||||||
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
|
||||||
"S3Key": "e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip"
|
|
||||||
},
|
|
||||||
"Description": "/opt/awscli/aws"
|
|
||||||
},
|
|
||||||
"Metadata": {
|
|
||||||
"aws:cdk:path": "AgentClawStack/WorkspaceFiles/AwsCliLayer/Resource",
|
|
||||||
"aws:asset:path": "asset.e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip",
|
|
||||||
"aws:asset:is-bundled": false,
|
|
||||||
"aws:asset:property": "Content"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"WorkspaceFilesCustomResourceA7FC771F": {
|
|
||||||
"Type": "Custom::CDKBucketDeployment",
|
|
||||||
"Properties": {
|
|
||||||
"ServiceToken": {
|
|
||||||
"Fn::GetAtt": [
|
|
||||||
"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536",
|
|
||||||
"Arn"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"SourceBucketNames": [
|
|
||||||
"cdk-hnb659fds-assets-495395224548-us-east-1"
|
|
||||||
],
|
|
||||||
"SourceObjectKeys": [
|
|
||||||
"0feea8d997b96e31a1bd7dd049faf8ee17babeb6d2f5b663ba7e3a70387302e0.zip"
|
|
||||||
],
|
|
||||||
"DestinationBucketName": {
|
|
||||||
"Ref": "WorkspaceBucket53E30B92"
|
|
||||||
},
|
|
||||||
"WaitForDistributionInvalidation": true,
|
|
||||||
"Prune": true,
|
|
||||||
"OutputObjectKeys": true
|
|
||||||
},
|
|
||||||
"UpdateReplacePolicy": "Delete",
|
|
||||||
"DeletionPolicy": "Delete",
|
|
||||||
"Metadata": {
|
|
||||||
"aws:cdk:path": "AgentClawStack/WorkspaceFiles/CustomResource/Default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Action": "sts:AssumeRole",
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": "lambda.amazonaws.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Version": "2012-10-17"
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{
|
|
||||||
"Fn::Join": [
|
|
||||||
"",
|
|
||||||
[
|
|
||||||
"arn:",
|
|
||||||
{
|
|
||||||
"Ref": "AWS::Partition"
|
|
||||||
},
|
|
||||||
":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Metadata": {
|
|
||||||
"aws:cdk:path": "AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF": {
|
|
||||||
"Type": "AWS::IAM::Policy",
|
|
||||||
"Properties": {
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Action": [
|
|
||||||
"s3:GetObject*",
|
|
||||||
"s3:GetBucket*",
|
|
||||||
"s3:List*"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": [
|
|
||||||
{
|
|
||||||
"Fn::Join": [
|
|
||||||
"",
|
|
||||||
[
|
|
||||||
"arn:",
|
|
||||||
{
|
|
||||||
"Ref": "AWS::Partition"
|
|
||||||
},
|
|
||||||
":s3:::cdk-hnb659fds-assets-495395224548-us-east-1"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Fn::Join": [
|
|
||||||
"",
|
|
||||||
[
|
|
||||||
"arn:",
|
|
||||||
{
|
|
||||||
"Ref": "AWS::Partition"
|
|
||||||
},
|
|
||||||
":s3:::cdk-hnb659fds-assets-495395224548-us-east-1/*"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Action": [
|
|
||||||
"s3:GetObject*",
|
|
||||||
"s3:GetBucket*",
|
|
||||||
"s3:List*",
|
|
||||||
"s3:DeleteObject*",
|
|
||||||
"s3:PutObject",
|
|
||||||
"s3:PutObjectLegalHold",
|
|
||||||
"s3:PutObjectRetention",
|
|
||||||
"s3:PutObjectTagging",
|
|
||||||
"s3:PutObjectVersionTagging",
|
|
||||||
"s3:Abort*"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": [
|
|
||||||
{
|
|
||||||
"Fn::GetAtt": [
|
|
||||||
"WorkspaceBucket53E30B92",
|
|
||||||
"Arn"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Fn::Join": [
|
|
||||||
"",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Fn::GetAtt": [
|
|
||||||
"WorkspaceBucket53E30B92",
|
|
||||||
"Arn"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/*"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Version": "2012-10-17"
|
|
||||||
},
|
|
||||||
"PolicyName": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF",
|
|
||||||
"Roles": [
|
|
||||||
{
|
|
||||||
"Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Metadata": {
|
|
||||||
"aws:cdk:path": "AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
|
||||||
"S3Key": "3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9.zip"
|
|
||||||
},
|
|
||||||
"Environment": {
|
|
||||||
"Variables": {
|
|
||||||
"AWS_CA_BUNDLE": "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Handler": "index.handler",
|
|
||||||
"Layers": [
|
|
||||||
{
|
|
||||||
"Ref": "WorkspaceFilesAwsCliLayer50B6E9D8"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Role": {
|
|
||||||
"Fn::GetAtt": [
|
|
||||||
"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265",
|
|
||||||
"Arn"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Runtime": "python3.13",
|
|
||||||
"Timeout": 900
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF",
|
|
||||||
"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265"
|
|
||||||
],
|
|
||||||
"Metadata": {
|
|
||||||
"aws:cdk:path": "AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource",
|
|
||||||
"aws:asset:path": "asset.3423a042b818e31c1e34a19d6689ab2e5f9b70fcbe9e71df66f241b20a200bd9",
|
|
||||||
"aws:asset:is-bundled": false,
|
|
||||||
"aws:asset:property": "Code"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SessionStore8C86EEFE": {
|
"SessionStore8C86EEFE": {
|
||||||
"Type": "AWS::DynamoDB::Table",
|
"Type": "AWS::DynamoDB::Table",
|
||||||
"Properties": {
|
"Properties": {
|
||||||
@@ -353,13 +121,52 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Action": "ssm:GetParameter",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/telegram-bot-token",
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/brave-api-key",
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/google-oauth-client"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
"secretsmanager:GetSecretValue",
|
"s3:DeleteObject*",
|
||||||
"secretsmanager:DescribeSecret"
|
"s3:PutObject",
|
||||||
|
"s3:PutObjectLegalHold",
|
||||||
|
"s3:PutObjectRetention",
|
||||||
|
"s3:PutObjectTagging",
|
||||||
|
"s3:PutObjectVersionTagging",
|
||||||
|
"s3:Abort*"
|
||||||
],
|
],
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"
|
"Resource": [
|
||||||
|
{
|
||||||
|
"Fn::Join": [
|
||||||
|
"",
|
||||||
|
[
|
||||||
|
"arn:",
|
||||||
|
{
|
||||||
|
"Ref": "AWS::Partition"
|
||||||
|
},
|
||||||
|
":s3:::agent-claw-workspace-495395224548"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Fn::Join": [
|
||||||
|
"",
|
||||||
|
[
|
||||||
|
"arn:",
|
||||||
|
{
|
||||||
|
"Ref": "AWS::Partition"
|
||||||
|
},
|
||||||
|
":s3:::agent-claw-workspace-495395224548/*"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Version": "2012-10-17"
|
"Version": "2012-10-17"
|
||||||
@@ -380,15 +187,16 @@
|
|||||||
"Properties": {
|
"Properties": {
|
||||||
"Code": {
|
"Code": {
|
||||||
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"S3Key": "f4461651bfa7d2822e3f36525ace7882e1610dcdaf85e052e1907241e25491d6.zip"
|
"S3Key": "e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2.zip"
|
||||||
},
|
},
|
||||||
"Environment": {
|
"Environment": {
|
||||||
"Variables": {
|
"Variables": {
|
||||||
"MESSAGE_QUEUE_URL": {
|
"MESSAGE_QUEUE_URL": {
|
||||||
"Ref": "MessageQueue7A3BF959"
|
"Ref": "MessageQueue7A3BF959"
|
||||||
},
|
},
|
||||||
"TELEGRAM_BOT_TOKEN_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3",
|
"TELEGRAM_BOT_TOKEN_SSM_PARAM": "/agent-claw/telegram-bot-token",
|
||||||
"TELEGRAM_WEBHOOK_SECRET": ""
|
"TELEGRAM_WEBHOOK_SECRET": "",
|
||||||
|
"ATTACHMENTS_BUCKET_NAME": "agent-claw-workspace-495395224548"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"FunctionName": "agent-claw-tg-ingest",
|
"FunctionName": "agent-claw-tg-ingest",
|
||||||
@@ -409,7 +217,7 @@
|
|||||||
],
|
],
|
||||||
"Metadata": {
|
"Metadata": {
|
||||||
"aws:cdk:path": "AgentClawStack/TgIngest/Resource",
|
"aws:cdk:path": "AgentClawStack/TgIngest/Resource",
|
||||||
"aws:asset:path": "asset.f4461651bfa7d2822e3f36525ace7882e1610dcdaf85e052e1907241e25491d6",
|
"aws:asset:path": "asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2",
|
||||||
"aws:asset:is-bundled": false,
|
"aws:asset:is-bundled": false,
|
||||||
"aws:asset:property": "Code"
|
"aws:asset:property": "Code"
|
||||||
}
|
}
|
||||||
@@ -538,42 +346,39 @@
|
|||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": [
|
"Resource": [
|
||||||
{
|
{
|
||||||
"Fn::GetAtt": [
|
"Fn::Join": [
|
||||||
"WorkspaceBucket53E30B92",
|
"",
|
||||||
"Arn"
|
[
|
||||||
|
"arn:",
|
||||||
|
{
|
||||||
|
"Ref": "AWS::Partition"
|
||||||
|
},
|
||||||
|
":s3:::agent-claw-workspace-495395224548"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Fn::Join": [
|
"Fn::Join": [
|
||||||
"",
|
"",
|
||||||
[
|
[
|
||||||
|
"arn:",
|
||||||
{
|
{
|
||||||
"Fn::GetAtt": [
|
"Ref": "AWS::Partition"
|
||||||
"WorkspaceBucket53E30B92",
|
|
||||||
"Arn"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"/*"
|
":s3:::agent-claw-workspace-495395224548/*"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": "ssm:GetParameter",
|
||||||
"secretsmanager:GetSecretValue",
|
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"
|
"Resource": [
|
||||||
},
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/telegram-bot-token",
|
||||||
{
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/brave-api-key",
|
||||||
"Action": [
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/google-oauth-client"
|
||||||
"secretsmanager:GetSecretValue",
|
]
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
@@ -615,18 +420,16 @@
|
|||||||
"Properties": {
|
"Properties": {
|
||||||
"Code": {
|
"Code": {
|
||||||
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"S3Key": "e8d92532d2cb081ba122764c803acc80aaa41350d3497665468ca165dd5ff799.zip"
|
"S3Key": "59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848.zip"
|
||||||
},
|
},
|
||||||
"Environment": {
|
"Environment": {
|
||||||
"Variables": {
|
"Variables": {
|
||||||
"SESSION_TABLE_NAME": {
|
"SESSION_TABLE_NAME": {
|
||||||
"Ref": "SessionStore8C86EEFE"
|
"Ref": "SessionStore8C86EEFE"
|
||||||
},
|
},
|
||||||
"WORKSPACE_BUCKET_NAME": {
|
"WORKSPACE_BUCKET_NAME": "agent-claw-workspace-495395224548",
|
||||||
"Ref": "WorkspaceBucket53E30B92"
|
"TELEGRAM_BOT_TOKEN_SSM_PARAM": "/agent-claw/telegram-bot-token",
|
||||||
},
|
"BRAVE_API_KEY_SSM_PARAM": "/agent-claw/brave-api-key",
|
||||||
"TELEGRAM_BOT_TOKEN_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3",
|
|
||||||
"BRAVE_API_KEY_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi",
|
|
||||||
"RUNTIME_1_ARN": "arn:aws:bedrock-agentcore:us-east-1:495395224548:runtime/agentclaw_agent_claw_main-vTRGIEG6ON",
|
"RUNTIME_1_ARN": "arn:aws:bedrock-agentcore:us-east-1:495395224548:runtime/agentclaw_agent_claw_main-vTRGIEG6ON",
|
||||||
"AWS_REGION_NAME": "us-east-1",
|
"AWS_REGION_NAME": "us-east-1",
|
||||||
"USERS_TABLE_NAME": {
|
"USERS_TABLE_NAME": {
|
||||||
@@ -653,7 +456,7 @@
|
|||||||
],
|
],
|
||||||
"Metadata": {
|
"Metadata": {
|
||||||
"aws:cdk:path": "AgentClawStack/AgentRunner/Resource",
|
"aws:cdk:path": "AgentClawStack/AgentRunner/Resource",
|
||||||
"aws:asset:path": "asset.e8d92532d2cb081ba122764c803acc80aaa41350d3497665468ca165dd5ff799",
|
"aws:asset:path": "asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848",
|
||||||
"aws:asset:is-bundled": false,
|
"aws:asset:is-bundled": false,
|
||||||
"aws:asset:property": "Code"
|
"aws:asset:property": "Code"
|
||||||
}
|
}
|
||||||
@@ -1055,42 +858,39 @@
|
|||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": [
|
"Resource": [
|
||||||
{
|
{
|
||||||
"Fn::GetAtt": [
|
"Fn::Join": [
|
||||||
"WorkspaceBucket53E30B92",
|
"",
|
||||||
"Arn"
|
[
|
||||||
|
"arn:",
|
||||||
|
{
|
||||||
|
"Ref": "AWS::Partition"
|
||||||
|
},
|
||||||
|
":s3:::agent-claw-workspace-495395224548"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Fn::Join": [
|
"Fn::Join": [
|
||||||
"",
|
"",
|
||||||
[
|
[
|
||||||
|
"arn:",
|
||||||
{
|
{
|
||||||
"Fn::GetAtt": [
|
"Ref": "AWS::Partition"
|
||||||
"WorkspaceBucket53E30B92",
|
|
||||||
"Arn"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"/*"
|
":s3:::agent-claw-workspace-495395224548/*"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": "ssm:GetParameter",
|
||||||
"secretsmanager:GetSecretValue",
|
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"
|
"Resource": [
|
||||||
},
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/telegram-bot-token",
|
||||||
{
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/brave-api-key",
|
||||||
"Action": [
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/google-oauth-client"
|
||||||
"secretsmanager:GetSecretValue",
|
]
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
@@ -1161,14 +961,6 @@
|
|||||||
},
|
},
|
||||||
"Sid": "WorkspaceMcpInvoke"
|
"Sid": "WorkspaceMcpInvoke"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Action": [
|
|
||||||
"secretsmanager:GetSecretValue",
|
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Action": "secretsmanager:GetSecretValue",
|
"Action": "secretsmanager:GetSecretValue",
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
@@ -1207,6 +999,72 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Sid": "SchedulerLambdaPermission"
|
"Sid": "SchedulerLambdaPermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"codebuild:*",
|
||||||
|
"ecr:*",
|
||||||
|
"ecs:*",
|
||||||
|
"logs:*"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "*",
|
||||||
|
"Sid": "ComputeBuild"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"s3:List*",
|
||||||
|
"s3:GetObject",
|
||||||
|
"lambda:List*",
|
||||||
|
"lambda:Get*",
|
||||||
|
"cloudformation:Describe*",
|
||||||
|
"cloudformation:List*",
|
||||||
|
"sqs:List*",
|
||||||
|
"sqs:GetQueueAttributes",
|
||||||
|
"ec2:Describe*",
|
||||||
|
"ssm:Describe*",
|
||||||
|
"ssm:List*",
|
||||||
|
"ce:GetCostAndUsage",
|
||||||
|
"ce:GetCostForecast"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "*",
|
||||||
|
"Sid": "BroadReadOnly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"iam:PutRolePolicy",
|
||||||
|
"iam:AttachRolePolicy",
|
||||||
|
"iam:DetachRolePolicy",
|
||||||
|
"iam:DeleteRolePolicy"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": {
|
||||||
|
"Fn::GetAtt": [
|
||||||
|
"Runtime1RoleA7A82078",
|
||||||
|
"Arn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Sid": "IamSelfModify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"iam:CreatePolicy",
|
||||||
|
"iam:GetPolicy",
|
||||||
|
"iam:ListPolicies"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "*",
|
||||||
|
"Sid": "IamPolicyManagement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"ssm:GetParameter",
|
||||||
|
"ssm:GetParameters"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/aws-mcp-url",
|
||||||
|
"Sid": "AwsMcpUrlSsmRead"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Version": "2012-10-17"
|
"Version": "2012-10-17"
|
||||||
@@ -1228,12 +1086,13 @@
|
|||||||
"PolicyDocument": {
|
"PolicyDocument": {
|
||||||
"Statement": [
|
"Statement": [
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": "ssm:GetParameter",
|
||||||
"secretsmanager:GetSecretValue",
|
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl"
|
"Resource": [
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/telegram-bot-token",
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/brave-api-key",
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/google-oauth-client"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Action": "secretsmanager:GetSecretValue",
|
"Action": "secretsmanager:GetSecretValue",
|
||||||
@@ -1293,20 +1152,13 @@
|
|||||||
"PolicyDocument": {
|
"PolicyDocument": {
|
||||||
"Statement": [
|
"Statement": [
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": "ssm:GetParameter",
|
||||||
"secretsmanager:GetSecretValue",
|
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl"
|
"Resource": [
|
||||||
},
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/telegram-bot-token",
|
||||||
{
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/brave-api-key",
|
||||||
"Action": [
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/google-oauth-client"
|
||||||
"secretsmanager:GetSecretValue",
|
]
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
@@ -1346,12 +1198,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Action": "secretsmanager:GetSecretValue",
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl",
|
|
||||||
"Sid": "GoogleOAuthClientSecretExact"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
"secretsmanager:CreateSecret",
|
"secretsmanager:CreateSecret",
|
||||||
@@ -1381,15 +1227,15 @@
|
|||||||
"Properties": {
|
"Properties": {
|
||||||
"Code": {
|
"Code": {
|
||||||
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"S3Key": "99aabce70089266e2352cb313d55ee18b849e39c418e8e9cd25dea8c4bf85fc4.zip"
|
"S3Key": "6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e.zip"
|
||||||
},
|
},
|
||||||
"Environment": {
|
"Environment": {
|
||||||
"Variables": {
|
"Variables": {
|
||||||
"GOOGLE_OAUTH_CLIENT_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl",
|
"GOOGLE_OAUTH_CLIENT_SSM_PARAM": "/agent-claw/google-oauth-client",
|
||||||
"USERS_TABLE_NAME": {
|
"USERS_TABLE_NAME": {
|
||||||
"Ref": "UsersTable9725E9C8"
|
"Ref": "UsersTable9725E9C8"
|
||||||
},
|
},
|
||||||
"TELEGRAM_BOT_TOKEN_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3",
|
"TELEGRAM_BOT_TOKEN_SSM_PARAM": "/agent-claw/telegram-bot-token",
|
||||||
"OAUTH_REDIRECT_URI": {
|
"OAUTH_REDIRECT_URI": {
|
||||||
"Fn::Join": [
|
"Fn::Join": [
|
||||||
"",
|
"",
|
||||||
@@ -1426,7 +1272,7 @@
|
|||||||
],
|
],
|
||||||
"Metadata": {
|
"Metadata": {
|
||||||
"aws:cdk:path": "AgentClawStack/OAuthHandler/Resource",
|
"aws:cdk:path": "AgentClawStack/OAuthHandler/Resource",
|
||||||
"aws:asset:path": "asset.99aabce70089266e2352cb313d55ee18b849e39c418e8e9cd25dea8c4bf85fc4",
|
"aws:asset:path": "asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e",
|
||||||
"aws:asset:is-bundled": false,
|
"aws:asset:is-bundled": false,
|
||||||
"aws:asset:property": "Code"
|
"aws:asset:property": "Code"
|
||||||
}
|
}
|
||||||
@@ -1656,12 +1502,13 @@
|
|||||||
"PolicyDocument": {
|
"PolicyDocument": {
|
||||||
"Statement": [
|
"Statement": [
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": "ssm:GetParameter",
|
||||||
"secretsmanager:GetSecretValue",
|
|
||||||
"secretsmanager:DescribeSecret"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"
|
"Resource": [
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/telegram-bot-token",
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/brave-api-key",
|
||||||
|
"arn:aws:ssm:us-east-1:495395224548:parameter/agent-claw/google-oauth-client"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
@@ -1690,11 +1537,11 @@
|
|||||||
"Properties": {
|
"Properties": {
|
||||||
"Code": {
|
"Code": {
|
||||||
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
"S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1",
|
||||||
"S3Key": "8e7324457a5952eb51f04a34fbc5ba853252e7157d8d8958ac5fda92e72edb1f.zip"
|
"S3Key": "1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b.zip"
|
||||||
},
|
},
|
||||||
"Environment": {
|
"Environment": {
|
||||||
"Variables": {
|
"Variables": {
|
||||||
"TELEGRAM_BOT_TOKEN_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"
|
"TELEGRAM_BOT_TOKEN_SSM_PARAM": "/agent-claw/telegram-bot-token"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"FunctionName": "agent-claw-scheduler",
|
"FunctionName": "agent-claw-scheduler",
|
||||||
@@ -1715,7 +1562,7 @@
|
|||||||
],
|
],
|
||||||
"Metadata": {
|
"Metadata": {
|
||||||
"aws:cdk:path": "AgentClawStack/Scheduler/Resource",
|
"aws:cdk:path": "AgentClawStack/Scheduler/Resource",
|
||||||
"aws:asset:path": "asset.8e7324457a5952eb51f04a34fbc5ba853252e7157d8d8958ac5fda92e72edb1f",
|
"aws:asset:path": "asset.1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b",
|
||||||
"aws:asset:is-bundled": false,
|
"aws:asset:is-bundled": false,
|
||||||
"aws:asset:property": "Code"
|
"aws:asset:property": "Code"
|
||||||
}
|
}
|
||||||
@@ -1740,7 +1587,7 @@
|
|||||||
"CDKMetadata": {
|
"CDKMetadata": {
|
||||||
"Type": "AWS::CDK::Metadata",
|
"Type": "AWS::CDK::Metadata",
|
||||||
"Properties": {
|
"Properties": {
|
||||||
"Analytics": "v2:deflate64:H4sIAAAAAAAA/21R207DMAz9Ft4zA934gG2AQAIxOsTr5LVelS1NSu2sqqL+O0rKxoR4OsfHl9gnGWR3GdxcYceTojxMjN5CWAsWB5UTO98WpLDjTeAphIUvDiRqubM/bIQFMg2Kp5tQUmNcX5MVGFP3Z0EhMwnDPMKgDNbbEiEsd/YFe2o/qWXtrFprWxkSZx+9LSQqZ7Lc/YoPR7KyTuu9YtNoW8X0/+qK2lozX84aN9ZYQ8idoViVcOWMLvrUlNigyt5i7cothA/cjpWJDIq/GMK7J5/ERAaFja5QqMP+mEF4EmnmjY75CDFcC1apYSRRyp2XkT1boarF07V/wlQ3KIpHMoTc/yzuDQ0nQzcmmrnBjgujYd7x0ujkr0q+x0eTK57F1ecPjlMu+JuXxsugrCsJ9nx9zGZwO4Obqz1rPWm9FV0T5CN+AxjAathBAgAA"
|
"Analytics": "v2:deflate64:H4sIAAAAAAAA/21P0U7DMAz8lr2nZnTjAzYEggfEaHmf3NZU2dqk1M6qKsq/o6QbD4inO5991l0O+UMO6xVOnNXNOet0Bb4UrM8KJz563sDe1WeSPTKpZjbY26YC/4lVR+rxyyQSFH8z+A9HLomJBNVhXzUI/tmZWrQ1cfXLny5kpLRurOkNh0GbNq7/Vw809po52m7+mCcojT34wi5REh5sp+s5mRILijdHZCZh2EVQOOgWhSacLzn4F5FhN+hoiBDHUrBNDxcSpcI6WdirEWpHvNX5M6a7oCi2YPCFuyZzHYWgCuLUTKUg8fu137uTwUlQxjYEJ7675Fu438J6dWKts9EZ0T1BseAPQK+curMBAAA="
|
||||||
},
|
},
|
||||||
"Metadata": {
|
"Metadata": {
|
||||||
"aws:cdk:path": "AgentClawStack/CDKMetadata/Default"
|
"aws:cdk:path": "AgentClawStack/CDKMetadata/Default"
|
||||||
@@ -1811,9 +1658,7 @@
|
|||||||
},
|
},
|
||||||
"WorkspaceBucketName": {
|
"WorkspaceBucketName": {
|
||||||
"Description": "S3 bucket containing agent workspace files",
|
"Description": "S3 bucket containing agent workspace files",
|
||||||
"Value": {
|
"Value": "agent-claw-workspace-495395224548"
|
||||||
"Ref": "WorkspaceBucket53E30B92"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"SessionTableName": {
|
"SessionTableName": {
|
||||||
"Description": "DynamoDB table for session mapping",
|
"Description": "DynamoDB table for session mapping",
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
"""EventBridge-triggered Lambda: sends a Telegram reminder then deletes the rule."""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import boto3
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
|
||||||
|
def handler(event, context):
|
||||||
|
chat_id = event['chat_id']
|
||||||
|
message = event['message']
|
||||||
|
rule_name = event['rule_name']
|
||||||
|
|
||||||
|
# Fetch bot token
|
||||||
|
ssm = boto3.client('ssm', region_name='us-east-1')
|
||||||
|
token = ssm.get_parameter(Name=os.environ['TELEGRAM_BOT_TOKEN_SSM_PARAM'], WithDecryption=True)['Parameter']['Value']
|
||||||
|
|
||||||
|
# Send Telegram message
|
||||||
|
payload = json.dumps({'chat_id': chat_id, 'text': message}).encode()
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f'https://api.telegram.org/bot{token}/sendMessage',
|
||||||
|
data=payload,
|
||||||
|
headers={'Content-Type': 'application/json'},
|
||||||
|
)
|
||||||
|
urllib.request.urlopen(req)
|
||||||
|
|
||||||
|
# Delete the one-time rule
|
||||||
|
eb = boto3.client('events', region_name='us-east-1')
|
||||||
|
eb.remove_targets(Rule=rule_name, Ids=['scheduler'])
|
||||||
|
eb.delete_rule(Name=rule_name)
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
import boto3
|
||||||
|
import urllib.request
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
# AWS clients
|
||||||
|
_ddb = None
|
||||||
|
_agentcore = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_ddb():
|
||||||
|
global _ddb
|
||||||
|
if _ddb is None:
|
||||||
|
_ddb = boto3.resource('dynamodb')
|
||||||
|
return _ddb
|
||||||
|
|
||||||
|
|
||||||
|
def get_agentcore():
|
||||||
|
global _agentcore
|
||||||
|
if _agentcore is None:
|
||||||
|
from botocore.config import Config
|
||||||
|
_agentcore = boto3.client(
|
||||||
|
'bedrock-agentcore',
|
||||||
|
region_name='us-east-1',
|
||||||
|
config=Config(read_timeout=600, connect_timeout=10)
|
||||||
|
)
|
||||||
|
return _agentcore
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_user(actor_id: str, from_info: dict) -> dict:
|
||||||
|
"""Look up user in registry, auto-registering on first contact."""
|
||||||
|
table_name = os.environ.get('USERS_TABLE_NAME', '')
|
||||||
|
if not table_name:
|
||||||
|
return {'actor_id': actor_id, 'display_name': from_info.get('from_name', actor_id)}
|
||||||
|
table = get_ddb().Table(table_name)
|
||||||
|
response = table.get_item(Key={'actor_id': actor_id})
|
||||||
|
item = response.get('Item')
|
||||||
|
if item:
|
||||||
|
return item
|
||||||
|
now = int(time.time())
|
||||||
|
item = {
|
||||||
|
'actor_id': actor_id,
|
||||||
|
'display_name': from_info.get('from_name') or actor_id,
|
||||||
|
'telegram_username': from_info.get('from_username', ''),
|
||||||
|
'created_at': str(now),
|
||||||
|
'status': 'pending',
|
||||||
|
'services': {},
|
||||||
|
}
|
||||||
|
table.put_item(Item=item)
|
||||||
|
print(f'[agent-runner] Registered new user (pending): {actor_id}')
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def update_user_status(actor_id: str, name: str, status: str) -> None:
|
||||||
|
table_name = os.environ.get('USERS_TABLE_NAME', '')
|
||||||
|
if not table_name:
|
||||||
|
return
|
||||||
|
table = get_ddb().Table(table_name)
|
||||||
|
table.update_item(
|
||||||
|
Key={'actor_id': actor_id},
|
||||||
|
UpdateExpression='SET display_name = :n, #s = :s',
|
||||||
|
ExpressionAttributeNames={'#s': 'status'},
|
||||||
|
ExpressionAttributeValues={':n': name, ':s': status},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Per-invocation dedup: track sent message hashes to prevent AgentCore retry duplicates
|
||||||
|
_sent_hashes: set = set()
|
||||||
|
|
||||||
|
|
||||||
|
def send_telegram_direct(chat_id: str, token: str, text: str, thread_id: int | None = None) -> None:
|
||||||
|
import hashlib
|
||||||
|
h = hashlib.md5(f'{chat_id}:{text}'.encode()).hexdigest()[:12]
|
||||||
|
if h in _sent_hashes:
|
||||||
|
print(f'[agent-runner] dedup: skipping duplicate message (hash={h})')
|
||||||
|
return
|
||||||
|
_sent_hashes.add(h)
|
||||||
|
url = f'https://api.telegram.org/bot{token}/sendMessage'
|
||||||
|
payload: dict = {'chat_id': chat_id, 'text': text}
|
||||||
|
if thread_id is not None:
|
||||||
|
payload['message_thread_id'] = thread_id
|
||||||
|
data = json.dumps(payload).encode()
|
||||||
|
req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/json'})
|
||||||
|
try:
|
||||||
|
resp = urllib.request.urlopen(req, timeout=10)
|
||||||
|
resp_body = resp.read()
|
||||||
|
import re
|
||||||
|
msg_id = re.search(r'"message_id":(\d+)', resp_body.decode('utf-8', errors='replace'))
|
||||||
|
print(f'[agent-runner] Telegram sendMessage -> msg_id={msg_id.group(1) if msg_id else "?"} hash={h}')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[agent-runner] Telegram sendMessage FAILED: {type(e).__name__}: {e} hash={h}')
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_session(actor_id: str) -> str:
|
||||||
|
"""Look up active session for actor, or create a new one."""
|
||||||
|
table = get_ddb().Table(os.environ['SESSION_TABLE_NAME'])
|
||||||
|
|
||||||
|
response = table.get_item(Key={'actor_id': actor_id})
|
||||||
|
item = response.get('Item')
|
||||||
|
|
||||||
|
now = int(time.time())
|
||||||
|
ttl_8hr = now + (8 * 3600)
|
||||||
|
|
||||||
|
if item and item.get('ttl', 0) > now:
|
||||||
|
# Active session exists — extend TTL
|
||||||
|
table.update_item(
|
||||||
|
Key={'actor_id': actor_id},
|
||||||
|
UpdateExpression='SET #ttl = :ttl',
|
||||||
|
ExpressionAttributeNames={'#ttl': 'ttl'},
|
||||||
|
ExpressionAttributeValues={':ttl': ttl_8hr},
|
||||||
|
)
|
||||||
|
return item['session_id']
|
||||||
|
|
||||||
|
# Create new session
|
||||||
|
session_id = str(uuid.uuid4())
|
||||||
|
table.put_item(Item={
|
||||||
|
'actor_id': actor_id,
|
||||||
|
'session_id': session_id,
|
||||||
|
'created_at': str(now),
|
||||||
|
'ttl': ttl_8hr,
|
||||||
|
})
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
|
||||||
|
def handler(event, context):
|
||||||
|
# ── Parse SQS records (FIFO — all from same actor) ───────────────────
|
||||||
|
records = []
|
||||||
|
for record in event.get('Records', []):
|
||||||
|
try:
|
||||||
|
records.append(json.loads(record['body']))
|
||||||
|
except (json.JSONDecodeError, KeyError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not records:
|
||||||
|
return
|
||||||
|
|
||||||
|
first = records[0]
|
||||||
|
channel = first.get('channel', 'telegram')
|
||||||
|
chat_id = first.get('chat_id', '')
|
||||||
|
message_thread_id = first.get('message_thread_id') # int or None
|
||||||
|
# Use sender's user ID for identity (not chat_id, which is the group ID in group chats)
|
||||||
|
from_info_early = first.get('messages', [{}])[0]
|
||||||
|
sender_id = from_info_early.get('from_id') or chat_id
|
||||||
|
actor_id = f"{channel}:{sender_id}"
|
||||||
|
|
||||||
|
# ── User registry ─────────────────────────────────────────────────────
|
||||||
|
from_info = first.get('messages', [{}])[0]
|
||||||
|
user_profile = get_or_create_user(actor_id, from_info)
|
||||||
|
|
||||||
|
# ── Onboarding gate ─────────────────────────────────────────────────────
|
||||||
|
table_name = os.environ.get('USERS_TABLE_NAME', '')
|
||||||
|
if table_name and user_profile.get('status', 'active') == 'pending':
|
||||||
|
raw_prompt = records[0]['messages'][0]['text'] if records else ''
|
||||||
|
is_name_msg = bool(raw_prompt and len(raw_prompt.strip()) < 50 and '?' not in raw_prompt)
|
||||||
|
if is_name_msg:
|
||||||
|
name = raw_prompt.strip()
|
||||||
|
update_user_status(actor_id, name=name, status='active')
|
||||||
|
user_profile['display_name'] = name
|
||||||
|
user_profile['status'] = 'active'
|
||||||
|
prompt = f"[System: User just registered with name '{name}'. Welcome them warmly and ask how you can help.]"
|
||||||
|
else:
|
||||||
|
bot_token_secret_arn = os.environ.get('TELEGRAM_BOT_TOKEN_SSM_PARAM', '')
|
||||||
|
bot_token = ''
|
||||||
|
if bot_token_secret_arn:
|
||||||
|
ssm = boto3.client('ssm', region_name='us-east-1')
|
||||||
|
bot_token = ssm.get_parameter(Name=bot_token_secret_arn, WithDecryption=True)['Parameter']['Value']
|
||||||
|
send_telegram_direct(chat_id, bot_token, "Hi! I don't recognize you yet. What's your name?", thread_id=message_thread_id)
|
||||||
|
return
|
||||||
|
# ── Get or create AgentCore session ──────────────────────────────────
|
||||||
|
session_id = get_or_create_session(actor_id)
|
||||||
|
print(f"[agent-runner] actor={actor_id} session={session_id} user={user_profile.get('display_name', '')}")
|
||||||
|
|
||||||
|
# ── Bundle messages ───────────────────────────────────────────────────
|
||||||
|
if len(records) == 1:
|
||||||
|
prompt = records[0]['messages'][0]['text']
|
||||||
|
else:
|
||||||
|
lines = [
|
||||||
|
f"[{i+1}] {r['messages'][0]['text']}"
|
||||||
|
for i, r in enumerate(records)
|
||||||
|
]
|
||||||
|
prompt = f"You have {len(records)} queued messages:\n" + "\n".join(lines)
|
||||||
|
|
||||||
|
# ── Attach file context if present ────────────────────────────────────
|
||||||
|
attachment = first.get('attachment')
|
||||||
|
if attachment:
|
||||||
|
file_name = attachment.get('file_name', 'unknown')
|
||||||
|
if 'inline_content' in attachment:
|
||||||
|
prompt += f"\n\n[Attached file: {file_name}]\n```\n{attachment['inline_content']}\n```"
|
||||||
|
elif 's3_key' in attachment:
|
||||||
|
s3_ref = f"s3://{attachment['s3_bucket']}/{attachment['s3_key']}"
|
||||||
|
prompt += f"\n\n[Attached file: {file_name} ({attachment.get('mime_type', '')}) — stored at {s3_ref}]"
|
||||||
|
elif 'error' in attachment:
|
||||||
|
prompt += f"\n\n[Attachment {file_name} could not be processed: {attachment['error']}]"
|
||||||
|
|
||||||
|
# ── Build payload for AgentCore Runtime 1 ────────────────────────────
|
||||||
|
payload: dict[str, Any] = {
|
||||||
|
'prompt': prompt,
|
||||||
|
'actor_id': actor_id,
|
||||||
|
'session_id': session_id,
|
||||||
|
'user_profile': {
|
||||||
|
'display_name': user_profile.get('display_name', actor_id),
|
||||||
|
'telegram_username': user_profile.get('telegram_username', ''),
|
||||||
|
'google_accounts': user_profile.get('google_accounts', {'primary': user_profile['google_email']} if user_profile.get('google_email') else {}),
|
||||||
|
'allowed': user_profile.get('allowed', True),
|
||||||
|
'services': user_profile.get('enrolled_services', user_profile.get('services', {})),
|
||||||
|
},
|
||||||
|
'channel_adapter': {
|
||||||
|
'type': channel,
|
||||||
|
'target_id': str(chat_id),
|
||||||
|
'message_thread_id': message_thread_id,
|
||||||
|
'bot_token_secret_arn': os.environ.get('TELEGRAM_BOT_TOKEN_SSM_PARAM', ''),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Invoke AgentCore Runtime 1 ────────────────────────────────────────
|
||||||
|
runtime_arn = os.environ.get('RUNTIME_1_ARN', '')
|
||||||
|
if not runtime_arn or runtime_arn == 'PLACEHOLDER_SET_AFTER_RUNTIME_DEPLOY':
|
||||||
|
print(f"[agent-runner] RUNTIME_1_ARN not set — skipping AgentCore invoke")
|
||||||
|
print(f"[agent-runner] Would have sent: {json.dumps(payload)[:200]}")
|
||||||
|
return
|
||||||
|
|
||||||
|
client = get_agentcore()
|
||||||
|
response = client.invoke_agent_runtime(
|
||||||
|
agentRuntimeArn=runtime_arn,
|
||||||
|
runtimeSessionId=session_id,
|
||||||
|
payload=json.dumps(payload).encode(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Process streaming response: buffer text chunks and send to Telegram as paragraphs arrive
|
||||||
|
bot_token = ''
|
||||||
|
bot_token_param = os.environ.get('TELEGRAM_BOT_TOKEN_SSM_PARAM', '')
|
||||||
|
if bot_token_param:
|
||||||
|
ssm = boto3.client('ssm', region_name='us-east-1')
|
||||||
|
try:
|
||||||
|
bot_token = ssm.get_parameter(Name=bot_token_param, WithDecryption=True)['Parameter']['Value']
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[agent-runner] Failed to get bot token: {e}')
|
||||||
|
|
||||||
|
body = response.get('response')
|
||||||
|
text_buffer = ''
|
||||||
|
leftover = ''
|
||||||
|
if body is not None:
|
||||||
|
for raw_chunk in body.iter_chunks():
|
||||||
|
if not raw_chunk:
|
||||||
|
continue
|
||||||
|
# AgentCore streams SSE format: "data: {...}\n\n"
|
||||||
|
text = leftover + raw_chunk.decode('utf-8', errors='replace')
|
||||||
|
parts = text.split('\n\n')
|
||||||
|
leftover = parts[-1]
|
||||||
|
for part in parts[:-1]:
|
||||||
|
for line in part.splitlines():
|
||||||
|
if not line.startswith('data: '):
|
||||||
|
continue
|
||||||
|
data = line[6:].strip()
|
||||||
|
if not data or data == '[DONE]':
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
event = json.loads(data)
|
||||||
|
except (json.JSONDecodeError, ValueError):
|
||||||
|
continue
|
||||||
|
if not isinstance(event, dict):
|
||||||
|
continue
|
||||||
|
# Extract text delta from contentBlockDelta ONLY
|
||||||
|
# Do NOT use event.get('data') — that's the full formatted summary,
|
||||||
|
# causing duplicate delivery alongside the token stream.
|
||||||
|
delta = event.get('event', {}).get('contentBlockDelta', {}).get('delta', {})
|
||||||
|
if not isinstance(delta, dict):
|
||||||
|
continue
|
||||||
|
token = delta.get('text', '')
|
||||||
|
if token:
|
||||||
|
text_buffer += token
|
||||||
|
# Only flush if buffer is very large — prevents splitting multi-turn responses
|
||||||
|
if len(text_buffer) > 1200:
|
||||||
|
print(f'[agent-runner] send chunk {len(text_buffer)}c to {chat_id}')
|
||||||
|
send_telegram_direct(str(chat_id), bot_token, text_buffer.strip(), thread_id=message_thread_id)
|
||||||
|
text_buffer = ''
|
||||||
|
|
||||||
|
# Flush any remaining text
|
||||||
|
print(f'[agent-runner] stream done buffer={len(text_buffer)} bot_token_set={bool(bot_token)}')
|
||||||
|
if text_buffer.strip() and bot_token:
|
||||||
|
# Suppress heartbeat OK responses
|
||||||
|
if text_buffer.strip().upper().startswith('HEARTBEAT_OK'):
|
||||||
|
print(f'[agent-runner] heartbeat suppressed for {actor_id}')
|
||||||
|
return
|
||||||
|
print(f'[agent-runner] flushing {len(text_buffer)}c to {chat_id}')
|
||||||
|
send_telegram_direct(str(chat_id), bot_token, text_buffer.strip(), thread_id=message_thread_id)
|
||||||
|
|
||||||
|
print(f"[agent-runner] Completed session={session_id} actor={actor_id}")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
boto3>=1.34.0
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
"""
|
||||||
|
Google OAuth handler Lambda.
|
||||||
|
|
||||||
|
Routes:
|
||||||
|
GET /oauth/start?actor_id=telegram:123&label=work → redirect to Google OAuth consent
|
||||||
|
GET /oauth/callback?code=...&state=... → exchange code, store tokens, update DynamoDB
|
||||||
|
"""
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
import boto3
|
||||||
|
|
||||||
|
_sm = None
|
||||||
|
_ddb = None
|
||||||
|
|
||||||
|
SCOPES = ' '.join([
|
||||||
|
'https://www.googleapis.com/auth/gmail.modify',
|
||||||
|
'https://www.googleapis.com/auth/calendar',
|
||||||
|
'https://www.googleapis.com/auth/drive',
|
||||||
|
'https://www.googleapis.com/auth/spreadsheets',
|
||||||
|
'https://www.googleapis.com/auth/documents',
|
||||||
|
'openid',
|
||||||
|
'email',
|
||||||
|
'profile',
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def get_sm():
|
||||||
|
global _sm
|
||||||
|
if _sm is None:
|
||||||
|
_sm = boto3.client('secretsmanager', region_name=os.environ.get('AWS_REGION', 'us-east-1'))
|
||||||
|
return _sm
|
||||||
|
|
||||||
|
|
||||||
|
def get_ddb():
|
||||||
|
global _ddb
|
||||||
|
if _ddb is None:
|
||||||
|
_ddb = boto3.resource('dynamodb')
|
||||||
|
return _ddb
|
||||||
|
|
||||||
|
|
||||||
|
def get_oauth_client() -> tuple[str, str]:
|
||||||
|
"""Return (client_id, client_secret) from SSM Parameter Store."""
|
||||||
|
param_name = os.environ['GOOGLE_OAUTH_CLIENT_SSM_PARAM']
|
||||||
|
ssm = boto3.client('ssm', region_name=os.environ.get('AWS_REGION', 'us-east-1'))
|
||||||
|
secret = json.loads(ssm.get_parameter(Name=param_name, WithDecryption=True)['Parameter']['Value'])
|
||||||
|
return secret['client_id'], secret['client_secret']
|
||||||
|
|
||||||
|
|
||||||
|
def actor_id_to_secret_name(actor_id: str, label: str = 'primary') -> str:
|
||||||
|
safe = actor_id.replace(':', '-').replace('/', '-')
|
||||||
|
return f'agent-claw/google-credentials/{safe}/{label}'
|
||||||
|
|
||||||
|
|
||||||
|
def _redirect(url: str) -> dict:
|
||||||
|
return {'statusCode': 302, 'headers': {'Location': url}, 'body': ''}
|
||||||
|
|
||||||
|
|
||||||
|
def _html(body: str, status: int = 200) -> dict:
|
||||||
|
return {'statusCode': status, 'headers': {'Content-Type': 'text/html'}, 'body': body}
|
||||||
|
|
||||||
|
|
||||||
|
def handler(event, context):
|
||||||
|
path = event.get('rawPath') or event.get('path', '')
|
||||||
|
params = event.get('queryStringParameters') or {}
|
||||||
|
|
||||||
|
if path.endswith('/oauth/start'):
|
||||||
|
return handle_start(params)
|
||||||
|
elif path.endswith('/oauth/callback'):
|
||||||
|
return handle_callback(params)
|
||||||
|
else:
|
||||||
|
return {'statusCode': 404, 'body': 'Not found'}
|
||||||
|
|
||||||
|
|
||||||
|
def handle_start(params: dict) -> dict:
|
||||||
|
actor_id = params.get('actor_id', '')
|
||||||
|
if not actor_id:
|
||||||
|
return _html('<h1>Missing actor_id</h1>', 400)
|
||||||
|
|
||||||
|
label = params.get('label', 'primary')
|
||||||
|
|
||||||
|
client_id, _ = get_oauth_client()
|
||||||
|
redirect_uri = os.environ['OAUTH_REDIRECT_URI']
|
||||||
|
|
||||||
|
# Encode actor_id + label in state (JSON → base64)
|
||||||
|
state_data = json.dumps({'a': actor_id, 'l': label})
|
||||||
|
state = base64.urlsafe_b64encode(state_data.encode()).decode().rstrip('=')
|
||||||
|
|
||||||
|
auth_url = (
|
||||||
|
'https://accounts.google.com/o/oauth2/v2/auth?'
|
||||||
|
+ urllib.parse.urlencode({
|
||||||
|
'client_id': client_id,
|
||||||
|
'redirect_uri': redirect_uri,
|
||||||
|
'response_type': 'code',
|
||||||
|
'scope': SCOPES,
|
||||||
|
'access_type': 'offline',
|
||||||
|
'prompt': 'consent',
|
||||||
|
'state': state,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return _redirect(auth_url)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_callback(params: dict) -> dict:
|
||||||
|
code = params.get('code', '')
|
||||||
|
state = params.get('state', '')
|
||||||
|
error = params.get('error', '')
|
||||||
|
|
||||||
|
if error:
|
||||||
|
return _html(f'<h1>OAuth error: {error}</h1>', 400)
|
||||||
|
if not code or not state:
|
||||||
|
return _html('<h1>Missing code or state</h1>', 400)
|
||||||
|
|
||||||
|
# Decode actor_id + label from state
|
||||||
|
try:
|
||||||
|
padding = 4 - len(state) % 4
|
||||||
|
state_data = json.loads(base64.urlsafe_b64decode(state + '=' * padding).decode())
|
||||||
|
actor_id = state_data['a']
|
||||||
|
label = state_data.get('l', 'primary')
|
||||||
|
except Exception:
|
||||||
|
# Backward compat: old state was just base64(actor_id)
|
||||||
|
try:
|
||||||
|
padding = 4 - len(state) % 4
|
||||||
|
actor_id = base64.urlsafe_b64decode(state + '=' * padding).decode()
|
||||||
|
label = 'primary'
|
||||||
|
except Exception:
|
||||||
|
return _html('<h1>Invalid state</h1>', 400)
|
||||||
|
|
||||||
|
client_id, client_secret = get_oauth_client()
|
||||||
|
redirect_uri = os.environ['OAUTH_REDIRECT_URI']
|
||||||
|
|
||||||
|
# Exchange code for tokens
|
||||||
|
token_data = urllib.parse.urlencode({
|
||||||
|
'code': code,
|
||||||
|
'client_id': client_id,
|
||||||
|
'client_secret': client_secret,
|
||||||
|
'redirect_uri': redirect_uri,
|
||||||
|
'grant_type': 'authorization_code',
|
||||||
|
}).encode()
|
||||||
|
|
||||||
|
req = urllib.request.Request(
|
||||||
|
'https://oauth2.googleapis.com/token',
|
||||||
|
data=token_data,
|
||||||
|
headers={'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||||
|
tokens = json.loads(resp.read())
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[oauth] Token exchange failed: {e}')
|
||||||
|
return _html(f'<h1>Token exchange failed: {e}</h1>', 500)
|
||||||
|
|
||||||
|
# Fetch user email from Google
|
||||||
|
user_email = ''
|
||||||
|
try:
|
||||||
|
id_token_payload = tokens.get('id_token', '').split('.')[1]
|
||||||
|
padding = 4 - len(id_token_payload) % 4
|
||||||
|
claims = json.loads(base64.urlsafe_b64decode(id_token_payload + '=' * padding))
|
||||||
|
user_email = claims.get('email', '')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not user_email:
|
||||||
|
try:
|
||||||
|
access_token = tokens.get('access_token', '')
|
||||||
|
req2 = urllib.request.Request(
|
||||||
|
'https://www.googleapis.com/oauth2/v3/userinfo',
|
||||||
|
headers={'Authorization': f'Bearer {access_token}'},
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(req2, timeout=10) as resp2:
|
||||||
|
user_email = json.loads(resp2.read()).get('email', '')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[oauth] userinfo fetch failed: {e}')
|
||||||
|
|
||||||
|
# Build credentials dict (google-auth format)
|
||||||
|
creds = {
|
||||||
|
'token': tokens.get('access_token'),
|
||||||
|
'refresh_token': tokens.get('refresh_token'),
|
||||||
|
'token_uri': 'https://oauth2.googleapis.com/token',
|
||||||
|
'client_id': client_id,
|
||||||
|
'client_secret': client_secret,
|
||||||
|
'scopes': [s for s in SCOPES.split() if s.startswith('https://')],
|
||||||
|
'email': user_email,
|
||||||
|
'user_email': user_email,
|
||||||
|
}
|
||||||
|
if tokens.get('expires_in'):
|
||||||
|
creds['expiry'] = time.strftime(
|
||||||
|
'%Y-%m-%dT%H:%M:%SZ',
|
||||||
|
time.gmtime(time.time() + int(tokens['expires_in']))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Store in Secrets Manager at labeled path
|
||||||
|
secret_name = actor_id_to_secret_name(actor_id, label)
|
||||||
|
sm = get_sm()
|
||||||
|
try:
|
||||||
|
sm.create_secret(Name=secret_name, SecretString=json.dumps(creds))
|
||||||
|
except sm.exceptions.ResourceExistsException:
|
||||||
|
sm.put_secret_value(SecretId=secret_name, SecretString=json.dumps(creds))
|
||||||
|
print(f'[oauth] Stored credentials for actor={actor_id} label={label} email={user_email}')
|
||||||
|
|
||||||
|
# Update DynamoDB: merge into google_accounts map
|
||||||
|
table_name = os.environ.get('USERS_TABLE_NAME', '')
|
||||||
|
if table_name and actor_id:
|
||||||
|
try:
|
||||||
|
table = get_ddb().Table(table_name)
|
||||||
|
table.update_item(
|
||||||
|
Key={'actor_id': actor_id},
|
||||||
|
UpdateExpression='SET google_accounts = if_not_exists(google_accounts, :empty)',
|
||||||
|
ExpressionAttributeValues={':empty': {}},
|
||||||
|
)
|
||||||
|
table.update_item(
|
||||||
|
Key={'actor_id': actor_id},
|
||||||
|
UpdateExpression='SET google_accounts.#label = :email',
|
||||||
|
ExpressionAttributeNames={'#label': label},
|
||||||
|
ExpressionAttributeValues={':email': user_email},
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[oauth] DynamoDB update failed: {e}')
|
||||||
|
|
||||||
|
# Best-effort Telegram confirmation
|
||||||
|
try:
|
||||||
|
bot_token_param = os.environ.get('TELEGRAM_BOT_TOKEN_SSM_PARAM', '')
|
||||||
|
if bot_token_param and actor_id.startswith('telegram:'):
|
||||||
|
chat_id = actor_id.split(':', 1)[1]
|
||||||
|
ssm = boto3.client('ssm', region_name=os.environ.get('AWS_REGION', 'us-east-1'))
|
||||||
|
bot_token = ssm.get_parameter(Name=bot_token_param, WithDecryption=True)['Parameter']['Value']
|
||||||
|
tg_text = f'✅ Connected {user_email} as "{label}"'
|
||||||
|
tg_payload = json.dumps({'chat_id': chat_id, 'text': tg_text}).encode()
|
||||||
|
tg_req = urllib.request.Request(
|
||||||
|
f'https://api.telegram.org/bot{bot_token}/sendMessage',
|
||||||
|
data=tg_payload,
|
||||||
|
headers={'Content-Type': 'application/json'},
|
||||||
|
)
|
||||||
|
urllib.request.urlopen(tg_req, timeout=5)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[oauth] Telegram notification failed: {e}')
|
||||||
|
|
||||||
|
return _html(
|
||||||
|
f'<h1>✅ Google account connected!</h1>'
|
||||||
|
f'<p>Connected <b>{user_email}</b> as "<b>{label}</b>".</p>'
|
||||||
|
f'<p>You can close this window and return to Telegram.</p>'
|
||||||
|
)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
boto3>=1.34.0
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import boto3
|
||||||
|
|
||||||
|
# Cache bot token (fetched once at Lambda init)
|
||||||
|
_bot_token: str | None = None
|
||||||
|
_token_lock = threading.Lock()
|
||||||
|
|
||||||
|
TEXT_EXTENSIONS = {'.txt', '.py', '.js', '.ts', '.json', '.md', '.csv', '.xml', '.html',
|
||||||
|
'.css', '.yaml', '.yml', '.toml', '.ini', '.cfg', '.sh', '.bash',
|
||||||
|
'.sql', '.log', '.env', '.rs', '.go', '.java', '.c', '.h', '.cpp'}
|
||||||
|
MAX_INLINE_SIZE = 50 * 1024 # 50KB
|
||||||
|
|
||||||
|
|
||||||
|
def get_bot_token() -> str:
|
||||||
|
global _bot_token
|
||||||
|
if _bot_token is None:
|
||||||
|
with _token_lock:
|
||||||
|
if _bot_token is None:
|
||||||
|
sm = boto3.client('secretsmanager')
|
||||||
|
_bot_token = sm.get_secret_value(
|
||||||
|
SecretId=os.environ['TELEGRAM_BOT_TOKEN_SECRET_ARN']
|
||||||
|
)['SecretString']
|
||||||
|
return _bot_token
|
||||||
|
|
||||||
|
|
||||||
|
def send_typing(chat_id: str, thread_id: int | None = None) -> None:
|
||||||
|
"""Fire-and-forget typing action (does not raise on failure)."""
|
||||||
|
try:
|
||||||
|
token = get_bot_token()
|
||||||
|
payload: dict = {'chat_id': chat_id, 'action': 'typing'}
|
||||||
|
if thread_id is not None:
|
||||||
|
payload['message_thread_id'] = thread_id
|
||||||
|
data = json.dumps(payload).encode()
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f'https://api.telegram.org/bot{token}/sendChatAction',
|
||||||
|
data=data,
|
||||||
|
headers={'Content-Type': 'application/json'},
|
||||||
|
)
|
||||||
|
urllib.request.urlopen(req, timeout=3)
|
||||||
|
except Exception:
|
||||||
|
pass # typing is best-effort
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_from_telegram(file_id: str) -> tuple[str, bytes]:
|
||||||
|
"""Call getFile then download. Returns (file_path, file_bytes)."""
|
||||||
|
token = get_bot_token()
|
||||||
|
# getFile
|
||||||
|
url = f'https://api.telegram.org/bot{token}/getFile'
|
||||||
|
data = json.dumps({'file_id': file_id}).encode()
|
||||||
|
req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/json'})
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
result = json.loads(resp.read()).get('result', {})
|
||||||
|
file_path = result.get('file_path', '')
|
||||||
|
# Download
|
||||||
|
download_url = f'https://api.telegram.org/file/bot{token}/{file_path}'
|
||||||
|
with urllib.request.urlopen(download_url, timeout=60) as resp:
|
||||||
|
file_bytes = resp.read()
|
||||||
|
return file_path, file_bytes
|
||||||
|
|
||||||
|
|
||||||
|
def extract_attachment(message: dict) -> dict | None:
|
||||||
|
"""Extract file attachment info from a Telegram message. Returns metadata dict or None."""
|
||||||
|
# Priority: document > photo > audio > video > voice > video_note
|
||||||
|
if 'document' in message:
|
||||||
|
doc = message['document']
|
||||||
|
return {'type': 'document', 'file_id': doc['file_id'],
|
||||||
|
'file_name': doc.get('file_name', 'document'), 'mime_type': doc.get('mime_type', ''),
|
||||||
|
'file_size': doc.get('file_size', 0)}
|
||||||
|
if 'photo' in message:
|
||||||
|
# Take largest photo (last in array)
|
||||||
|
photo = message['photo'][-1]
|
||||||
|
return {'type': 'photo', 'file_id': photo['file_id'],
|
||||||
|
'file_name': 'photo.jpg', 'mime_type': 'image/jpeg',
|
||||||
|
'file_size': photo.get('file_size', 0)}
|
||||||
|
if 'audio' in message:
|
||||||
|
audio = message['audio']
|
||||||
|
return {'type': 'audio', 'file_id': audio['file_id'],
|
||||||
|
'file_name': audio.get('file_name', 'audio.ogg'), 'mime_type': audio.get('mime_type', 'audio/ogg'),
|
||||||
|
'file_size': audio.get('file_size', 0)}
|
||||||
|
if 'video' in message:
|
||||||
|
video = message['video']
|
||||||
|
return {'type': 'video', 'file_id': video['file_id'],
|
||||||
|
'file_name': video.get('file_name', 'video.mp4'), 'mime_type': video.get('mime_type', 'video/mp4'),
|
||||||
|
'file_size': video.get('file_size', 0)}
|
||||||
|
if 'voice' in message:
|
||||||
|
voice = message['voice']
|
||||||
|
return {'type': 'voice', 'file_id': voice['file_id'],
|
||||||
|
'file_name': 'voice.ogg', 'mime_type': voice.get('mime_type', 'audio/ogg'),
|
||||||
|
'file_size': voice.get('file_size', 0)}
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_text_file(file_name: str, mime_type: str) -> bool:
|
||||||
|
"""Determine if a file should be inlined as text."""
|
||||||
|
ext = os.path.splitext(file_name)[1].lower()
|
||||||
|
if ext in TEXT_EXTENSIONS:
|
||||||
|
return True
|
||||||
|
if mime_type.startswith('text/'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def handler(event, context):
|
||||||
|
# ── Validate Telegram webhook secret ──────────────────────────────────
|
||||||
|
expected_secret = os.environ.get('TELEGRAM_WEBHOOK_SECRET', '')
|
||||||
|
if expected_secret:
|
||||||
|
headers = event.get('headers') or {}
|
||||||
|
received = headers.get('x-telegram-bot-api-secret-token', '')
|
||||||
|
if received != expected_secret:
|
||||||
|
return {'statusCode': 403, 'body': 'Forbidden'}
|
||||||
|
|
||||||
|
# ── Parse Telegram Update ─────────────────────────────────────────────
|
||||||
|
try:
|
||||||
|
body = json.loads(event.get('body', '{}'))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f'[tg-ingest] Bad JSON body')
|
||||||
|
return {'statusCode': 400, 'body': 'Bad Request'}
|
||||||
|
|
||||||
|
print(f'[tg-ingest] Update keys: {list(body.keys())}')
|
||||||
|
update_id = body.get('update_id')
|
||||||
|
|
||||||
|
# Support regular messages and edited messages
|
||||||
|
message = body.get('message') or body.get('edited_message')
|
||||||
|
if not message:
|
||||||
|
print(f'[tg-ingest] No message field, update_type={list(body.keys())}')
|
||||||
|
return {'statusCode': 200, 'body': 'ok'}
|
||||||
|
|
||||||
|
chat_id = str(message.get('chat', {}).get('id', ''))
|
||||||
|
message_thread_id = message.get('message_thread_id') # present for supergroup topics
|
||||||
|
text = message.get('text', '') or message.get('caption', '')
|
||||||
|
from_user = message.get('from', {})
|
||||||
|
timestamp = message.get('date', 0)
|
||||||
|
|
||||||
|
# ── Detect file attachment ────────────────────────────────────────────
|
||||||
|
attachment = extract_attachment(message)
|
||||||
|
attachment_meta = None
|
||||||
|
|
||||||
|
if attachment:
|
||||||
|
print(f'[tg-ingest] Attachment detected: type={attachment["type"]} name={attachment["file_name"]} size={attachment["file_size"]}')
|
||||||
|
try:
|
||||||
|
file_path, file_bytes = get_file_from_telegram(attachment['file_id'])
|
||||||
|
file_name = attachment['file_name']
|
||||||
|
mime_type = attachment['mime_type']
|
||||||
|
|
||||||
|
if is_text_file(file_name, mime_type) and len(file_bytes) <= MAX_INLINE_SIZE:
|
||||||
|
# Inline small text files
|
||||||
|
try:
|
||||||
|
text_content = file_bytes.decode('utf-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
text_content = file_bytes.decode('latin-1')
|
||||||
|
attachment_meta = {
|
||||||
|
'type': attachment['type'],
|
||||||
|
'file_name': file_name,
|
||||||
|
'mime_type': mime_type,
|
||||||
|
'inline_content': text_content,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# Store to S3
|
||||||
|
bucket = os.environ.get('ATTACHMENTS_BUCKET_NAME', '')
|
||||||
|
if bucket:
|
||||||
|
s3 = boto3.client('s3')
|
||||||
|
s3_key = f'attachments/{chat_id}/{update_id}/{file_name}'
|
||||||
|
s3.put_object(Bucket=bucket, Key=s3_key, Body=file_bytes,
|
||||||
|
ContentType=mime_type or 'application/octet-stream')
|
||||||
|
attachment_meta = {
|
||||||
|
'type': attachment['type'],
|
||||||
|
'file_name': file_name,
|
||||||
|
'mime_type': mime_type,
|
||||||
|
's3_bucket': bucket,
|
||||||
|
's3_key': s3_key,
|
||||||
|
}
|
||||||
|
print(f'[tg-ingest] Stored to s3://{bucket}/{s3_key}')
|
||||||
|
else:
|
||||||
|
print(f'[tg-ingest] No ATTACHMENTS_BUCKET_NAME configured, skipping S3 upload')
|
||||||
|
attachment_meta = {
|
||||||
|
'type': attachment['type'],
|
||||||
|
'file_name': file_name,
|
||||||
|
'mime_type': mime_type,
|
||||||
|
'error': 'S3 bucket not configured',
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[tg-ingest] Failed to process attachment: {e}')
|
||||||
|
attachment_meta = {
|
||||||
|
'type': attachment['type'],
|
||||||
|
'file_name': attachment['file_name'],
|
||||||
|
'error': str(e),
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f'[tg-ingest] chat_id={chat_id} text_len={len(text)} attachment={bool(attachment_meta)} update_id={update_id}')
|
||||||
|
|
||||||
|
if not chat_id or (not text and not attachment_meta):
|
||||||
|
print(f'[tg-ingest] Dropping: chat_id={chat_id!r} text={text!r} attachment={attachment_meta}')
|
||||||
|
return {'statusCode': 200, 'body': 'ok'}
|
||||||
|
|
||||||
|
# ── Send typing action (non-blocking, background thread) ──────────────
|
||||||
|
t = threading.Thread(target=send_typing, args=(chat_id, message_thread_id))
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
# ── Enqueue to SQS FIFO ───────────────────────────────────────────────
|
||||||
|
sqs = boto3.client('sqs')
|
||||||
|
msg_body: dict = {
|
||||||
|
'channel': 'telegram',
|
||||||
|
'chat_id': chat_id,
|
||||||
|
'message_thread_id': message_thread_id,
|
||||||
|
'messages': [{
|
||||||
|
'text': text,
|
||||||
|
'from_id': str(from_user.get('id', '')),
|
||||||
|
'from_username': from_user.get('username', ''),
|
||||||
|
'from_name': f"{from_user.get('first_name', '')} {from_user.get('last_name', '')}".strip(),
|
||||||
|
}],
|
||||||
|
'update_id': update_id,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
}
|
||||||
|
if attachment_meta:
|
||||||
|
msg_body['attachment'] = attachment_meta
|
||||||
|
|
||||||
|
sqs.send_message(
|
||||||
|
QueueUrl=os.environ['MESSAGE_QUEUE_URL'],
|
||||||
|
MessageGroupId=chat_id,
|
||||||
|
MessageDeduplicationId=str(update_id),
|
||||||
|
MessageBody=json.dumps(msg_body),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {'statusCode': 200, 'body': 'ok'}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
boto3>=1.34.0
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
import boto3
|
||||||
|
import urllib.request
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
# AWS clients
|
||||||
|
_ddb = None
|
||||||
|
_agentcore = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_ddb():
|
||||||
|
global _ddb
|
||||||
|
if _ddb is None:
|
||||||
|
_ddb = boto3.resource('dynamodb')
|
||||||
|
return _ddb
|
||||||
|
|
||||||
|
|
||||||
|
def get_agentcore():
|
||||||
|
global _agentcore
|
||||||
|
if _agentcore is None:
|
||||||
|
from botocore.config import Config
|
||||||
|
_agentcore = boto3.client(
|
||||||
|
'bedrock-agentcore',
|
||||||
|
region_name='us-east-1',
|
||||||
|
config=Config(read_timeout=600, connect_timeout=10)
|
||||||
|
)
|
||||||
|
return _agentcore
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_user(actor_id: str, from_info: dict) -> dict:
|
||||||
|
"""Look up user in registry, auto-registering on first contact."""
|
||||||
|
table_name = os.environ.get('USERS_TABLE_NAME', '')
|
||||||
|
if not table_name:
|
||||||
|
return {'actor_id': actor_id, 'display_name': from_info.get('from_name', actor_id)}
|
||||||
|
table = get_ddb().Table(table_name)
|
||||||
|
response = table.get_item(Key={'actor_id': actor_id})
|
||||||
|
item = response.get('Item')
|
||||||
|
if item:
|
||||||
|
return item
|
||||||
|
now = int(time.time())
|
||||||
|
item = {
|
||||||
|
'actor_id': actor_id,
|
||||||
|
'display_name': from_info.get('from_name') or actor_id,
|
||||||
|
'telegram_username': from_info.get('from_username', ''),
|
||||||
|
'created_at': str(now),
|
||||||
|
'status': 'pending',
|
||||||
|
'services': {},
|
||||||
|
}
|
||||||
|
table.put_item(Item=item)
|
||||||
|
print(f'[agent-runner] Registered new user (pending): {actor_id}')
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def update_user_status(actor_id: str, name: str, status: str) -> None:
|
||||||
|
table_name = os.environ.get('USERS_TABLE_NAME', '')
|
||||||
|
if not table_name:
|
||||||
|
return
|
||||||
|
table = get_ddb().Table(table_name)
|
||||||
|
table.update_item(
|
||||||
|
Key={'actor_id': actor_id},
|
||||||
|
UpdateExpression='SET display_name = :n, #s = :s',
|
||||||
|
ExpressionAttributeNames={'#s': 'status'},
|
||||||
|
ExpressionAttributeValues={':n': name, ':s': status},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Per-invocation dedup: track sent message hashes to prevent AgentCore retry duplicates
|
||||||
|
_sent_hashes: set = set()
|
||||||
|
|
||||||
|
|
||||||
|
def send_telegram_direct(chat_id: str, token: str, text: str, thread_id: int | None = None) -> None:
|
||||||
|
import hashlib
|
||||||
|
h = hashlib.md5(f'{chat_id}:{text}'.encode()).hexdigest()[:12]
|
||||||
|
if h in _sent_hashes:
|
||||||
|
print(f'[agent-runner] dedup: skipping duplicate message (hash={h})')
|
||||||
|
return
|
||||||
|
_sent_hashes.add(h)
|
||||||
|
url = f'https://api.telegram.org/bot{token}/sendMessage'
|
||||||
|
payload: dict = {'chat_id': chat_id, 'text': text}
|
||||||
|
if thread_id is not None:
|
||||||
|
payload['message_thread_id'] = thread_id
|
||||||
|
data = json.dumps(payload).encode()
|
||||||
|
req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/json'})
|
||||||
|
try:
|
||||||
|
resp = urllib.request.urlopen(req, timeout=10)
|
||||||
|
resp_body = resp.read()
|
||||||
|
import re
|
||||||
|
msg_id = re.search(r'"message_id":(\d+)', resp_body.decode('utf-8', errors='replace'))
|
||||||
|
print(f'[agent-runner] Telegram sendMessage -> msg_id={msg_id.group(1) if msg_id else "?"} hash={h}')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[agent-runner] Telegram sendMessage FAILED: {type(e).__name__}: {e} hash={h}')
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_session(actor_id: str) -> str:
|
||||||
|
"""Look up active session for actor, or create a new one."""
|
||||||
|
table = get_ddb().Table(os.environ['SESSION_TABLE_NAME'])
|
||||||
|
|
||||||
|
response = table.get_item(Key={'actor_id': actor_id})
|
||||||
|
item = response.get('Item')
|
||||||
|
|
||||||
|
now = int(time.time())
|
||||||
|
ttl_8hr = now + (8 * 3600)
|
||||||
|
|
||||||
|
if item and item.get('ttl', 0) > now:
|
||||||
|
# Active session exists — extend TTL
|
||||||
|
table.update_item(
|
||||||
|
Key={'actor_id': actor_id},
|
||||||
|
UpdateExpression='SET #ttl = :ttl',
|
||||||
|
ExpressionAttributeNames={'#ttl': 'ttl'},
|
||||||
|
ExpressionAttributeValues={':ttl': ttl_8hr},
|
||||||
|
)
|
||||||
|
return item['session_id']
|
||||||
|
|
||||||
|
# Create new session
|
||||||
|
session_id = str(uuid.uuid4())
|
||||||
|
table.put_item(Item={
|
||||||
|
'actor_id': actor_id,
|
||||||
|
'session_id': session_id,
|
||||||
|
'created_at': str(now),
|
||||||
|
'ttl': ttl_8hr,
|
||||||
|
})
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
|
||||||
|
def handler(event, context):
|
||||||
|
# ── Parse SQS records (FIFO — all from same actor) ───────────────────
|
||||||
|
records = []
|
||||||
|
for record in event.get('Records', []):
|
||||||
|
try:
|
||||||
|
records.append(json.loads(record['body']))
|
||||||
|
except (json.JSONDecodeError, KeyError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not records:
|
||||||
|
return
|
||||||
|
|
||||||
|
first = records[0]
|
||||||
|
channel = first.get('channel', 'telegram')
|
||||||
|
chat_id = first.get('chat_id', '')
|
||||||
|
message_thread_id = first.get('message_thread_id') # int or None
|
||||||
|
# Use sender's user ID for identity (not chat_id, which is the group ID in group chats)
|
||||||
|
from_info_early = first.get('messages', [{}])[0]
|
||||||
|
sender_id = from_info_early.get('from_id') or chat_id
|
||||||
|
actor_id = f"{channel}:{sender_id}"
|
||||||
|
|
||||||
|
# ── User registry ─────────────────────────────────────────────────────
|
||||||
|
from_info = first.get('messages', [{}])[0]
|
||||||
|
user_profile = get_or_create_user(actor_id, from_info)
|
||||||
|
|
||||||
|
# ── Onboarding gate ─────────────────────────────────────────────────────
|
||||||
|
table_name = os.environ.get('USERS_TABLE_NAME', '')
|
||||||
|
if table_name and user_profile.get('status', 'active') == 'pending':
|
||||||
|
raw_prompt = records[0]['messages'][0]['text'] if records else ''
|
||||||
|
is_name_msg = bool(raw_prompt and len(raw_prompt.strip()) < 50 and '?' not in raw_prompt)
|
||||||
|
if is_name_msg:
|
||||||
|
name = raw_prompt.strip()
|
||||||
|
update_user_status(actor_id, name=name, status='active')
|
||||||
|
user_profile['display_name'] = name
|
||||||
|
user_profile['status'] = 'active'
|
||||||
|
prompt = f"[System: User just registered with name '{name}'. Welcome them warmly and ask how you can help.]"
|
||||||
|
else:
|
||||||
|
bot_token_secret_arn = os.environ.get('TELEGRAM_BOT_TOKEN_SECRET_ARN', '')
|
||||||
|
bot_token = ''
|
||||||
|
if bot_token_secret_arn:
|
||||||
|
sm = boto3.client('secretsmanager', region_name='us-east-1')
|
||||||
|
bot_token = sm.get_secret_value(SecretId=bot_token_secret_arn)['SecretString']
|
||||||
|
send_telegram_direct(chat_id, bot_token, "Hi! I don't recognize you yet. What's your name?", thread_id=message_thread_id)
|
||||||
|
return
|
||||||
|
# ── Get or create AgentCore session ──────────────────────────────────
|
||||||
|
session_id = get_or_create_session(actor_id)
|
||||||
|
print(f"[agent-runner] actor={actor_id} session={session_id} user={user_profile.get('display_name', '')}")
|
||||||
|
|
||||||
|
# ── Bundle messages ───────────────────────────────────────────────────
|
||||||
|
if len(records) == 1:
|
||||||
|
prompt = records[0]['messages'][0]['text']
|
||||||
|
else:
|
||||||
|
lines = [
|
||||||
|
f"[{i+1}] {r['messages'][0]['text']}"
|
||||||
|
for i, r in enumerate(records)
|
||||||
|
]
|
||||||
|
prompt = f"You have {len(records)} queued messages:\n" + "\n".join(lines)
|
||||||
|
|
||||||
|
# ── Attach file context if present ────────────────────────────────────
|
||||||
|
attachment = first.get('attachment')
|
||||||
|
if attachment:
|
||||||
|
file_name = attachment.get('file_name', 'unknown')
|
||||||
|
if 'inline_content' in attachment:
|
||||||
|
prompt += f"\n\n[Attached file: {file_name}]\n```\n{attachment['inline_content']}\n```"
|
||||||
|
elif 's3_key' in attachment:
|
||||||
|
s3_ref = f"s3://{attachment['s3_bucket']}/{attachment['s3_key']}"
|
||||||
|
prompt += f"\n\n[Attached file: {file_name} ({attachment.get('mime_type', '')}) — stored at {s3_ref}]"
|
||||||
|
elif 'error' in attachment:
|
||||||
|
prompt += f"\n\n[Attachment {file_name} could not be processed: {attachment['error']}]"
|
||||||
|
|
||||||
|
# ── Build payload for AgentCore Runtime 1 ────────────────────────────
|
||||||
|
payload: dict[str, Any] = {
|
||||||
|
'prompt': prompt,
|
||||||
|
'actor_id': actor_id,
|
||||||
|
'session_id': session_id,
|
||||||
|
'user_profile': {
|
||||||
|
'display_name': user_profile.get('display_name', actor_id),
|
||||||
|
'telegram_username': user_profile.get('telegram_username', ''),
|
||||||
|
'google_accounts': user_profile.get('google_accounts', {'primary': user_profile['google_email']} if user_profile.get('google_email') else {}),
|
||||||
|
'allowed': user_profile.get('allowed', True),
|
||||||
|
'services': user_profile.get('enrolled_services', user_profile.get('services', {})),
|
||||||
|
},
|
||||||
|
'channel_adapter': {
|
||||||
|
'type': channel,
|
||||||
|
'target_id': str(chat_id),
|
||||||
|
'message_thread_id': message_thread_id,
|
||||||
|
'bot_token_secret_arn': os.environ.get('TELEGRAM_BOT_TOKEN_SECRET_ARN', ''),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Invoke AgentCore Runtime 1 ────────────────────────────────────────
|
||||||
|
runtime_arn = os.environ.get('RUNTIME_1_ARN', '')
|
||||||
|
if not runtime_arn or runtime_arn == 'PLACEHOLDER_SET_AFTER_RUNTIME_DEPLOY':
|
||||||
|
print(f"[agent-runner] RUNTIME_1_ARN not set — skipping AgentCore invoke")
|
||||||
|
print(f"[agent-runner] Would have sent: {json.dumps(payload)[:200]}")
|
||||||
|
return
|
||||||
|
|
||||||
|
client = get_agentcore()
|
||||||
|
response = client.invoke_agent_runtime(
|
||||||
|
agentRuntimeArn=runtime_arn,
|
||||||
|
runtimeSessionId=session_id,
|
||||||
|
payload=json.dumps(payload).encode(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Process streaming response: buffer text chunks and send to Telegram as paragraphs arrive
|
||||||
|
bot_token = ''
|
||||||
|
bot_token_secret_arn = os.environ.get('TELEGRAM_BOT_TOKEN_SECRET_ARN', '')
|
||||||
|
if bot_token_secret_arn:
|
||||||
|
sm = boto3.client('secretsmanager', region_name='us-east-1')
|
||||||
|
try:
|
||||||
|
bot_token = sm.get_secret_value(SecretId=bot_token_secret_arn)['SecretString']
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[agent-runner] Failed to get bot token: {e}')
|
||||||
|
|
||||||
|
body = response.get('response')
|
||||||
|
text_buffer = ''
|
||||||
|
leftover = ''
|
||||||
|
if body is not None:
|
||||||
|
for raw_chunk in body.iter_chunks():
|
||||||
|
if not raw_chunk:
|
||||||
|
continue
|
||||||
|
# AgentCore streams SSE format: "data: {...}\n\n"
|
||||||
|
text = leftover + raw_chunk.decode('utf-8', errors='replace')
|
||||||
|
parts = text.split('\n\n')
|
||||||
|
leftover = parts[-1]
|
||||||
|
for part in parts[:-1]:
|
||||||
|
for line in part.splitlines():
|
||||||
|
if not line.startswith('data: '):
|
||||||
|
continue
|
||||||
|
data = line[6:].strip()
|
||||||
|
if not data or data == '[DONE]':
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
event = json.loads(data)
|
||||||
|
except (json.JSONDecodeError, ValueError):
|
||||||
|
continue
|
||||||
|
if not isinstance(event, dict):
|
||||||
|
continue
|
||||||
|
# Extract text delta from contentBlockDelta ONLY
|
||||||
|
# Do NOT use event.get('data') — that's the full formatted summary,
|
||||||
|
# causing duplicate delivery alongside the token stream.
|
||||||
|
delta = event.get('event', {}).get('contentBlockDelta', {}).get('delta', {})
|
||||||
|
if not isinstance(delta, dict):
|
||||||
|
continue
|
||||||
|
token = delta.get('text', '')
|
||||||
|
if token:
|
||||||
|
text_buffer += token
|
||||||
|
# Only flush if buffer is very large — prevents splitting multi-turn responses
|
||||||
|
if len(text_buffer) > 1200:
|
||||||
|
print(f'[agent-runner] send chunk {len(text_buffer)}c to {chat_id}')
|
||||||
|
send_telegram_direct(str(chat_id), bot_token, text_buffer.strip(), thread_id=message_thread_id)
|
||||||
|
text_buffer = ''
|
||||||
|
|
||||||
|
# Flush any remaining text
|
||||||
|
print(f'[agent-runner] stream done buffer={len(text_buffer)} bot_token_set={bool(bot_token)}')
|
||||||
|
if text_buffer.strip() and bot_token:
|
||||||
|
# Suppress heartbeat OK responses
|
||||||
|
if text_buffer.strip().upper().startswith('HEARTBEAT_OK'):
|
||||||
|
print(f'[agent-runner] heartbeat suppressed for {actor_id}')
|
||||||
|
return
|
||||||
|
print(f'[agent-runner] flushing {len(text_buffer)}c to {chat_id}')
|
||||||
|
send_telegram_direct(str(chat_id), bot_token, text_buffer.strip(), thread_id=message_thread_id)
|
||||||
|
|
||||||
|
print(f"[agent-runner] Completed session={session_id} actor={actor_id}")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
boto3>=1.34.0
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import boto3
|
||||||
|
|
||||||
|
# Cache bot token (fetched once at Lambda init)
|
||||||
|
_bot_token: str | None = None
|
||||||
|
_token_lock = threading.Lock()
|
||||||
|
|
||||||
|
TEXT_EXTENSIONS = {'.txt', '.py', '.js', '.ts', '.json', '.md', '.csv', '.xml', '.html',
|
||||||
|
'.css', '.yaml', '.yml', '.toml', '.ini', '.cfg', '.sh', '.bash',
|
||||||
|
'.sql', '.log', '.env', '.rs', '.go', '.java', '.c', '.h', '.cpp'}
|
||||||
|
MAX_INLINE_SIZE = 50 * 1024 # 50KB
|
||||||
|
|
||||||
|
|
||||||
|
def get_bot_token() -> str:
|
||||||
|
global _bot_token
|
||||||
|
if _bot_token is None:
|
||||||
|
with _token_lock:
|
||||||
|
if _bot_token is None:
|
||||||
|
ssm = boto3.client('ssm')
|
||||||
|
_bot_token = ssm.get_parameter(
|
||||||
|
Name=os.environ['TELEGRAM_BOT_TOKEN_SSM_PARAM'],
|
||||||
|
WithDecryption=True
|
||||||
|
)['Parameter']['Value']
|
||||||
|
return _bot_token
|
||||||
|
|
||||||
|
|
||||||
|
def send_typing(chat_id: str, thread_id: int | None = None) -> None:
|
||||||
|
"""Fire-and-forget typing action (does not raise on failure)."""
|
||||||
|
try:
|
||||||
|
token = get_bot_token()
|
||||||
|
payload: dict = {'chat_id': chat_id, 'action': 'typing'}
|
||||||
|
if thread_id is not None:
|
||||||
|
payload['message_thread_id'] = thread_id
|
||||||
|
data = json.dumps(payload).encode()
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f'https://api.telegram.org/bot{token}/sendChatAction',
|
||||||
|
data=data,
|
||||||
|
headers={'Content-Type': 'application/json'},
|
||||||
|
)
|
||||||
|
urllib.request.urlopen(req, timeout=3)
|
||||||
|
except Exception:
|
||||||
|
pass # typing is best-effort
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_from_telegram(file_id: str) -> tuple[str, bytes]:
|
||||||
|
"""Call getFile then download. Returns (file_path, file_bytes)."""
|
||||||
|
token = get_bot_token()
|
||||||
|
# getFile
|
||||||
|
url = f'https://api.telegram.org/bot{token}/getFile'
|
||||||
|
data = json.dumps({'file_id': file_id}).encode()
|
||||||
|
req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/json'})
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
result = json.loads(resp.read()).get('result', {})
|
||||||
|
file_path = result.get('file_path', '')
|
||||||
|
# Download
|
||||||
|
download_url = f'https://api.telegram.org/file/bot{token}/{file_path}'
|
||||||
|
with urllib.request.urlopen(download_url, timeout=60) as resp:
|
||||||
|
file_bytes = resp.read()
|
||||||
|
return file_path, file_bytes
|
||||||
|
|
||||||
|
|
||||||
|
def extract_attachment(message: dict) -> dict | None:
|
||||||
|
"""Extract file attachment info from a Telegram message. Returns metadata dict or None."""
|
||||||
|
# Priority: document > photo > audio > video > voice > video_note
|
||||||
|
if 'document' in message:
|
||||||
|
doc = message['document']
|
||||||
|
return {'type': 'document', 'file_id': doc['file_id'],
|
||||||
|
'file_name': doc.get('file_name', 'document'), 'mime_type': doc.get('mime_type', ''),
|
||||||
|
'file_size': doc.get('file_size', 0)}
|
||||||
|
if 'photo' in message:
|
||||||
|
# Take largest photo (last in array)
|
||||||
|
photo = message['photo'][-1]
|
||||||
|
return {'type': 'photo', 'file_id': photo['file_id'],
|
||||||
|
'file_name': 'photo.jpg', 'mime_type': 'image/jpeg',
|
||||||
|
'file_size': photo.get('file_size', 0)}
|
||||||
|
if 'audio' in message:
|
||||||
|
audio = message['audio']
|
||||||
|
return {'type': 'audio', 'file_id': audio['file_id'],
|
||||||
|
'file_name': audio.get('file_name', 'audio.ogg'), 'mime_type': audio.get('mime_type', 'audio/ogg'),
|
||||||
|
'file_size': audio.get('file_size', 0)}
|
||||||
|
if 'video' in message:
|
||||||
|
video = message['video']
|
||||||
|
return {'type': 'video', 'file_id': video['file_id'],
|
||||||
|
'file_name': video.get('file_name', 'video.mp4'), 'mime_type': video.get('mime_type', 'video/mp4'),
|
||||||
|
'file_size': video.get('file_size', 0)}
|
||||||
|
if 'voice' in message:
|
||||||
|
voice = message['voice']
|
||||||
|
return {'type': 'voice', 'file_id': voice['file_id'],
|
||||||
|
'file_name': 'voice.ogg', 'mime_type': voice.get('mime_type', 'audio/ogg'),
|
||||||
|
'file_size': voice.get('file_size', 0)}
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_text_file(file_name: str, mime_type: str) -> bool:
|
||||||
|
"""Determine if a file should be inlined as text."""
|
||||||
|
ext = os.path.splitext(file_name)[1].lower()
|
||||||
|
if ext in TEXT_EXTENSIONS:
|
||||||
|
return True
|
||||||
|
if mime_type.startswith('text/'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def handler(event, context):
|
||||||
|
# ── Validate Telegram webhook secret ──────────────────────────────────
|
||||||
|
expected_secret = os.environ.get('TELEGRAM_WEBHOOK_SECRET', '')
|
||||||
|
if expected_secret:
|
||||||
|
headers = event.get('headers') or {}
|
||||||
|
received = headers.get('x-telegram-bot-api-secret-token', '')
|
||||||
|
if received != expected_secret:
|
||||||
|
return {'statusCode': 403, 'body': 'Forbidden'}
|
||||||
|
|
||||||
|
# ── Parse Telegram Update ─────────────────────────────────────────────
|
||||||
|
try:
|
||||||
|
body = json.loads(event.get('body', '{}'))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f'[tg-ingest] Bad JSON body')
|
||||||
|
return {'statusCode': 400, 'body': 'Bad Request'}
|
||||||
|
|
||||||
|
print(f'[tg-ingest] Update keys: {list(body.keys())}')
|
||||||
|
update_id = body.get('update_id')
|
||||||
|
|
||||||
|
# Support regular messages and edited messages
|
||||||
|
message = body.get('message') or body.get('edited_message')
|
||||||
|
if not message:
|
||||||
|
print(f'[tg-ingest] No message field, update_type={list(body.keys())}')
|
||||||
|
return {'statusCode': 200, 'body': 'ok'}
|
||||||
|
|
||||||
|
chat_id = str(message.get('chat', {}).get('id', ''))
|
||||||
|
message_thread_id = message.get('message_thread_id') # present for supergroup topics
|
||||||
|
text = message.get('text', '') or message.get('caption', '')
|
||||||
|
from_user = message.get('from', {})
|
||||||
|
timestamp = message.get('date', 0)
|
||||||
|
|
||||||
|
# ── Detect file attachment ────────────────────────────────────────────
|
||||||
|
attachment = extract_attachment(message)
|
||||||
|
attachment_meta = None
|
||||||
|
|
||||||
|
if attachment:
|
||||||
|
print(f'[tg-ingest] Attachment detected: type={attachment["type"]} name={attachment["file_name"]} size={attachment["file_size"]}')
|
||||||
|
try:
|
||||||
|
file_path, file_bytes = get_file_from_telegram(attachment['file_id'])
|
||||||
|
file_name = attachment['file_name']
|
||||||
|
mime_type = attachment['mime_type']
|
||||||
|
|
||||||
|
if is_text_file(file_name, mime_type) and len(file_bytes) <= MAX_INLINE_SIZE:
|
||||||
|
# Inline small text files
|
||||||
|
try:
|
||||||
|
text_content = file_bytes.decode('utf-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
text_content = file_bytes.decode('latin-1')
|
||||||
|
attachment_meta = {
|
||||||
|
'type': attachment['type'],
|
||||||
|
'file_name': file_name,
|
||||||
|
'mime_type': mime_type,
|
||||||
|
'inline_content': text_content,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# Store to S3
|
||||||
|
bucket = os.environ.get('ATTACHMENTS_BUCKET_NAME', '')
|
||||||
|
if bucket:
|
||||||
|
s3 = boto3.client('s3')
|
||||||
|
s3_key = f'attachments/{chat_id}/{update_id}/{file_name}'
|
||||||
|
s3.put_object(Bucket=bucket, Key=s3_key, Body=file_bytes,
|
||||||
|
ContentType=mime_type or 'application/octet-stream')
|
||||||
|
attachment_meta = {
|
||||||
|
'type': attachment['type'],
|
||||||
|
'file_name': file_name,
|
||||||
|
'mime_type': mime_type,
|
||||||
|
's3_bucket': bucket,
|
||||||
|
's3_key': s3_key,
|
||||||
|
}
|
||||||
|
print(f'[tg-ingest] Stored to s3://{bucket}/{s3_key}')
|
||||||
|
else:
|
||||||
|
print(f'[tg-ingest] No ATTACHMENTS_BUCKET_NAME configured, skipping S3 upload')
|
||||||
|
attachment_meta = {
|
||||||
|
'type': attachment['type'],
|
||||||
|
'file_name': file_name,
|
||||||
|
'mime_type': mime_type,
|
||||||
|
'error': 'S3 bucket not configured',
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[tg-ingest] Failed to process attachment: {e}')
|
||||||
|
attachment_meta = {
|
||||||
|
'type': attachment['type'],
|
||||||
|
'file_name': attachment['file_name'],
|
||||||
|
'error': str(e),
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f'[tg-ingest] chat_id={chat_id} text_len={len(text)} attachment={bool(attachment_meta)} update_id={update_id}')
|
||||||
|
|
||||||
|
if not chat_id or (not text and not attachment_meta):
|
||||||
|
print(f'[tg-ingest] Dropping: chat_id={chat_id!r} text={text!r} attachment={attachment_meta}')
|
||||||
|
return {'statusCode': 200, 'body': 'ok'}
|
||||||
|
|
||||||
|
# ── Send typing action (non-blocking, background thread) ──────────────
|
||||||
|
t = threading.Thread(target=send_typing, args=(chat_id, message_thread_id))
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
# ── Enqueue to SQS FIFO ───────────────────────────────────────────────
|
||||||
|
sqs = boto3.client('sqs')
|
||||||
|
msg_body: dict = {
|
||||||
|
'channel': 'telegram',
|
||||||
|
'chat_id': chat_id,
|
||||||
|
'message_thread_id': message_thread_id,
|
||||||
|
'messages': [{
|
||||||
|
'text': text,
|
||||||
|
'from_id': str(from_user.get('id', '')),
|
||||||
|
'from_username': from_user.get('username', ''),
|
||||||
|
'from_name': f"{from_user.get('first_name', '')} {from_user.get('last_name', '')}".strip(),
|
||||||
|
}],
|
||||||
|
'update_id': update_id,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
}
|
||||||
|
if attachment_meta:
|
||||||
|
msg_body['attachment'] = attachment_meta
|
||||||
|
|
||||||
|
sqs.send_message(
|
||||||
|
QueueUrl=os.environ['MESSAGE_QUEUE_URL'],
|
||||||
|
MessageGroupId=chat_id,
|
||||||
|
MessageDeduplicationId=str(update_id),
|
||||||
|
MessageBody=json.dumps(msg_body),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {'statusCode': 200, 'body': 'ok'}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
boto3>=1.34.0
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"validateOnSynth": false,
|
"validateOnSynth": false,
|
||||||
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-deploy-role-495395224548-us-east-1",
|
"assumeRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-deploy-role-495395224548-us-east-1",
|
||||||
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-cfn-exec-role-495395224548-us-east-1",
|
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::495395224548:role/cdk-hnb659fds-cfn-exec-role-495395224548-us-east-1",
|
||||||
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-495395224548-us-east-1/78c322849179468ec994f5c3e550a0db4961592ea962b9cf484463e5f04b5a70.json",
|
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-495395224548-us-east-1/9c45c012ea9c045aa771b1c3049eadcb15fe66ca16d02e617d50ee9745fa967a.json",
|
||||||
"requiresBootstrapStackVersion": 6,
|
"requiresBootstrapStackVersion": 6,
|
||||||
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
|
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
|
||||||
"additionalDependencies": [
|
"additionalDependencies": [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user