From 266231d070b2cfa6be9291a77022de1e61a12963 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 15 May 2026 10:03:56 -0500 Subject: [PATCH] Add native boto3 AWS tools, remove broken AWS MCP client --- agentclaw/app/agent_claw_main/main.py | 103 ++-- ...7477e0a0e299a850447e7456a8c8091604131b.zip | Bin 0 -> 666 bytes ...500d036cdde29fdb255aa6a82d61555fde4848.zip | Bin 0 -> 4449 bytes ...4cf1c084e3fbfc98ebd0479fed6a84075a378e.zip | Bin 0 -> 3294 bytes ...90bd05892243fb0829b75f0461fbbfb7912e9f.zip | Bin 0 -> 3449 bytes ...640a19f48d0184ace0312db637407d5efaf857.zip | Bin 0 -> 4430 bytes ...0b587c43ed791927a064104e5eb6c507c0cdb2.zip | Bin 0 -> 3474 bytes cdk/cdk.out/AgentClawStack.assets.json | 83 +-- cdk/cdk.out/AgentClawStack.metadata.json | 206 ++----- cdk/cdk.out/AgentClawStack.template.json | 517 ++++++------------ .../handler.py | 29 + .../handler.py | 292 ++++++++++ .../requirements.txt | 1 + .../handler.py | 246 +++++++++ .../requirements.txt | 1 + .../handler.py | 229 ++++++++ .../requirements.txt | 1 + .../handler.py | 292 ++++++++++ .../requirements.txt | 1 + .../handler.py | 230 ++++++++ .../requirements.txt | 1 + cdk/cdk.out/manifest.json | 2 +- cdk/cdk.out/tree.json | 2 +- 23 files changed, 1638 insertions(+), 598 deletions(-) create mode 100644 cdk/cdk.out/.cache/1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b.zip create mode 100644 cdk/cdk.out/.cache/59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848.zip create mode 100644 cdk/cdk.out/.cache/6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e.zip create mode 100644 cdk/cdk.out/.cache/b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f.zip create mode 100644 cdk/cdk.out/.cache/dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857.zip create mode 100644 cdk/cdk.out/.cache/e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2.zip create mode 100644 cdk/cdk.out/asset.1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b/handler.py create mode 100644 cdk/cdk.out/asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848/handler.py create mode 100644 cdk/cdk.out/asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848/requirements.txt create mode 100644 cdk/cdk.out/asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e/handler.py create mode 100644 cdk/cdk.out/asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e/requirements.txt create mode 100644 cdk/cdk.out/asset.b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f/handler.py create mode 100644 cdk/cdk.out/asset.b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f/requirements.txt create mode 100644 cdk/cdk.out/asset.dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857/handler.py create mode 100644 cdk/cdk.out/asset.dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857/requirements.txt create mode 100644 cdk/cdk.out/asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2/handler.py create mode 100644 cdk/cdk.out/asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2/requirements.txt diff --git a/agentclaw/app/agent_claw_main/main.py b/agentclaw/app/agent_claw_main/main.py index 254d9cd..eb86fde 100644 --- a/agentclaw/app/agent_claw_main/main.py +++ b/agentclaw/app/agent_claw_main/main.py @@ -63,25 +63,6 @@ class _SigV4HttpxAuth(httpx.Auth): if self._actor_id: request.headers['x-actor-id'] = self._actor_id 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.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}." +@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 ──────────────────────────────────────────────────────────── # Module-level actor_id for tool closures (set per-invocation) @@ -443,6 +481,7 @@ async def main(payload: dict, context): _now = datetime.now(_tz) _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 + '\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}') # 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, manage_service, manage_mcp_connection, schedule_reminder, list_reminders, cancel_reminder, 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 mcp_connections = services.get('mcp_connections', []) mcp_clients, _mcp_to_close = mcp_loader.load_mcp_tools(mcp_connections, actor_id) - # AWS MCP Server connection (system-level, SigV4 auth) - _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 + all_tools = base_tools + mcp_clients agent = Agent( model=model, @@ -505,11 +529,6 @@ async def main(payload: dict, context): _typing_active = False session_manager.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 memory_manager.check_window_and_flag(actor_id, session_id) diff --git a/cdk/cdk.out/.cache/1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b.zip b/cdk/cdk.out/.cache/1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b.zip new file mode 100644 index 0000000000000000000000000000000000000000..642c306ea858c8a4931662d28dcbf8d36b86f10d GIT binary patch literal 666 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 literal 0 HcmV?d00001 diff --git a/cdk/cdk.out/.cache/6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e.zip b/cdk/cdk.out/.cache/6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e.zip new file mode 100644 index 0000000000000000000000000000000000000000..ab4daa30806110245a72d600704dc57af385d23f GIT binary patch literal 3294 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 SDgH1>uOs;SHM{1Jko*rKlpZet literal 0 HcmV?d00001 diff --git a/cdk/cdk.out/.cache/b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f.zip b/cdk/cdk.out/.cache/b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f.zip new file mode 100644 index 0000000000000000000000000000000000000000..f07f6240f54fede60d773376f82607e4bcbfdf34 GIT binary patch literal 3449 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 O?J3-z>fsXy1?Ar&8fZ@d literal 0 HcmV?d00001 diff --git a/cdk/cdk.out/.cache/dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857.zip b/cdk/cdk.out/.cache/dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857.zip new file mode 100644 index 0000000000000000000000000000000000000000..abf014cfbaf4382d69b4856b986a24288e490c14 GIT binary patch literal 4430 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 O2L9g&_*=jHpX6Uqty&cT literal 0 HcmV?d00001 diff --git a/cdk/cdk.out/.cache/e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2.zip b/cdk/cdk.out/.cache/e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2.zip new file mode 100644 index 0000000000000000000000000000000000000000..c53367211f5a1b7c7eee7dececd5b17a92ff679f GIT binary patch literal 3474 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 (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -56,7 +46,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -70,7 +60,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -84,7 +74,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -98,7 +88,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -112,7 +102,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -126,7 +116,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -140,7 +130,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -154,7 +144,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -168,7 +158,7 @@ { "type": "aws:cdk:creationStack", "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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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)", - " (/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)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", - "...node internals, ts-node, ts-node, ts-node..." - ] - } - ], "/AgentClawStack/SessionStore/Resource": [ { "type": "aws:cdk:logicalId", @@ -239,7 +199,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -254,7 +214,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -269,7 +229,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -284,7 +244,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -299,7 +259,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -314,7 +274,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -329,7 +289,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -344,7 +304,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -359,7 +319,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -374,7 +334,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -389,7 +349,7 @@ "type": "aws:cdk:creationStack", "data": [ "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -404,7 +364,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -419,7 +379,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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)", - " (/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)", - " (/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)", - " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", - "...node internals, ts-node, ts-node, ts-node..." - ] - } - ], "/AgentClawStack/TgIngest/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", @@ -492,7 +407,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -507,7 +422,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -522,7 +437,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -537,7 +452,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -688,7 +603,7 @@ "type": "aws:cdk:creationStack", "data": [ "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -702,8 +617,8 @@ { "type": "aws:cdk:creationStack", "data": [ - "...environmentFromArn.grantRead in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:201:29)", + "...WrappedClass. in aws-cdk-lib...", + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:207:45)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -718,7 +633,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -733,7 +648,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -748,24 +663,7 @@ "type": "aws:cdk:creationStack", "data": [ "...new Function2 in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:337:25)", - " (/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)", + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:335:25)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -780,7 +678,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -795,7 +693,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...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...", "Array.map (:)", "...WrappedClass. 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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -877,8 +775,8 @@ { "type": "aws:cdk:creationStack", "data": [ - "...environmentFromArn.grantRead in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:258:29)", + "...WrappedClass.addToRolePolicy in aws-cdk-lib...", + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:263:20)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -893,7 +791,7 @@ "type": "aws:cdk:creationStack", "data": [ "...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)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] @@ -907,8 +805,8 @@ { "type": "aws:cdk:creationStack", "data": [ - "...environmentFromArn.grantRead in aws-cdk-lib...", - "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:348:20)", + "...WrappedClass.addToRolePolicy in aws-cdk-lib...", + "new AgentClawStack (/Users/daniel/agent-claw/cdk/lib/agent-claw-stack.ts:346:17)", " (/Users/daniel/agent-claw/cdk/bin/agent-claw.ts:8:1)", "...node internals, ts-node, ts-node, ts-node..." ] diff --git a/cdk/cdk.out/AgentClawStack.template.json b/cdk/cdk.out/AgentClawStack.template.json index 1f1e680..23465c5 100644 --- a/cdk/cdk.out/AgentClawStack.template.json +++ b/cdk/cdk.out/AgentClawStack.template.json @@ -1,238 +1,6 @@ { "Description": "agent-claw: serverless personal assistant on AgentCore", "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": { "Type": "AWS::DynamoDB::Table", "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": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", + "s3:Abort*" ], "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" @@ -380,15 +187,16 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1", - "S3Key": "f4461651bfa7d2822e3f36525ace7882e1610dcdaf85e052e1907241e25491d6.zip" + "S3Key": "e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2.zip" }, "Environment": { "Variables": { "MESSAGE_QUEUE_URL": { "Ref": "MessageQueue7A3BF959" }, - "TELEGRAM_BOT_TOKEN_SECRET_ARN": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3", - "TELEGRAM_WEBHOOK_SECRET": "" + "TELEGRAM_BOT_TOKEN_SSM_PARAM": "/agent-claw/telegram-bot-token", + "TELEGRAM_WEBHOOK_SECRET": "", + "ATTACHMENTS_BUCKET_NAME": "agent-claw-workspace-495395224548" } }, "FunctionName": "agent-claw-tg-ingest", @@ -409,7 +217,7 @@ ], "Metadata": { "aws:cdk:path": "AgentClawStack/TgIngest/Resource", - "aws:asset:path": "asset.f4461651bfa7d2822e3f36525ace7882e1610dcdaf85e052e1907241e25491d6", + "aws:asset:path": "asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2", "aws:asset:is-bundled": false, "aws:asset:property": "Code" } @@ -538,42 +346,39 @@ "Effect": "Allow", "Resource": [ { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::agent-claw-workspace-495395224548" + ] ] }, { "Fn::Join": [ "", [ + "arn:", { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" - ] + "Ref": "AWS::Partition" }, - "/*" + ":s3:::agent-claw-workspace-495395224548/*" ] ] } ] }, { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], + "Action": "ssm:GetParameter", "Effect": "Allow", - "Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3" - }, - { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], - "Effect": "Allow", - "Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi" + "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": [ @@ -615,18 +420,16 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1", - "S3Key": "e8d92532d2cb081ba122764c803acc80aaa41350d3497665468ca165dd5ff799.zip" + "S3Key": "59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848.zip" }, "Environment": { "Variables": { "SESSION_TABLE_NAME": { "Ref": "SessionStore8C86EEFE" }, - "WORKSPACE_BUCKET_NAME": { - "Ref": "WorkspaceBucket53E30B92" - }, - "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", + "WORKSPACE_BUCKET_NAME": "agent-claw-workspace-495395224548", + "TELEGRAM_BOT_TOKEN_SSM_PARAM": "/agent-claw/telegram-bot-token", + "BRAVE_API_KEY_SSM_PARAM": "/agent-claw/brave-api-key", "RUNTIME_1_ARN": "arn:aws:bedrock-agentcore:us-east-1:495395224548:runtime/agentclaw_agent_claw_main-vTRGIEG6ON", "AWS_REGION_NAME": "us-east-1", "USERS_TABLE_NAME": { @@ -653,7 +456,7 @@ ], "Metadata": { "aws:cdk:path": "AgentClawStack/AgentRunner/Resource", - "aws:asset:path": "asset.e8d92532d2cb081ba122764c803acc80aaa41350d3497665468ca165dd5ff799", + "aws:asset:path": "asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848", "aws:asset:is-bundled": false, "aws:asset:property": "Code" } @@ -1055,42 +858,39 @@ "Effect": "Allow", "Resource": [ { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::agent-claw-workspace-495395224548" + ] ] }, { "Fn::Join": [ "", [ + "arn:", { - "Fn::GetAtt": [ - "WorkspaceBucket53E30B92", - "Arn" - ] + "Ref": "AWS::Partition" }, - "/*" + ":s3:::agent-claw-workspace-495395224548/*" ] ] } ] }, { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], + "Action": "ssm:GetParameter", "Effect": "Allow", - "Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3" - }, - { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], - "Effect": "Allow", - "Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi" + "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": [ @@ -1161,14 +961,6 @@ }, "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", "Effect": "Allow", @@ -1207,6 +999,72 @@ ] }, "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" @@ -1228,12 +1086,13 @@ "PolicyDocument": { "Statement": [ { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], + "Action": "ssm:GetParameter", "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", @@ -1293,20 +1152,13 @@ "PolicyDocument": { "Statement": [ { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], + "Action": "ssm:GetParameter", "Effect": "Allow", - "Resource": "arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl" - }, - { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], - "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": [ @@ -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": [ "secretsmanager:CreateSecret", @@ -1381,15 +1227,15 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1", - "S3Key": "99aabce70089266e2352cb313d55ee18b849e39c418e8e9cd25dea8c4bf85fc4.zip" + "S3Key": "6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e.zip" }, "Environment": { "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": { "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": { "Fn::Join": [ "", @@ -1426,7 +1272,7 @@ ], "Metadata": { "aws:cdk:path": "AgentClawStack/OAuthHandler/Resource", - "aws:asset:path": "asset.99aabce70089266e2352cb313d55ee18b849e39c418e8e9cd25dea8c4bf85fc4", + "aws:asset:path": "asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e", "aws:asset:is-bundled": false, "aws:asset:property": "Code" } @@ -1656,12 +1502,13 @@ "PolicyDocument": { "Statement": [ { - "Action": [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret" - ], + "Action": "ssm:GetParameter", "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": [ @@ -1690,11 +1537,11 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-495395224548-us-east-1", - "S3Key": "8e7324457a5952eb51f04a34fbc5ba853252e7157d8d8958ac5fda92e72edb1f.zip" + "S3Key": "1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b.zip" }, "Environment": { "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", @@ -1715,7 +1562,7 @@ ], "Metadata": { "aws:cdk:path": "AgentClawStack/Scheduler/Resource", - "aws:asset:path": "asset.8e7324457a5952eb51f04a34fbc5ba853252e7157d8d8958ac5fda92e72edb1f", + "aws:asset:path": "asset.1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b", "aws:asset:is-bundled": false, "aws:asset:property": "Code" } @@ -1740,7 +1587,7 @@ "CDKMetadata": { "Type": "AWS::CDK::Metadata", "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": { "aws:cdk:path": "AgentClawStack/CDKMetadata/Default" @@ -1811,9 +1658,7 @@ }, "WorkspaceBucketName": { "Description": "S3 bucket containing agent workspace files", - "Value": { - "Ref": "WorkspaceBucket53E30B92" - } + "Value": "agent-claw-workspace-495395224548" }, "SessionTableName": { "Description": "DynamoDB table for session mapping", diff --git a/cdk/cdk.out/asset.1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b/handler.py b/cdk/cdk.out/asset.1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b/handler.py new file mode 100644 index 0000000..d67efda --- /dev/null +++ b/cdk/cdk.out/asset.1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b/handler.py @@ -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) diff --git a/cdk/cdk.out/asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848/handler.py b/cdk/cdk.out/asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848/handler.py new file mode 100644 index 0000000..eb07b75 --- /dev/null +++ b/cdk/cdk.out/asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848/handler.py @@ -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}") diff --git a/cdk/cdk.out/asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848/requirements.txt b/cdk/cdk.out/asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848/requirements.txt new file mode 100644 index 0000000..011ba23 --- /dev/null +++ b/cdk/cdk.out/asset.59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848/requirements.txt @@ -0,0 +1 @@ +boto3>=1.34.0 diff --git a/cdk/cdk.out/asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e/handler.py b/cdk/cdk.out/asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e/handler.py new file mode 100644 index 0000000..507fc82 --- /dev/null +++ b/cdk/cdk.out/asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e/handler.py @@ -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('

Missing actor_id

', 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'

OAuth error: {error}

', 400) + if not code or not state: + return _html('

Missing code or state

', 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('

Invalid state

', 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'

Token exchange failed: {e}

', 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'

✅ Google account connected!

' + f'

Connected {user_email} as "{label}".

' + f'

You can close this window and return to Telegram.

' + ) diff --git a/cdk/cdk.out/asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e/requirements.txt b/cdk/cdk.out/asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e/requirements.txt new file mode 100644 index 0000000..011ba23 --- /dev/null +++ b/cdk/cdk.out/asset.6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e/requirements.txt @@ -0,0 +1 @@ +boto3>=1.34.0 diff --git a/cdk/cdk.out/asset.b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f/handler.py b/cdk/cdk.out/asset.b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f/handler.py new file mode 100644 index 0000000..defcb48 --- /dev/null +++ b/cdk/cdk.out/asset.b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f/handler.py @@ -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'} diff --git a/cdk/cdk.out/asset.b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f/requirements.txt b/cdk/cdk.out/asset.b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f/requirements.txt new file mode 100644 index 0000000..011ba23 --- /dev/null +++ b/cdk/cdk.out/asset.b01876bea94330360089c0096390bd05892243fb0829b75f0461fbbfb7912e9f/requirements.txt @@ -0,0 +1 @@ +boto3>=1.34.0 diff --git a/cdk/cdk.out/asset.dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857/handler.py b/cdk/cdk.out/asset.dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857/handler.py new file mode 100644 index 0000000..ee05504 --- /dev/null +++ b/cdk/cdk.out/asset.dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857/handler.py @@ -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}") diff --git a/cdk/cdk.out/asset.dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857/requirements.txt b/cdk/cdk.out/asset.dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857/requirements.txt new file mode 100644 index 0000000..011ba23 --- /dev/null +++ b/cdk/cdk.out/asset.dd2a514b58ca6ec490d8028e46640a19f48d0184ace0312db637407d5efaf857/requirements.txt @@ -0,0 +1 @@ +boto3>=1.34.0 diff --git a/cdk/cdk.out/asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2/handler.py b/cdk/cdk.out/asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2/handler.py new file mode 100644 index 0000000..74ab39d --- /dev/null +++ b/cdk/cdk.out/asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2/handler.py @@ -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'} diff --git a/cdk/cdk.out/asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2/requirements.txt b/cdk/cdk.out/asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2/requirements.txt new file mode 100644 index 0000000..011ba23 --- /dev/null +++ b/cdk/cdk.out/asset.e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2/requirements.txt @@ -0,0 +1 @@ +boto3>=1.34.0 diff --git a/cdk/cdk.out/manifest.json b/cdk/cdk.out/manifest.json index 8b5e66d..0335a49 100644 --- a/cdk/cdk.out/manifest.json +++ b/cdk/cdk.out/manifest.json @@ -18,7 +18,7 @@ "validateOnSynth": false, "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", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-495395224548-us-east-1/78c322849179468ec994f5c3e550a0db4961592ea962b9cf484463e5f04b5a70.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-495395224548-us-east-1/9c45c012ea9c045aa771b1c3049eadcb15fe66ca16d02e617d50ee9745fa967a.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/cdk/cdk.out/tree.json b/cdk/cdk.out/tree.json index 84ec2ee..1cd6f73 100644 --- a/cdk/cdk.out/tree.json +++ b/cdk/cdk.out/tree.json @@ -1 +1 @@ -{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"2.252.0"},"children":{"AgentClawStack":{"id":"AgentClawStack","path":"AgentClawStack","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"2.252.0"},"children":{"TelegramBotToken":{"id":"TelegramBotToken","path":"AgentClawStack/TelegramBotToken","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"BraveApiKey":{"id":"BraveApiKey","path":"AgentClawStack/BraveApiKey","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"WorkspaceBucket":{"id":"WorkspaceBucket","path":"AgentClawStack/WorkspaceBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.Bucket","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WorkspaceBucket/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.CfnBucket","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::S3::Bucket","aws:cdk:cloudformation:logicalId":"WorkspaceBucket53E30B92","aws:cdk:cloudformation:props":{"bucketEncryption":{"serverSideEncryptionConfiguration":[{"serverSideEncryptionByDefault":{"sseAlgorithm":"AES256"}}]},"bucketName":"agent-claw-workspace-495395224548","tags":[{"key":"aws-cdk:cr-owned:254e75d0","value":"true"}]}}}}},"WorkspaceFiles":{"id":"WorkspaceFiles","path":"AgentClawStack/WorkspaceFiles","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_deployment.BucketDeployment","version":"2.252.0"},"children":{"AwsCliLayer":{"id":"AwsCliLayer","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer","constructInfo":{"fqn":"aws-cdk-lib.lambda_layer_awscli.AwsCliLayer","version":"2.252.0"},"children":{"Code":{"id":"Code","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WorkspaceFiles/AwsCliLayer/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnLayerVersion","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::LayerVersion","aws:cdk:cloudformation:logicalId":"WorkspaceFilesAwsCliLayer50B6E9D8","aws:cdk:cloudformation:props":{"content":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"e2659170a0721541efa761a8d5d04d5e36cbbf691c4b15a9053002b7c825055d.zip"},"description":"/opt/awscli/aws"}}}}},"CustomResourceHandler":{"id":"CustomResourceHandler","path":"AgentClawStack/WorkspaceFiles/CustomResourceHandler","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.SingletonFunction","version":"2.252.0"}},"Asset1":{"id":"Asset1","path":"AgentClawStack/WorkspaceFiles/Asset1","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/WorkspaceFiles/Asset1/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/WorkspaceFiles/Asset1/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"CustomResource":{"id":"CustomResource","path":"AgentClawStack/WorkspaceFiles/CustomResource","constructInfo":{"fqn":"aws-cdk-lib.CustomResource","version":"2.252.0"},"children":{"Default":{"id":"Default","path":"AgentClawStack/WorkspaceFiles/CustomResource/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"2.252.0"}}}}}},"Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C":{"id":"Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF","aws:cdk:cloudformation:props":{"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"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536","aws:cdk:cloudformation:props":{"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}}}}},"SessionStore":{"id":"SessionStore","path":"AgentClawStack/SessionStore","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.Table","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/SessionStore/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.CfnTable","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::DynamoDB::Table","aws:cdk:cloudformation:logicalId":"SessionStore8C86EEFE","aws:cdk:cloudformation:props":{"attributeDefinitions":[{"attributeName":"actor_id","attributeType":"S"}],"billingMode":"PAY_PER_REQUEST","keySchema":[{"attributeName":"actor_id","keyType":"HASH"}],"tableName":"agent-claw-sessions","timeToLiveSpecification":{"attributeName":"ttl","enabled":true}}}},"ScalingRole":{"id":"ScalingRole","path":"AgentClawStack/SessionStore/ScalingRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}}}},"UsersTable":{"id":"UsersTable","path":"AgentClawStack/UsersTable","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.Table","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/UsersTable/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.CfnTable","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::DynamoDB::Table","aws:cdk:cloudformation:logicalId":"UsersTable9725E9C8","aws:cdk:cloudformation:props":{"attributeDefinitions":[{"attributeName":"actor_id","attributeType":"S"}],"billingMode":"PAY_PER_REQUEST","keySchema":[{"attributeName":"actor_id","keyType":"HASH"}],"tableName":"agent-claw-users"}}},"ScalingRole":{"id":"ScalingRole","path":"AgentClawStack/UsersTable/ScalingRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}}}},"MessageQueue":{"id":"MessageQueue","path":"AgentClawStack/MessageQueue","constructInfo":{"fqn":"aws-cdk-lib.aws_sqs.Queue","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/MessageQueue/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_sqs.CfnQueue","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SQS::Queue","aws:cdk:cloudformation:logicalId":"MessageQueue7A3BF959","aws:cdk:cloudformation:props":{"contentBasedDeduplication":false,"fifoQueue":true,"queueName":"agent-claw-messages.fifo","receiveMessageWaitTimeSeconds":20,"visibilityTimeout":900}}}}},"TgIngest":{"id":"TgIngest","path":"AgentClawStack/TgIngest","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/TgIngest/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"TgIngestServiceRoleB96980B6","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/TgIngest/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"TgIngestServiceRoleDefaultPolicyCC51E135","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["sqs:SendMessage","sqs:GetQueueAttributes","sqs:GetQueueUrl"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"}],"Version":"2012-10-17"},"policyName":"TgIngestServiceRoleDefaultPolicyCC51E135","roles":[{"Ref":"TgIngestServiceRoleB96980B6"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/TgIngest/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/TgIngest/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/TgIngest/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"TgIngest4CB35C2F","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"f4461651bfa7d2822e3f36525ace7882e1610dcdaf85e052e1907241e25491d6.zip"},"environment":{"variables":{"MESSAGE_QUEUE_URL":{"Ref":"MessageQueue7A3BF959"},"TELEGRAM_BOT_TOKEN_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3","TELEGRAM_WEBHOOK_SECRET":""}},"functionName":"agent-claw-tg-ingest","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["TgIngestServiceRoleB96980B6","Arn"]},"runtime":"python3.12","timeout":10}}}}},"AgentRunner":{"id":"AgentRunner","path":"AgentClawStack/AgentRunner","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/AgentRunner/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"AgentRunnerServiceRole40CA0A00","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/AgentRunner/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"AgentRunnerServiceRoleDefaultPolicyA584A5CF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["SessionStore8C86EEFE","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["SessionStore8C86EEFE","Arn"]}]},{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},{"Fn::Join":["",[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},"/*"]]}]},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi"},{"Action":["sqs:ReceiveMessage","sqs:ChangeMessageVisibility","sqs:GetQueueUrl","sqs:DeleteMessage","sqs:GetQueueAttributes"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":"bedrock-agentcore:InvokeAgentRuntime","Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"},"policyName":"AgentRunnerServiceRoleDefaultPolicyA584A5CF","roles":[{"Ref":"AgentRunnerServiceRole40CA0A00"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/AgentRunner/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/AgentRunner/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/AgentRunner/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"AgentRunnerBDE3FA56","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"e8d92532d2cb081ba122764c803acc80aaa41350d3497665468ca165dd5ff799.zip"},"environment":{"variables":{"SESSION_TABLE_NAME":{"Ref":"SessionStore8C86EEFE"},"WORKSPACE_BUCKET_NAME":{"Ref":"WorkspaceBucket53E30B92"},"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","AWS_REGION_NAME":"us-east-1","USERS_TABLE_NAME":{"Ref":"UsersTable9725E9C8"},"WORKSPACE_MCP_URL":"https://25hugrzw4uwtueeg77jsmft6lq0wunmd.lambda-url.us-east-1.on.aws/mcp"}},"functionName":"agent-claw-agent-runner","handler":"handler.handler","memorySize":256,"role":{"Fn::GetAtt":["AgentRunnerServiceRole40CA0A00","Arn"]},"runtime":"python3.12","timeout":900}}},"SqsEventSource:AgentClawStackMessageQueue9AF4DF23":{"id":"SqsEventSource:AgentClawStackMessageQueue9AF4DF23","path":"AgentClawStack/AgentRunner/SqsEventSource:AgentClawStackMessageQueue9AF4DF23","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.EventSourceMapping","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/SqsEventSource:AgentClawStackMessageQueue9AF4DF23/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnEventSourceMapping","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::EventSourceMapping","aws:cdk:cloudformation:logicalId":"AgentRunnerSqsEventSourceAgentClawStackMessageQueue9AF4DF234671B32B","aws:cdk:cloudformation:props":{"batchSize":10,"enabled":true,"eventSourceArn":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]},"functionName":{"Ref":"AgentRunnerBDE3FA56"}}}}}}}},"WebhookApi":{"id":"WebhookApi","path":"AgentClawStack/WebhookApi","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpApi","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnApi","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Api","aws:cdk:cloudformation:logicalId":"WebhookApi28122C53","aws:cdk:cloudformation:props":{"name":"agent-claw-webhook","protocolType":"HTTP"}}},"DefaultStage":{"id":"DefaultStage","path":"AgentClawStack/WebhookApi/DefaultStage","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpStage","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/DefaultStage/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnStage","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Stage","aws:cdk:cloudformation:logicalId":"WebhookApiDefaultStageC0BC9CA5","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"autoDeploy":true,"stageName":"$default"}}}}},"POST--telegram":{"id":"POST--telegram","path":"AgentClawStack/WebhookApi/POST--telegram","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"TgIngestIntegration":{"id":"TgIngestIntegration","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramTgIngestIntegration9EE5BB85","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::GetAtt":["TgIngest4CB35C2F","Arn"]},"payloadFormatVersion":"2.0"}}}}},"TgIngestIntegration-Permission":{"id":"TgIngestIntegration-Permission","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramTgIngestIntegrationPermissionFEBC2E3B","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["TgIngest4CB35C2F","Arn"]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/telegram"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/POST--telegram/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramF7127CFF","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"POST /telegram","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiPOSTtelegramTgIngestIntegration9EE5BB85"}]]}}}}}},"GET--oauth--start":{"id":"GET--oauth--start","path":"AgentClawStack/WebhookApi/GET--oauth--start","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"OAuthStartIntegration":{"id":"OAuthStartIntegration","path":"AgentClawStack/WebhookApi/GET--oauth--start/OAuthStartIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/GET--oauth--start/OAuthStartIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthstartOAuthStartIntegrationA546443F","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::GetAtt":["OAuthHandlerC97C2476","Arn"]},"payloadFormatVersion":"2.0"}}}}},"OAuthStartIntegration-Permission":{"id":"OAuthStartIntegration-Permission","path":"AgentClawStack/WebhookApi/GET--oauth--start/OAuthStartIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthstartOAuthStartIntegrationPermission38BAEF6D","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["OAuthHandlerC97C2476","Arn"]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/oauth/start"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/GET--oauth--start/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthstart6DCA713A","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"GET /oauth/start","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiGEToauthstartOAuthStartIntegrationA546443F"}]]}}}}}},"GET--oauth--callback":{"id":"GET--oauth--callback","path":"AgentClawStack/WebhookApi/GET--oauth--callback","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"OAuthCallbackIntegration":{"id":"OAuthCallbackIntegration","path":"AgentClawStack/WebhookApi/GET--oauth--callback/OAuthCallbackIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/GET--oauth--callback/OAuthCallbackIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthcallbackOAuthCallbackIntegrationCFBBEB09","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::GetAtt":["OAuthHandlerC97C2476","Arn"]},"payloadFormatVersion":"2.0"}}}}},"OAuthCallbackIntegration-Permission":{"id":"OAuthCallbackIntegration-Permission","path":"AgentClawStack/WebhookApi/GET--oauth--callback/OAuthCallbackIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthcallbackOAuthCallbackIntegrationPermission6BA3A5AD","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["OAuthHandlerC97C2476","Arn"]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/oauth/callback"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/GET--oauth--callback/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthcallbackFC1F6BCD","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"GET /oauth/callback","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiGEToauthcallbackOAuthCallbackIntegrationCFBBEB09"}]]}}}}}},"ANY--workspace--{proxy+}":{"id":"ANY--workspace--{proxy+}","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"WorkspaceMcpIntegration":{"id":"WorkspaceMcpIntegration","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}/WorkspaceMcpIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}/WorkspaceMcpIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiANYworkspaceproxyWorkspaceMcpIntegration7377EE13","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":lambda:us-east-1:495395224548:function:agent-claw-workspace-mcp"]]},"payloadFormatVersion":"2.0"}}}}},"WorkspaceMcpIntegration-Permission":{"id":"WorkspaceMcpIntegration-Permission","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}/WorkspaceMcpIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiANYworkspaceproxyWorkspaceMcpIntegrationPermission97613ADF","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":lambda:us-east-1:495395224548:function:agent-claw-workspace-mcp"]]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/workspace/{proxy+}"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiANYworkspaceproxy4455BE19","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"ANY /workspace/{proxy+}","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiANYworkspaceproxyWorkspaceMcpIntegration7377EE13"}]]}}}}}}}},"Runtime1Role":{"id":"Runtime1Role","path":"AgentClawStack/Runtime1Role","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Runtime1Role/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"Runtime1RoleA7A82078","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"bedrock-agentcore.amazonaws.com"}}],"Version":"2012-10-17"},"description":"Execution role for agent-claw Runtime 1 (main assistant)"}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/Runtime1Role/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Runtime1Role/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"Runtime1RoleDefaultPolicy1A3D5ACF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["bedrock:InvokeModel","bedrock:InvokeModelWithResponseStream"],"Effect":"Allow","Resource":"*"},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},{"Fn::Join":["",[{"Fn::GetAtt":["WorkspaceBucket53E30B92","Arn"]},"/*"]]}]},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"},{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/brave-api-key-uUSgzi"},{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["bedrock-agentcore:CreateEvent","bedrock-agentcore:ListEvents","bedrock-agentcore:RetrieveMemoryRecords"],"Effect":"Allow","Resource":"*"},{"Action":"lambda:InvokeFunctionUrl","Condition":{"StringEquals":{"lambda:FunctionUrlAuthType":"AWS_IAM"}},"Effect":"Allow","Resource":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":lambda:us-east-1:495395224548:function:agent-claw-workspace-mcp"]]},"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","Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*","Sid":"PerUserGoogleCredentialsReadRuntime"},{"Action":"secretsmanager:ListSecrets","Effect":"Allow","Resource":"*","Sid":"GoogleCredentialsListRuntime"},{"Action":["events:PutRule","events:PutTargets","events:ListRules","events:ListTargetsByRule","events:RemoveTargets","events:DeleteRule"],"Effect":"Allow","Resource":"arn:aws:events:us-east-1:*:rule/agent-claw-reminder-*","Sid":"EventBridgeScheduler"},{"Action":["lambda:AddPermission","lambda:RemovePermission"],"Effect":"Allow","Resource":{"Fn::GetAtt":["SchedulerCFE73206","Arn"]},"Sid":"SchedulerLambdaPermission"}],"Version":"2012-10-17"},"policyName":"Runtime1RoleDefaultPolicy1A3D5ACF","roles":[{"Ref":"Runtime1RoleA7A82078"}]}}}}}}},"GoogleOAuthClient":{"id":"GoogleOAuthClient","path":"AgentClawStack/GoogleOAuthClient","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}},"WorkspaceMcpRole":{"id":"WorkspaceMcpRole","path":"AgentClawStack/WorkspaceMcpRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"},"children":{"Policy":{"id":"Policy","path":"AgentClawStack/WorkspaceMcpRole/Policy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WorkspaceMcpRole/Policy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"WorkspaceMcpRolePolicy5B8B0072","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"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","Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*","Sid":"PerUserGoogleCredentialsRead"}],"Version":"2012-10-17"},"policyName":"WorkspaceMcpRolePolicy5B8B0072","roles":["agent-claw-workspace-mcp-role"]}}}}}}},"WorkspaceMcp":{"id":"WorkspaceMcp","path":"AgentClawStack/WorkspaceMcp","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.FunctionBase","version":"2.252.0"}},"OAuthHandler":{"id":"OAuthHandler","path":"AgentClawStack/OAuthHandler","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/OAuthHandler/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/OAuthHandler/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"OAuthHandlerServiceRole9CDCCF9E","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/OAuthHandler/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/OAuthHandler/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"OAuthHandlerServiceRoleDefaultPolicy69D90416","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"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","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"},{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":"secretsmanager:GetSecretValue","Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl","Sid":"GoogleOAuthClientSecretExact"},{"Action":["secretsmanager:CreateSecret","secretsmanager:PutSecretValue","secretsmanager:GetSecretValue"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*","Sid":"PerUserGoogleCredentialsWrite"}],"Version":"2012-10-17"},"policyName":"OAuthHandlerServiceRoleDefaultPolicy69D90416","roles":[{"Ref":"OAuthHandlerServiceRole9CDCCF9E"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/OAuthHandler/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/OAuthHandler/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/OAuthHandler/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/OAuthHandler/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"OAuthHandlerC97C2476","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"99aabce70089266e2352cb313d55ee18b849e39c418e8e9cd25dea8c4bf85fc4.zip"},"environment":{"variables":{"GOOGLE_OAUTH_CLIENT_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl","USERS_TABLE_NAME":{"Ref":"UsersTable9725E9C8"},"TELEGRAM_BOT_TOKEN_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3","OAUTH_REDIRECT_URI":{"Fn::Join":["",["https://",{"Ref":"WebhookApi28122C53"},".execute-api.us-east-1.",{"Ref":"AWS::URLSuffix"},"/oauth/callback"]]}}},"functionName":"agent-claw-oauth-handler","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["OAuthHandlerServiceRole9CDCCF9E","Arn"]},"runtime":"python3.12","timeout":30}}}}},"HeartbeatRunner":{"id":"HeartbeatRunner","path":"AgentClawStack/HeartbeatRunner","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/HeartbeatRunner/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/HeartbeatRunner/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"HeartbeatRunnerServiceRole07B33F7E","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/HeartbeatRunner/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/HeartbeatRunner/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"HeartbeatRunnerServiceRoleDefaultPolicy08E364EE","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["sqs:SendMessage","sqs:GetQueueAttributes","sqs:GetQueueUrl"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]}],"Version":"2012-10-17"},"policyName":"HeartbeatRunnerServiceRoleDefaultPolicy08E364EE","roles":[{"Ref":"HeartbeatRunnerServiceRole07B33F7E"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/HeartbeatRunner/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/HeartbeatRunner/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/HeartbeatRunner/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/HeartbeatRunner/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"HeartbeatRunnerEA31B930","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"724b3c95c6cd487c828621ad670d23696cd81da614d7df21b846c2d97ef058bf.zip"},"environment":{"variables":{"MESSAGE_QUEUE_URL":{"Ref":"MessageQueue7A3BF959"},"USERS_TABLE_NAME":{"Ref":"UsersTable9725E9C8"}}},"functionName":"agent-claw-heartbeat-runner","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["HeartbeatRunnerServiceRole07B33F7E","Arn"]},"runtime":"python3.12","timeout":60}}}}},"HeartbeatRule":{"id":"HeartbeatRule","path":"AgentClawStack/HeartbeatRule","constructInfo":{"fqn":"aws-cdk-lib.aws_events.Rule","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/HeartbeatRule/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_events.CfnRule","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Events::Rule","aws:cdk:cloudformation:logicalId":"HeartbeatRuleDCC8D7FB","aws:cdk:cloudformation:props":{"name":"agent-claw-heartbeat","scheduleExpression":"rate(30 minutes)","state":"ENABLED","targets":[{"id":"Target0","arn":{"Fn::GetAtt":["HeartbeatRunnerEA31B930","Arn"]}}]}}},"AllowEventRuleAgentClawStackHeartbeatRunner11988F5B":{"id":"AllowEventRuleAgentClawStackHeartbeatRunner11988F5B","path":"AgentClawStack/HeartbeatRule/AllowEventRuleAgentClawStackHeartbeatRunner11988F5B","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"HeartbeatRuleAllowEventRuleAgentClawStackHeartbeatRunner11988F5BB95BE86F","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["HeartbeatRunnerEA31B930","Arn"]},"principal":"events.amazonaws.com","sourceArn":{"Fn::GetAtt":["HeartbeatRuleDCC8D7FB","Arn"]}}}}}},"Scheduler":{"id":"Scheduler","path":"AgentClawStack/Scheduler","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/Scheduler/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Scheduler/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"SchedulerServiceRole62CDA70C","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/Scheduler/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Scheduler/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"SchedulerServiceRoleDefaultPolicyFA0D8235","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"},{"Action":["events:RemoveTargets","events:DeleteRule"],"Effect":"Allow","Resource":"arn:aws:events:us-east-1:495395224548:rule/agent-claw-reminder-*"}],"Version":"2012-10-17"},"policyName":"SchedulerServiceRoleDefaultPolicyFA0D8235","roles":[{"Ref":"SchedulerServiceRole62CDA70C"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/Scheduler/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/Scheduler/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/Scheduler/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/Scheduler/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"SchedulerCFE73206","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"8e7324457a5952eb51f04a34fbc5ba853252e7157d8d8958ac5fda92e72edb1f.zip"},"environment":{"variables":{"TELEGRAM_BOT_TOKEN_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/telegram-bot-token-Oq3in3"}},"functionName":"agent-claw-scheduler","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["SchedulerServiceRole62CDA70C","Arn"]},"runtime":"python3.12","timeout":30}}},"EventBridgeInvoke":{"id":"EventBridgeInvoke","path":"AgentClawStack/Scheduler/EventBridgeInvoke","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"SchedulerEventBridgeInvoke72A0529A","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["SchedulerCFE73206","Arn"]},"principal":"events.amazonaws.com","sourceArn":"arn:aws:events:us-east-1:495395224548:rule/agent-claw-reminder-*"}}}}},"WorkspaceMcpFunctionUrl":{"id":"WorkspaceMcpFunctionUrl","path":"AgentClawStack/WorkspaceMcpFunctionUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"OAuthStartUrl":{"id":"OAuthStartUrl","path":"AgentClawStack/OAuthStartUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"OAuthRedirectUri":{"id":"OAuthRedirectUri","path":"AgentClawStack/OAuthRedirectUri","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"WebhookUrl":{"id":"WebhookUrl","path":"AgentClawStack/WebhookUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"WorkspaceBucketName":{"id":"WorkspaceBucketName","path":"AgentClawStack/WorkspaceBucketName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"SessionTableName":{"id":"SessionTableName","path":"AgentClawStack/SessionTableName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"UsersTableName":{"id":"UsersTableName","path":"AgentClawStack/UsersTableName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"MessageQueueUrl":{"id":"MessageQueueUrl","path":"AgentClawStack/MessageQueueUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"Runtime1RoleArn":{"id":"Runtime1RoleArn","path":"AgentClawStack/Runtime1RoleArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"SchedulerLambdaArn":{"id":"SchedulerLambdaArn","path":"AgentClawStack/SchedulerLambdaArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"CDKMetadata":{"id":"CDKMetadata","path":"AgentClawStack/CDKMetadata","constructInfo":{"fqn":"constructs.Construct","version":"10.6.0"},"children":{"Default":{"id":"Default","path":"AgentClawStack/CDKMetadata/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"2.252.0"}}}},"BootstrapVersion":{"id":"BootstrapVersion","path":"AgentClawStack/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"2.252.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"AgentClawStack/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"2.252.0"}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.6.0"}}}}} \ No newline at end of file +{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"2.252.0"},"children":{"AgentClawStack":{"id":"AgentClawStack","path":"AgentClawStack","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"2.252.0"},"children":{"WorkspaceBucket":{"id":"WorkspaceBucket","path":"AgentClawStack/WorkspaceBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}},"SessionStore":{"id":"SessionStore","path":"AgentClawStack/SessionStore","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.Table","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/SessionStore/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.CfnTable","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::DynamoDB::Table","aws:cdk:cloudformation:logicalId":"SessionStore8C86EEFE","aws:cdk:cloudformation:props":{"attributeDefinitions":[{"attributeName":"actor_id","attributeType":"S"}],"billingMode":"PAY_PER_REQUEST","keySchema":[{"attributeName":"actor_id","keyType":"HASH"}],"tableName":"agent-claw-sessions","timeToLiveSpecification":{"attributeName":"ttl","enabled":true}}}},"ScalingRole":{"id":"ScalingRole","path":"AgentClawStack/SessionStore/ScalingRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}}}},"UsersTable":{"id":"UsersTable","path":"AgentClawStack/UsersTable","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.Table","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/UsersTable/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_dynamodb.CfnTable","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::DynamoDB::Table","aws:cdk:cloudformation:logicalId":"UsersTable9725E9C8","aws:cdk:cloudformation:props":{"attributeDefinitions":[{"attributeName":"actor_id","attributeType":"S"}],"billingMode":"PAY_PER_REQUEST","keySchema":[{"attributeName":"actor_id","keyType":"HASH"}],"tableName":"agent-claw-users"}}},"ScalingRole":{"id":"ScalingRole","path":"AgentClawStack/UsersTable/ScalingRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"}}}},"MessageQueue":{"id":"MessageQueue","path":"AgentClawStack/MessageQueue","constructInfo":{"fqn":"aws-cdk-lib.aws_sqs.Queue","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/MessageQueue/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_sqs.CfnQueue","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SQS::Queue","aws:cdk:cloudformation:logicalId":"MessageQueue7A3BF959","aws:cdk:cloudformation:props":{"contentBasedDeduplication":false,"fifoQueue":true,"queueName":"agent-claw-messages.fifo","receiveMessageWaitTimeSeconds":20,"visibilityTimeout":900}}}}},"TgIngest":{"id":"TgIngest","path":"AgentClawStack/TgIngest","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/TgIngest/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"TgIngestServiceRoleB96980B6","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/TgIngest/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"TgIngestServiceRoleDefaultPolicyCC51E135","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["sqs:SendMessage","sqs:GetQueueAttributes","sqs:GetQueueUrl"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"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":["s3:DeleteObject*","s3:PutObject","s3:PutObjectLegalHold","s3:PutObjectRetention","s3:PutObjectTagging","s3:PutObjectVersionTagging","s3:Abort*"],"Effect":"Allow","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"},"policyName":"TgIngestServiceRoleDefaultPolicyCC51E135","roles":[{"Ref":"TgIngestServiceRoleB96980B6"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/TgIngest/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/TgIngest/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/TgIngest/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/TgIngest/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"TgIngest4CB35C2F","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"e0a834c0c682fe0c528540082d0b587c43ed791927a064104e5eb6c507c0cdb2.zip"},"environment":{"variables":{"MESSAGE_QUEUE_URL":{"Ref":"MessageQueue7A3BF959"},"TELEGRAM_BOT_TOKEN_SSM_PARAM":"/agent-claw/telegram-bot-token","TELEGRAM_WEBHOOK_SECRET":"","ATTACHMENTS_BUCKET_NAME":"agent-claw-workspace-495395224548"}},"functionName":"agent-claw-tg-ingest","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["TgIngestServiceRoleB96980B6","Arn"]},"runtime":"python3.12","timeout":10}}}}},"AgentRunner":{"id":"AgentRunner","path":"AgentClawStack/AgentRunner","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/AgentRunner/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"AgentRunnerServiceRole40CA0A00","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/AgentRunner/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"AgentRunnerServiceRoleDefaultPolicyA584A5CF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["SessionStore8C86EEFE","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["SessionStore8C86EEFE","Arn"]}]},{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::agent-claw-workspace-495395224548"]]},{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::agent-claw-workspace-495395224548/*"]]}]},{"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":["sqs:ReceiveMessage","sqs:ChangeMessageVisibility","sqs:GetQueueUrl","sqs:DeleteMessage","sqs:GetQueueAttributes"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":"bedrock-agentcore:InvokeAgentRuntime","Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"},"policyName":"AgentRunnerServiceRoleDefaultPolicyA584A5CF","roles":[{"Ref":"AgentRunnerServiceRole40CA0A00"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/AgentRunner/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/AgentRunner/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/AgentRunner/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"AgentRunnerBDE3FA56","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"59b4a808a6125020261c01ae23500d036cdde29fdb255aa6a82d61555fde4848.zip"},"environment":{"variables":{"SESSION_TABLE_NAME":{"Ref":"SessionStore8C86EEFE"},"WORKSPACE_BUCKET_NAME":"agent-claw-workspace-495395224548","TELEGRAM_BOT_TOKEN_SSM_PARAM":"/agent-claw/telegram-bot-token","BRAVE_API_KEY_SSM_PARAM":"/agent-claw/brave-api-key","RUNTIME_1_ARN":"arn:aws:bedrock-agentcore:us-east-1:495395224548:runtime/agentclaw_agent_claw_main-vTRGIEG6ON","AWS_REGION_NAME":"us-east-1","USERS_TABLE_NAME":{"Ref":"UsersTable9725E9C8"},"WORKSPACE_MCP_URL":"https://25hugrzw4uwtueeg77jsmft6lq0wunmd.lambda-url.us-east-1.on.aws/mcp"}},"functionName":"agent-claw-agent-runner","handler":"handler.handler","memorySize":256,"role":{"Fn::GetAtt":["AgentRunnerServiceRole40CA0A00","Arn"]},"runtime":"python3.12","timeout":900}}},"SqsEventSource:AgentClawStackMessageQueue9AF4DF23":{"id":"SqsEventSource:AgentClawStackMessageQueue9AF4DF23","path":"AgentClawStack/AgentRunner/SqsEventSource:AgentClawStackMessageQueue9AF4DF23","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.EventSourceMapping","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/AgentRunner/SqsEventSource:AgentClawStackMessageQueue9AF4DF23/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnEventSourceMapping","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::EventSourceMapping","aws:cdk:cloudformation:logicalId":"AgentRunnerSqsEventSourceAgentClawStackMessageQueue9AF4DF234671B32B","aws:cdk:cloudformation:props":{"batchSize":10,"enabled":true,"eventSourceArn":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]},"functionName":{"Ref":"AgentRunnerBDE3FA56"}}}}}}}},"WebhookApi":{"id":"WebhookApi","path":"AgentClawStack/WebhookApi","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpApi","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnApi","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Api","aws:cdk:cloudformation:logicalId":"WebhookApi28122C53","aws:cdk:cloudformation:props":{"name":"agent-claw-webhook","protocolType":"HTTP"}}},"DefaultStage":{"id":"DefaultStage","path":"AgentClawStack/WebhookApi/DefaultStage","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpStage","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/DefaultStage/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnStage","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Stage","aws:cdk:cloudformation:logicalId":"WebhookApiDefaultStageC0BC9CA5","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"autoDeploy":true,"stageName":"$default"}}}}},"POST--telegram":{"id":"POST--telegram","path":"AgentClawStack/WebhookApi/POST--telegram","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"TgIngestIntegration":{"id":"TgIngestIntegration","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramTgIngestIntegration9EE5BB85","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::GetAtt":["TgIngest4CB35C2F","Arn"]},"payloadFormatVersion":"2.0"}}}}},"TgIngestIntegration-Permission":{"id":"TgIngestIntegration-Permission","path":"AgentClawStack/WebhookApi/POST--telegram/TgIngestIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramTgIngestIntegrationPermissionFEBC2E3B","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["TgIngest4CB35C2F","Arn"]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/telegram"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/POST--telegram/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiPOSTtelegramF7127CFF","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"POST /telegram","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiPOSTtelegramTgIngestIntegration9EE5BB85"}]]}}}}}},"GET--oauth--start":{"id":"GET--oauth--start","path":"AgentClawStack/WebhookApi/GET--oauth--start","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"OAuthStartIntegration":{"id":"OAuthStartIntegration","path":"AgentClawStack/WebhookApi/GET--oauth--start/OAuthStartIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/GET--oauth--start/OAuthStartIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthstartOAuthStartIntegrationA546443F","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::GetAtt":["OAuthHandlerC97C2476","Arn"]},"payloadFormatVersion":"2.0"}}}}},"OAuthStartIntegration-Permission":{"id":"OAuthStartIntegration-Permission","path":"AgentClawStack/WebhookApi/GET--oauth--start/OAuthStartIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthstartOAuthStartIntegrationPermission38BAEF6D","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["OAuthHandlerC97C2476","Arn"]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/oauth/start"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/GET--oauth--start/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthstart6DCA713A","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"GET /oauth/start","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiGEToauthstartOAuthStartIntegrationA546443F"}]]}}}}}},"GET--oauth--callback":{"id":"GET--oauth--callback","path":"AgentClawStack/WebhookApi/GET--oauth--callback","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"OAuthCallbackIntegration":{"id":"OAuthCallbackIntegration","path":"AgentClawStack/WebhookApi/GET--oauth--callback/OAuthCallbackIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/GET--oauth--callback/OAuthCallbackIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthcallbackOAuthCallbackIntegrationCFBBEB09","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::GetAtt":["OAuthHandlerC97C2476","Arn"]},"payloadFormatVersion":"2.0"}}}}},"OAuthCallbackIntegration-Permission":{"id":"OAuthCallbackIntegration-Permission","path":"AgentClawStack/WebhookApi/GET--oauth--callback/OAuthCallbackIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthcallbackOAuthCallbackIntegrationPermission6BA3A5AD","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["OAuthHandlerC97C2476","Arn"]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/oauth/callback"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/GET--oauth--callback/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiGEToauthcallbackFC1F6BCD","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"GET /oauth/callback","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiGEToauthcallbackOAuthCallbackIntegrationCFBBEB09"}]]}}}}}},"ANY--workspace--{proxy+}":{"id":"ANY--workspace--{proxy+}","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpRoute","version":"2.252.0"},"children":{"WorkspaceMcpIntegration":{"id":"WorkspaceMcpIntegration","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}/WorkspaceMcpIntegration","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.HttpIntegration","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}/WorkspaceMcpIntegration/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnIntegration","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Integration","aws:cdk:cloudformation:logicalId":"WebhookApiANYworkspaceproxyWorkspaceMcpIntegration7377EE13","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"integrationType":"AWS_PROXY","integrationUri":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":lambda:us-east-1:495395224548:function:agent-claw-workspace-mcp"]]},"payloadFormatVersion":"2.0"}}}}},"WorkspaceMcpIntegration-Permission":{"id":"WorkspaceMcpIntegration-Permission","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}/WorkspaceMcpIntegration-Permission","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"WebhookApiANYworkspaceproxyWorkspaceMcpIntegrationPermission97613ADF","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":lambda:us-east-1:495395224548:function:agent-claw-workspace-mcp"]]},"principal":"apigateway.amazonaws.com","sourceArn":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":execute-api:us-east-1:495395224548:",{"Ref":"WebhookApi28122C53"},"/*/*/workspace/{proxy+}"]]}}}},"Resource":{"id":"Resource","path":"AgentClawStack/WebhookApi/ANY--workspace--{proxy+}/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_apigatewayv2.CfnRoute","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::ApiGatewayV2::Route","aws:cdk:cloudformation:logicalId":"WebhookApiANYworkspaceproxy4455BE19","aws:cdk:cloudformation:props":{"apiId":{"Ref":"WebhookApi28122C53"},"authorizationType":"NONE","routeKey":"ANY /workspace/{proxy+}","target":{"Fn::Join":["",["integrations/",{"Ref":"WebhookApiANYworkspaceproxyWorkspaceMcpIntegration7377EE13"}]]}}}}}}}},"Runtime1Role":{"id":"Runtime1Role","path":"AgentClawStack/Runtime1Role","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Runtime1Role/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"Runtime1RoleA7A82078","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"bedrock-agentcore.amazonaws.com"}}],"Version":"2012-10-17"},"description":"Execution role for agent-claw Runtime 1 (main assistant)"}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/Runtime1Role/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Runtime1Role/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"Runtime1RoleDefaultPolicy1A3D5ACF","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["bedrock:InvokeModel","bedrock:InvokeModelWithResponseStream"],"Effect":"Allow","Resource":"*"},{"Action":["s3:GetObject*","s3:GetBucket*","s3:List*"],"Effect":"Allow","Resource":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::agent-claw-workspace-495395224548"]]},{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":s3:::agent-claw-workspace-495395224548/*"]]}]},{"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":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["bedrock-agentcore:CreateEvent","bedrock-agentcore:ListEvents","bedrock-agentcore:RetrieveMemoryRecords"],"Effect":"Allow","Resource":"*"},{"Action":"lambda:InvokeFunctionUrl","Condition":{"StringEquals":{"lambda:FunctionUrlAuthType":"AWS_IAM"}},"Effect":"Allow","Resource":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":lambda:us-east-1:495395224548:function:agent-claw-workspace-mcp"]]},"Sid":"WorkspaceMcpInvoke"},{"Action":"secretsmanager:GetSecretValue","Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*","Sid":"PerUserGoogleCredentialsReadRuntime"},{"Action":"secretsmanager:ListSecrets","Effect":"Allow","Resource":"*","Sid":"GoogleCredentialsListRuntime"},{"Action":["events:PutRule","events:PutTargets","events:ListRules","events:ListTargetsByRule","events:RemoveTargets","events:DeleteRule"],"Effect":"Allow","Resource":"arn:aws:events:us-east-1:*:rule/agent-claw-reminder-*","Sid":"EventBridgeScheduler"},{"Action":["lambda:AddPermission","lambda:RemovePermission"],"Effect":"Allow","Resource":{"Fn::GetAtt":["SchedulerCFE73206","Arn"]},"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"},"policyName":"Runtime1RoleDefaultPolicy1A3D5ACF","roles":[{"Ref":"Runtime1RoleA7A82078"}]}}}}}}},"WorkspaceMcpRole":{"id":"WorkspaceMcpRole","path":"AgentClawStack/WorkspaceMcpRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"2.252.0"},"children":{"Policy":{"id":"Policy","path":"AgentClawStack/WorkspaceMcpRole/Policy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/WorkspaceMcpRole/Policy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"WorkspaceMcpRolePolicy5B8B0072","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"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":"secretsmanager:GetSecretValue","Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*","Sid":"PerUserGoogleCredentialsRead"}],"Version":"2012-10-17"},"policyName":"WorkspaceMcpRolePolicy5B8B0072","roles":["agent-claw-workspace-mcp-role"]}}}}}}},"WorkspaceMcp":{"id":"WorkspaceMcp","path":"AgentClawStack/WorkspaceMcp","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.FunctionBase","version":"2.252.0"}},"OAuthHandler":{"id":"OAuthHandler","path":"AgentClawStack/OAuthHandler","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/OAuthHandler/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/OAuthHandler/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"OAuthHandlerServiceRole9CDCCF9E","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/OAuthHandler/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/OAuthHandler/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"OAuthHandlerServiceRoleDefaultPolicy69D90416","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"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":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:BatchWriteItem","dynamodb:PutItem","dynamodb:UpdateItem","dynamodb:DeleteItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["secretsmanager:CreateSecret","secretsmanager:PutSecretValue","secretsmanager:GetSecretValue"],"Effect":"Allow","Resource":"arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-credentials/*","Sid":"PerUserGoogleCredentialsWrite"}],"Version":"2012-10-17"},"policyName":"OAuthHandlerServiceRoleDefaultPolicy69D90416","roles":[{"Ref":"OAuthHandlerServiceRole9CDCCF9E"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/OAuthHandler/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/OAuthHandler/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/OAuthHandler/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/OAuthHandler/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"OAuthHandlerC97C2476","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"6a3663dc0651e59d7ac6f549864cf1c084e3fbfc98ebd0479fed6a84075a378e.zip"},"environment":{"variables":{"GOOGLE_OAUTH_CLIENT_SSM_PARAM":"/agent-claw/google-oauth-client","USERS_TABLE_NAME":{"Ref":"UsersTable9725E9C8"},"TELEGRAM_BOT_TOKEN_SSM_PARAM":"/agent-claw/telegram-bot-token","OAUTH_REDIRECT_URI":{"Fn::Join":["",["https://",{"Ref":"WebhookApi28122C53"},".execute-api.us-east-1.",{"Ref":"AWS::URLSuffix"},"/oauth/callback"]]}}},"functionName":"agent-claw-oauth-handler","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["OAuthHandlerServiceRole9CDCCF9E","Arn"]},"runtime":"python3.12","timeout":30}}}}},"HeartbeatRunner":{"id":"HeartbeatRunner","path":"AgentClawStack/HeartbeatRunner","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/HeartbeatRunner/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/HeartbeatRunner/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"HeartbeatRunnerServiceRole07B33F7E","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/HeartbeatRunner/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/HeartbeatRunner/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"HeartbeatRunnerServiceRoleDefaultPolicy08E364EE","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["sqs:SendMessage","sqs:GetQueueAttributes","sqs:GetQueueUrl"],"Effect":"Allow","Resource":{"Fn::GetAtt":["MessageQueue7A3BF959","Arn"]}},{"Action":["dynamodb:BatchGetItem","dynamodb:Query","dynamodb:GetItem","dynamodb:Scan","dynamodb:ConditionCheckItem","dynamodb:DescribeTable"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]},{"Action":["dynamodb:GetRecords","dynamodb:GetShardIterator"],"Effect":"Allow","Resource":[{"Fn::GetAtt":["UsersTable9725E9C8","Arn"]}]}],"Version":"2012-10-17"},"policyName":"HeartbeatRunnerServiceRoleDefaultPolicy08E364EE","roles":[{"Ref":"HeartbeatRunnerServiceRole07B33F7E"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/HeartbeatRunner/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/HeartbeatRunner/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/HeartbeatRunner/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/HeartbeatRunner/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"HeartbeatRunnerEA31B930","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"724b3c95c6cd487c828621ad670d23696cd81da614d7df21b846c2d97ef058bf.zip"},"environment":{"variables":{"MESSAGE_QUEUE_URL":{"Ref":"MessageQueue7A3BF959"},"USERS_TABLE_NAME":{"Ref":"UsersTable9725E9C8"}}},"functionName":"agent-claw-heartbeat-runner","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["HeartbeatRunnerServiceRole07B33F7E","Arn"]},"runtime":"python3.12","timeout":60}}}}},"HeartbeatRule":{"id":"HeartbeatRule","path":"AgentClawStack/HeartbeatRule","constructInfo":{"fqn":"aws-cdk-lib.aws_events.Rule","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/HeartbeatRule/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_events.CfnRule","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Events::Rule","aws:cdk:cloudformation:logicalId":"HeartbeatRuleDCC8D7FB","aws:cdk:cloudformation:props":{"name":"agent-claw-heartbeat","scheduleExpression":"rate(30 minutes)","state":"ENABLED","targets":[{"id":"Target0","arn":{"Fn::GetAtt":["HeartbeatRunnerEA31B930","Arn"]}}]}}},"AllowEventRuleAgentClawStackHeartbeatRunner11988F5B":{"id":"AllowEventRuleAgentClawStackHeartbeatRunner11988F5B","path":"AgentClawStack/HeartbeatRule/AllowEventRuleAgentClawStackHeartbeatRunner11988F5B","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"HeartbeatRuleAllowEventRuleAgentClawStackHeartbeatRunner11988F5BB95BE86F","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["HeartbeatRunnerEA31B930","Arn"]},"principal":"events.amazonaws.com","sourceArn":{"Fn::GetAtt":["HeartbeatRuleDCC8D7FB","Arn"]}}}}}},"Scheduler":{"id":"Scheduler","path":"AgentClawStack/Scheduler","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"2.252.0"},"children":{"ServiceRole":{"id":"ServiceRole","path":"AgentClawStack/Scheduler/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Scheduler/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:logicalId":"SchedulerServiceRole62CDA70C","aws:cdk:cloudformation:props":{"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"AgentClawStack/Scheduler/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"2.252.0"},"children":{"Resource":{"id":"Resource","path":"AgentClawStack/Scheduler/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:logicalId":"SchedulerServiceRoleDefaultPolicyFA0D8235","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"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":["events:RemoveTargets","events:DeleteRule"],"Effect":"Allow","Resource":"arn:aws:events:us-east-1:495395224548:rule/agent-claw-reminder-*"}],"Version":"2012-10-17"},"policyName":"SchedulerServiceRoleDefaultPolicyFA0D8235","roles":[{"Ref":"SchedulerServiceRole62CDA70C"}]}}}}}}},"Code":{"id":"Code","path":"AgentClawStack/Scheduler/Code","constructInfo":{"fqn":"aws-cdk-lib.aws_s3_assets.Asset","version":"2.252.0"},"children":{"Stage":{"id":"Stage","path":"AgentClawStack/Scheduler/Code/Stage","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"2.252.0"}},"AssetBucket":{"id":"AssetBucket","path":"AgentClawStack/Scheduler/Code/AssetBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketBase","version":"2.252.0"}}}},"Resource":{"id":"Resource","path":"AgentClawStack/Scheduler/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Function","aws:cdk:cloudformation:logicalId":"SchedulerCFE73206","aws:cdk:cloudformation:props":{"code":{"s3Bucket":"cdk-hnb659fds-assets-495395224548-us-east-1","s3Key":"1aaec5c60ed6a294ff9b918a707477e0a0e299a850447e7456a8c8091604131b.zip"},"environment":{"variables":{"TELEGRAM_BOT_TOKEN_SSM_PARAM":"/agent-claw/telegram-bot-token"}},"functionName":"agent-claw-scheduler","handler":"handler.handler","memorySize":128,"role":{"Fn::GetAtt":["SchedulerServiceRole62CDA70C","Arn"]},"runtime":"python3.12","timeout":30}}},"EventBridgeInvoke":{"id":"EventBridgeInvoke","path":"AgentClawStack/Scheduler/EventBridgeInvoke","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnPermission","version":"2.252.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::Permission","aws:cdk:cloudformation:logicalId":"SchedulerEventBridgeInvoke72A0529A","aws:cdk:cloudformation:props":{"action":"lambda:InvokeFunction","functionName":{"Fn::GetAtt":["SchedulerCFE73206","Arn"]},"principal":"events.amazonaws.com","sourceArn":"arn:aws:events:us-east-1:495395224548:rule/agent-claw-reminder-*"}}}}},"WorkspaceMcpFunctionUrl":{"id":"WorkspaceMcpFunctionUrl","path":"AgentClawStack/WorkspaceMcpFunctionUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"OAuthStartUrl":{"id":"OAuthStartUrl","path":"AgentClawStack/OAuthStartUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"OAuthRedirectUri":{"id":"OAuthRedirectUri","path":"AgentClawStack/OAuthRedirectUri","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"WebhookUrl":{"id":"WebhookUrl","path":"AgentClawStack/WebhookUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"WorkspaceBucketName":{"id":"WorkspaceBucketName","path":"AgentClawStack/WorkspaceBucketName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"SessionTableName":{"id":"SessionTableName","path":"AgentClawStack/SessionTableName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"UsersTableName":{"id":"UsersTableName","path":"AgentClawStack/UsersTableName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"MessageQueueUrl":{"id":"MessageQueueUrl","path":"AgentClawStack/MessageQueueUrl","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"Runtime1RoleArn":{"id":"Runtime1RoleArn","path":"AgentClawStack/Runtime1RoleArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"SchedulerLambdaArn":{"id":"SchedulerLambdaArn","path":"AgentClawStack/SchedulerLambdaArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"2.252.0"}},"CDKMetadata":{"id":"CDKMetadata","path":"AgentClawStack/CDKMetadata","constructInfo":{"fqn":"constructs.Construct","version":"10.6.0"},"children":{"Default":{"id":"Default","path":"AgentClawStack/CDKMetadata/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"2.252.0"}}}},"BootstrapVersion":{"id":"BootstrapVersion","path":"AgentClawStack/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"2.252.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"AgentClawStack/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"2.252.0"}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.6.0"}}}}} \ No newline at end of file