Launch With AWS
AWSOfficialPublished by AWS
- Skill
- Workflow
- Cloud and DevOps
- Apache-2.0
- Medium risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Launch with AWS provides a structured, automated workflow to migrate full-stack web applications and frontend projects to AWS. It analyzes the source code, generates a migration plan with cost estimates, and produces deployable AWS Blocks infrastructure code. The process includes secure authentication, source code analysis, and a guided execution phase that results in a migration snapshot ready for local application and deployment via generated instructions.
What it does
- Analyzes web application source code to determine migration requirements.
- Generates detailed migration plans and AWS cost estimates.
- Produces AWS Blocks infrastructure code for target applications.
- Handles secure authentication and session management for AWS migration services.
- Provides a guided 3-way merge process to integrate migrated code into existing repositories.
- Generates project-specific deployment instructions via DEPLOY.md.
When to use it
- When you need to move a vibe-coded or frontend web application to AWS.
- When you want to transition from platforms like Lovable, Bolt.new, or Replit to AWS-native infrastructure.
- When you require a structured, step-by-step migration path for static sites, SPAs, or SSR frameworks.
- When you need to estimate the AWS infrastructure costs for hosting your existing web application.
Advantages
- Automates the complex process of infrastructure definition and migration planning.
- Provides clear cost visibility before committing to a migration.
- Maintains security by using encrypted data handling and avoiding AI training on user code.
- Supports a wide range of modern web frameworks and platforms.
- Ensures a clean integration process through git-based migration branches.
Usage examples
- “Migrate my Replit web app to AWS.”
- The agent initiates the authentication flow, analyzes the Replit project structure, and prepares a migration plan for AWS Lambda and Aurora Serverless.
- “How much would it cost to host my Next.js site on AWS?”
- The agent analyzes the project, calculates the estimated monthly AWS costs, and presents a breakdown of the required services.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/aws/launch-with-aws/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip launch-with-aws.zip -d .agents/skills
Documentation by AWS
Launch with AWS
Drives an AWS migration end-to-end using CLI scripts. Takes a user's web application, analyzes it, generates a migration plan with cost estimate, and delivers deployable AWS Blocks infrastructure code.
The AWS MCP server is recommended but is not required. This skill works standalone via its CLI scripts in any agent environment.
Script Invocation
All commands are run via:
python3 scripts/launch_with_aws.py <command> [args...]where scripts/ is relative to this skill directory. The agent MUST set the working directory to the skill root before invoking commands.
Required files: launch_with_aws.py, launch_config.py, auth.py, auth_callback_server.py, launch_api_client.py, archive.py, service model. When loaded via MCP, fetch all and write to a temp directory preserving structure before invoking.
Each command outputs JSON to stdout on success, or exits non-zero with a JSON error on stderr.
Two global flags may be added to any command: --region <region> selects the region the service runs the migration in, and --aws-mcp-url <url> passes the URL of the AWS MCP server the agent is connected to. See step 2.
Dependencies: Python 3.10+ and boto3. The script checks both on startup and exits with a clear error if either is missing.
Supported Application Types
Full-stack apps built with vibe-coding platforms, and frontend web applications and websites (static sites, SPAs, and SSR frameworks with static export).
| Origin Platform | What it covers |
|---|---|
| Lovable | Lovable-generated full-stack apps (React + Supabase) |
| Bolt.new | Bolt.new-generated full-stack apps (React + Supabase) |
| Replit | Replit-hosted full-stack apps (React + Express.js + PostgreSQL) |
| Framework | Examples |
|---|---|
| React ecosystem | React, CRA, Vite + React, Gatsby, Docusaurus |
| Vue ecosystem | Vue, Nuxt (static export), VitePress |
| Angular | Angular |
| Svelte ecosystem | Svelte, SvelteKit (static export) |
| SSR with static export | Next.js, Nuxt, Astro, SvelteKit |
| Other modern frameworks | Astro, Solid, Preact, Lit, Eleventy |
| Vite (generic) | Any Vite-based app |
Other frameworks may also work. If the user's app doesn't match these, see Unsupported Application Handling below.
What Gets Migrated vs. What Stays
Lovable / Bolt.new apps (Supabase-backed):
| Component | What happens |
|---|---|
| Frontend & hosting | Migrated to AWS (S3 + CloudFront + Lambda) |
| Edge functions / server functions | Migrated to AWS Lambda |
| AI calls (e.g. Lovable AI Gateway) | Migrated to Amazon Bedrock |
| Database (Supabase DB) | Stays on Supabase — not migrated |
| Auth (Supabase Auth) | Stays on Supabase — not migrated |
| Storage & Realtime | Stays on Supabase — not migrated |
The app continues to call Supabase for database, auth, storage, and realtime from the AWS-hosted application.
Replit apps (Express.js + PostgreSQL):
| Component | What happens |
|---|---|
| Frontend & hosting | Migrated to AWS (S3 + CloudFront + Lambda) |
| Server logic (Express.js) | Migrated to AWS Lambda (API Gateway) |
| Database (PostgreSQL) | Schema and code migrated to AWS (Aurora Serverless / DynamoDB). Existing data is NOT migrated — customers must export and import their data separately. |
| Auth (Replit Auth) | Code migrated to AWS (Cognito). Existing user accounts are NOT migrated — customers must re-create or invite users in Cognito. |
| Realtime (WebSockets) | Migrated to AWS (API Gateway WebSocket) |
| File storage | Migrated to AWS (S3). Existing files are NOT migrated. |
Replit app infrastructure and code are migrated to AWS-native services, but existing data, user accounts, and files must be migrated separately by the customer.
Input Resolution
Resolve the user's input to a local directory path:
If the user provides a local path: pass that path directly.
If the user provides a GitHub URL: clone the repository locally and use that directory.
If neither is provided: use the current working directory. If it doesn't look like an app directory, ask the user for the path.
Flow
Run the script commands in order, surfacing results to the user at each step:
1. Authentication
python3 scripts/launch_with_aws.py auth-startAlways run first. Returns immediately with JSON:
If already authenticated:
{"authenticated": true, "reusedCachedSession": true, "baseUrl": "..."}If silent refresh succeeded:
{"authenticated": true, "reusedCachedSession": false, "baseUrl": "..."}If interactive sign-in is needed:
{"authenticated": false, "signInUrl": "https://...", "pid": 12345, "port": 54321, "baseUrl": "..."}
When authenticated is false, immediately display the signInUrl to the user (so they can open it in their browser) and call auth-wait in the same response:
python3 scripts/launch_with_aws.py auth-wait <pid>where <pid> is the pid value from the auth-start response. This blocks until the user completes browser sign-in (or times out after 600s). Returns {"authenticated": true, "baseUrl": "..."} on success.
Sessions are capped at 90 days even if the identity provider does not set an expiration; after that the interactive flow is required again.
To check the current session without authenticating, or to sign out:
python3 scripts/launch_with_aws.py session-status
python3 scripts/launch_with_aws.py sign-outsession-status reports whether a session exists and how long until the token and overall session expire. sign-out best-effort revokes the refresh token and deletes the local ~/.launch-with-aws/session.json. On shared or untrusted workstations, run sign-out when finished.
2. Create Launch
For a local directory, present this data-handling confirmation verbatim and wait for explicit approval:
Your source code will be uploaded to the Launch with AWS service in [regionName], [region] ([sourceLabel]) to analyze your application and generate a migration plan. If you later approve execution, an AWS-hosted agent will modify a copy of your source code according to the plan and produce a migrated snapshot for you to download. Your uploaded source code and associated launch data are encrypted in transit and at rest and retained for up to 48 hours for recovery. Your data is never used to train AI models. We exclude Git history, Git-ignored files, and files matching common sensitive-file patterns. Sensitive-file filtering is best effort; review your project for secrets. Continue? Or reply with [otherRegions regionName] to run the migration there instead.
Fill in the region fields from resolve-region, verbatim, and offer each otherRegions entry as an alternative. If the agent is connected to an AWS MCP server, pass that server's URL verbatim. The script reads the region from it and decides whether it counts as a signal.
python3 scripts/launch_with_aws.py resolve-region [--aws-mcp-url https://aws-mcp.<region>.api.aws/mcp]Returns {"region": ..., "regionName": ..., "source": ..., "sourceLabel": ..., "baseUrl": ..., "otherRegions": [{"region": ..., "regionName": ...}]}.
Do NOT call create-launch until the user explicitly confirms; a missing or ambiguous response means no. Naming one of otherRegions is both a region choice and approval — pass its region as --region and do not ask again.
python3 scripts/launch_with_aws.py create-launch <app-directory> [name] --region <region>Zips the directory, uploads it, then creates the launch. Returns the full launch object including launch.launchId and the region it was created in, which becomes the user's saved default. A launch exists only in its own region — pass its --region on every later command.
The launch starts in analyzing status and automatically progresses through analysis and planning.
3. Poll Launch Status
python3 scripts/launch_with_aws.py get-launch-status <launch-id> --region <region>Poll until status is planned (ready for execution) or failed; report any other status to the user. Key status progression:
analyzing→ detecting app type and dependenciesplanning→ generating migration planplanned→ ready for executionexecuting→ deployment in progresscompleted→ donefailed→ checkfailureReason
4. Get Full Launch Details & Confirm
python3 scripts/launch_with_aws.py get-launch <launch-id> plan,cost_estimate --region <region>Get full launch details. Optional second argument is a comma-separated include list: analysis, plan, execution, cost_estimate, download_url.
Present the cost estimate and plan to the user. The costEstimate field in the response contains estimatedMonthlyCost, region, and a services breakdown with per-service costs.
Confirmation Gate — present and wait for explicit approval:
Migration Summary
App type: [detected type from analysis]
Architecture: [target architecture from plan]
Estimated monthly cost: $X.XX/month ([costEstimate.region] pricing)
Migration runs in: [regionName], [region]
Ready to proceed? This will execute the migration in an AWS-managed environment (no cost to you) and produce the migrated snapshot for you to download.
Do NOT call start-launch-execution until the user explicitly confirms.
5. Start Execution
python3 scripts/launch_with_aws.py start-launch-execution <launch-id> --region <region>Starts deployment. Then poll with get-launch-status until status is completed or failed. Sleep at least 30 seconds between polls.
6. Download
python3 scripts/launch_with_aws.py get-launch-download-url <launch-id> --region <region>Always present the full download URL to the user — they may need it to download the migrated snapshot directly or for reference.
7. List or Delete Launches
python3 scripts/launch_with_aws.py list-launches
python3 scripts/launch_with_aws.py delete-launch <launch-id> --region <region>list-launches queries both regions and returns items with a region on each launch; add --region to query one. A region that cannot be reached is reported under errors instead of failing the command, unless every queried region fails, in which case the command exits with that error.
8. Post-Migration: Apply Migrated Code Locally
After obtaining the download URL (adapt commands for the user's platform if not POSIX):
Step A: Download and unpack
curl -L -o /tmp/migration-snapshot.zip "<download_url>"
mkdir -p /tmp/migration-output
unzip -o /tmp/migration-snapshot.zip -d /tmp/migration-outputStep B: Prepare the local workspace
Ensure the user's working directory is clean:
cd <user-app-directory>
git statusIf there are uncommitted changes, ask the user to commit or stash first. Do NOT proceed with a dirty working tree.
Step C: Apply migration (3-way merge)
Create a migration branch and overlay the migrated files:
cd <user-app-directory>
git checkout -b aws-migration
rsync -a /tmp/migration-output/ .
git status
git diff --statReview the changes with the user. Key additions to highlight:
aws-blocks/— AWS Blocks infrastructure definitionDEPLOY.md— deployment instructionsAny modified config files
If there are conflicts with the user's existing files, present them and ask how to resolve.
Step D: Follow DEPLOY.md
Read the DEPLOY.md file in the project root and follow its instructions to deploy the app to the user's AWS account. Typical steps:
AWS authentication (
aws login --profile aws-migrate --region us-east-1)CDK bootstrap (first-time only):
npm install && npx cdk bootstrapDeploy:
npx cdk deploy --all --progress eventsVerify the CloudFront URL that CDK prints on completion.
Important: Always read DEPLOY.md from the migrated output — it is generated specifically for this app and architecture. Do not assume deployment steps from memory.
Unsupported Application Handling
If a launch fails during analysis with a failureReason indicating an unsupported app type (or the user's stack doesn't match the supported list):
Tell the user: "This app type isn't directly supported by Launch with AWS yet. Let me search for other skills that can help deploy this kind of application."
Search for relevant skills based on the app type (e.g.
aws-serverless,aws-containers,aws-database,aws-deployment,aws-cdk,aws-ai-ml).
Files
- ATTRIBUTION.mdAdded by AutoGineer895 B
- LICENSELicence file added by AutoGineer9.9 KiB
- NOTICELicence file added by AutoGineer67 B
- SKILL.md13.4 KiB
- references/launchwithaws-2026-06-15.json34.9 KiB
- scripts/archive.pyScript16.2 KiB
- scripts/auth.pyScript16.1 KiB
- scripts/auth_callback_server.pyScript2.9 KiB
- scripts/launch_api_client.pyScript9.7 KiB
- scripts/launch_config.pyScript12.6 KiB
- scripts/launch_with_aws.pyScript12.7 KiB
scripts/auth.py
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""IAM Identity Center PKCE authentication.
Uses SSO OIDC directly:
1. RegisterClient -> obtain clientId/clientSecret
2. PKCE authorize via browser -> capture auth code on loopback
3. CreateToken (authorization_code grant) -> access + refresh tokens
4. CreateToken (refresh_token grant) -> silent refresh
Tokens cached in ~/.launch-with-aws/session.json (mode 0600).
"""
import base64
import hashlib
import html
import json
import os
import secrets
import signal
import subprocess
import sys
import threading
import time
from dataclasses import asdict
from http.server import BaseHTTPRequestHandler
from typing import Any, Optional, Tuple
from urllib.parse import parse_qs, urlencode, urlparse
from launch_config import (
AUTH_WAIT_POLL_INTERVAL_SECS,
CALLBACK_TIMEOUT_SECS,
CLIENT_NAME,
DEFAULT_TOKEN_LIFETIME_SECS,
ENV_SCOPES,
MAX_SESSION_LIFETIME_SECS,
SCOPES,
SESSION_DIR,
SESSION_FILE_NAME,
SSO_OIDC_REGION,
TOKEN_EXPIRY_BUFFER_SECS,
ClientCredentials,
StoredSession,
resolve_issuer_url,
write_private_file,
)
__version__ = "0.1.0"
class SessionExpiredError(Exception):
"""Raised when the session cannot be refreshed non-interactively."""
pass
def _session_dir() -> str:
return os.path.expanduser(SESSION_DIR)
def _session_file() -> str:
return os.path.join(_session_dir(), SESSION_FILE_NAME)
def _create_sso_oidc_client(region: str) -> Any:
import boto3
from botocore.config import Config as BotoConfig
config = BotoConfig(user_agent_extra=f"md/awslabs#launch-with-aws#{__version__}")
return boto3.client("sso-oidc", region_name=region, config=config)
def _generate_pkce() -> Tuple[str, str]:
code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b"=").decode("ascii")
digest = hashlib.sha256(code_verifier.encode("ascii")).digest()
code_challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
return code_verifier, code_challenge
def _generate_state() -> str:
return secrets.token_hex(16)
# ── Session persistence ──────────────────────────────────────────────────
def load_session() -> Optional[StoredSession]:
try:
with open(_session_file()) as f:
return StoredSession.from_json(f.read())
except (OSError, ValueError, TypeError, KeyError):
return None
def save_session(session: StoredSession) -> None:
write_private_file(_session_file(), session.to_json(indent=2))
def has_valid_session() -> bool:
session = load_session()
return bool(session and session.token_expires_at > int(time.time()) + TOKEN_EXPIRY_BUFFER_SECS)
# ── Client registration ──────────────────────────────────────────────────
def _resolve_scopes() -> list[str]:
env_val = os.environ.get(ENV_SCOPES)
if env_val:
return [s.strip() for s in env_val.split(",") if s.strip()]
return list(SCOPES)
def _register_client() -> ClientCredentials:
sso_oidc = _create_sso_oidc_client(SSO_OIDC_REGION)
scopes = _resolve_scopes()
kwargs: dict[str, Any] = {
"clientName": CLIENT_NAME,
"clientType": "public",
"grantTypes": ["authorization_code", "refresh_token"],
"scopes": scopes,
"issuerUrl": resolve_issuer_url(),
"redirectUris": ["http://127.0.0.1"],
}
response = sso_oidc.register_client(**kwargs)
client_id = response.get("clientId")
client_secret = response.get("clientSecret")
if not client_id or not client_secret:
raise RuntimeError("Client registration response missing credentials")
# Cap the local session lifetime at MAX_SESSION_LIFETIME_SECS. A
# clientSecretExpiresAt of 0 means the IdP set no expiry; otherwise use the
# sooner of the IdP value and the cap.
expires_at_sec = response.get("clientSecretExpiresAt") or 0
now = int(time.time())
cap = now + MAX_SESSION_LIFETIME_SECS
client_expires_at = cap if expires_at_sec == 0 else min(int(expires_at_sec), cap)
return ClientCredentials(
client_id=client_id,
client_secret=client_secret,
client_expires_at=client_expires_at,
authorize_endpoint=f"https://oidc.{SSO_OIDC_REGION}.amazonaws.com/authorize",
token_endpoint=f"https://oidc.{SSO_OIDC_REGION}.amazonaws.com/token",
scopes=scopes,
)
# ── Token exchange ────────────────────────────────────────────────────────
def _exchange_code(
credentials: ClientCredentials,
code: str,
code_verifier: str,
redirect_uri: str,
) -> Tuple[str, str, int]:
sso_oidc = _create_sso_oidc_client(SSO_OIDC_REGION)
response = sso_oidc.create_token(
clientId=credentials.client_id,
clientSecret=credentials.client_secret,
grantType="authorization_code",
code=code,
codeVerifier=code_verifier,
redirectUri=redirect_uri,
)
access_token = response.get("accessToken")
refresh_token = response.get("refreshToken")
if not access_token or not refresh_token:
raise RuntimeError("Token exchange response missing tokens")
expires_in = response.get("expiresIn") or DEFAULT_TOKEN_LIFETIME_SECS
return access_token, refresh_token, int(time.time()) + expires_in
def _refresh_token(session: StoredSession) -> Tuple[str, str, int]:
sso_oidc = _create_sso_oidc_client(SSO_OIDC_REGION)
response = sso_oidc.create_token(
clientId=session.client_id,
clientSecret=session.client_secret,
grantType="refresh_token",
refreshToken=session.refresh_token,
)
access_token = response.get("accessToken")
if not access_token:
raise RuntimeError("Token refresh response missing tokens")
refresh_token = response.get("refreshToken") or session.refresh_token
expires_in = response.get("expiresIn") or DEFAULT_TOKEN_LIFETIME_SECS
return access_token, refresh_token, int(time.time()) + expires_in
# ── Loopback callback server ─────────────────────────────────────────────
class _OAuthState:
def __init__(self, expected_state: str) -> None:
self.expected_state = expected_state
self.auth_code: Optional[str] = None
self.error: Optional[str] = None
self.code_received = threading.Event()
class _CallbackHandler(BaseHTTPRequestHandler):
def _respond(self, status: int, message: str) -> None:
self.send_response(status)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
body = f'<html><head><meta charset="utf-8"></head><body><h2>{html.escape(message)}</h2></body></html>'
self.wfile.write(body.encode("utf-8"))
def do_GET(self) -> None: # noqa: N802
state: _OAuthState = self.server.oauth_state # type: ignore[attr-defined]
params = parse_qs(urlparse(self.path).query)
error = params.get("error", [None])[0]
if error:
desc = params.get("error_description", [error])[0]
self._respond(400, f"Authorization failed: {desc}")
state.error = f"Authorization denied: {desc}"
state.code_received.set()
return
returned_state = params.get("state", [None])[0]
code = params.get("code", [None])[0]
if returned_state != state.expected_state:
if not code:
self.send_response(204)
self.end_headers()
return
self._respond(400, "State mismatch")
state.error = "OAuth state mismatch - possible CSRF"
state.code_received.set()
return
if not code:
self.send_response(204)
self.end_headers()
return
self._respond(200, "Authenticated - you can close this tab.")
state.auth_code = code
state.code_received.set()
def log_message(self, format: str, *args: object) -> None:
pass
# ── Public API ────────────────────────────────────────────────────────────
def get_access_token() -> str:
"""Return a valid Bearer access token (non-interactive).
Tries the cached token and silent refresh. If neither works, raises
SessionExpiredError so the caller can direct the user to auth-start.
"""
session = load_session()
now = int(time.time())
# 1. Valid cached access token.
if session and session.token_expires_at > now + TOKEN_EXPIRY_BUFFER_SECS:
return session.access_token
# 2. Try silent refresh.
if session and session.client_expires_at > now:
try:
access_token, refresh_tok, token_expires_at = _refresh_token(session)
updated = StoredSession(
**{
**asdict(session),
"access_token": access_token,
"refresh_token": refresh_tok,
"token_expires_at": token_expires_at,
}
)
save_session(updated)
return updated.access_token
except Exception:
pass
raise SessionExpiredError("Session expired or not authenticated. Run auth-start to sign in.")
def session_status() -> dict:
"""Report the local session state without triggering authentication.
Returns a dict describing whether a session exists, whether its access
token is currently usable, and how long until the access token and the
overall session registration expire (seconds, clamped at 0).
"""
session = load_session()
if not session:
return {"authenticated": False}
now = int(time.time())
token_valid = session.token_expires_at > now + TOKEN_EXPIRY_BUFFER_SECS
return {
"authenticated": token_valid,
"tokenExpiresInSecs": max(0, session.token_expires_at - now),
"sessionExpiresInSecs": max(0, session.client_expires_at - now),
"canRefresh": session.client_expires_at > now,
}
def sign_out() -> dict:
"""Sign out: delete the local session so the next call requires re-auth.
Best-effort revokes the refresh token, then removes the local session file.
Revocation failures do not block local deletion.
"""
session = load_session()
if session and session.refresh_token:
try:
sso_oidc = _create_sso_oidc_client(SSO_OIDC_REGION)
# revoke_token is not exposed on all endpoints; call it only when
# available.
revoke = getattr(sso_oidc, "revoke_token", None)
if callable(revoke):
revoke(
clientId=session.client_id,
clientSecret=session.client_secret,
token=session.refresh_token,
tokenTypeHint="refresh_token",
)
except Exception:
pass
removed = False
try:
os.unlink(_session_file())
removed = True
except FileNotFoundError:
pass
return {"signedOut": True, "sessionRemoved": removed}
# ── Non-blocking auth (auth-start / auth-wait) ──────────────────────────
def start_auth() -> dict:
"""Attempt auth non-blockingly. Returns immediately.
Returns:
- {authenticated: True, reusedCachedSession: True} if cached token valid
- {authenticated: True, reusedCachedSession: False} if silent refresh worked
- {authenticated: False, signInUrl: ..., pid: N, port: N} if interactive needed
"""
session = load_session()
now = int(time.time())
if session and session.token_expires_at > now + TOKEN_EXPIRY_BUFFER_SECS:
return {"authenticated": True, "reusedCachedSession": True}
if session and session.client_expires_at > now:
try:
access_token, refresh_tok, token_expires_at = _refresh_token(session)
updated = StoredSession(
**{
**asdict(session),
"access_token": access_token,
"refresh_token": refresh_tok,
"token_expires_at": token_expires_at,
}
)
save_session(updated)
return {"authenticated": True, "reusedCachedSession": False}
except Exception:
pass
credentials: ClientCredentials
if session and session.client_expires_at > now:
credentials = session
else:
credentials = _register_client()
code_verifier, code_challenge = _generate_pkce()
state_value = _generate_state()
server_script = os.path.join(os.path.dirname(__file__), "auth_callback_server.py")
# Configuration values are passed to the child over stdin as a JSON blob.
proc = subprocess.Popen(
[
sys.executable,
server_script,
"--state",
state_value,
"--timeout",
str(int(CALLBACK_TIMEOUT_SECS)),
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
start_new_session=True,
)
# Write the config to the child over stdin, then close it so the child sees EOF.
assert proc.stdin is not None
proc.stdin.write(
json.dumps(
{
"client_id": credentials.client_id,
"client_secret": credentials.client_secret,
"client_expires_at": credentials.client_expires_at,
"scopes": credentials.scopes,
"code_verifier": code_verifier,
}
).encode()
)
proc.stdin.close()
# Child writes port to stdout once bound.
assert proc.stdout is not None
assert proc.stderr is not None
port_line = proc.stdout.readline().decode().strip()
proc.stdout.close()
if not port_line.isdigit():
proc.terminate()
err_output = proc.stderr.read().decode().strip()
proc.stderr.close()
detail = err_output or port_line or "no output"
raise RuntimeError(f"Callback server failed to start: {detail}")
proc.stderr.close()
port = int(port_line)
redirect_uri = f"http://127.0.0.1:{port}"
authorize_url = f"{credentials.authorize_endpoint}?" + urlencode(
{
"response_type": "code",
"client_id": credentials.client_id,
"redirect_uri": redirect_uri,
"state": state_value,
"code_challenge": code_challenge,
"code_challenge_method": "S256",
"scope": " ".join(credentials.scopes),
}
)
return {
"authenticated": False,
"signInUrl": authorize_url,
"pid": proc.pid,
"port": port,
}
def wait_for_auth(pid: int, timeout: Optional[float] = None) -> dict:
"""Block until the background callback server completes auth.
Args:
pid: PID of the background callback server (from start_auth).
timeout: Max seconds to wait (default: CALLBACK_TIMEOUT_SECS).
Returns:
{authenticated: True} on success.
Raises:
TimeoutError if timeout exceeded.
RuntimeError if the server exited without completing auth.
"""
timeout = timeout or CALLBACK_TIMEOUT_SECS
deadline = time.time() + timeout
while time.time() < deadline:
if has_valid_session():
return {"authenticated": True}
try:
os.kill(pid, 0)
except OSError:
if has_valid_session():
return {"authenticated": True}
raise RuntimeError(
"Auth callback server exited without completing authentication. "
"Run auth-start again."
)
time.sleep(AUTH_WAIT_POLL_INTERVAL_SECS)
try:
os.kill(pid, signal.SIGTERM)
except OSError:
pass
raise TimeoutError(f"Authentication timed out after {int(timeout)} seconds")
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server, Needs a command-line tool, Needs Python, Needs Node.js
Security
What our scanner noticed· Medium risk
- Phrasing that asks for actions without confirmation
- References to credential files
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- AWS
- Publisher
- AWS (Official)
- Source
- aws/agent-toolkit-for-aws · skills/core-skills/launch-with-aws · commit 0a218f8
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- NOTICE (added by AutoGineer from NOTICE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Also distributed in
- aws/agent-toolkit-for-aws
AutoGineer is not affiliated with or endorsed by AWS.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download launch-with-aws.zip