This is a React + TypeScript + Vite dashboard for managing task records used by the remote Lambda build agent.
- Tailwind CSS setup using the Vite plugin
- Tailwind utility-based component styling across the UI
- root
lambda.envfile in bracket-section format matching the Lambda filenames - Python Lambda CRUD handlers in
/lambdas
- React
- TypeScript
- Vite
- Tailwind CSS
react-oidc-context
npm install
cp .env.example .env
npm run devBuild with:
npm run buildVITE_API_BASE_URL=https://your-api-id.execute-api.your-region.amazonaws.com/prod
VITE_OIDC_AUTHORITY=https://your-cognito-domain.auth.your-region.amazoncognito.com
VITE_OIDC_CLIENT_ID=your-client-id
VITE_OIDC_REDIRECT_URI=http://localhost:5173
VITE_OIDC_POST_LOGOUT_REDIRECT_URI=http://localhost:5173
VITE_OIDC_SCOPE=openid email profileThe root lambda.env file follows the same bracketed section style as your uploaded example. Each section name matches the Lambda filename in /lambdas, plus a shared section.
Example sections:
[shared][list_tasks][get_task][create_task][update_task][patch_task_status][delete_task]
Returns:
{ "tasks": [] }Returns one task record.
Creates a new task.
Updates an existing task.
Updates only the task status.
Example body:
{
"status": {
"flag": "paused",
"humanStopRequested": true
}
}Deletes the task record.
lambdas/list_tasks.pylambdas/get_task.pylambdas/create_task.pylambdas/update_task.pylambdas/patch_task_status.pylambdas/delete_task.py
Each task is stored in S3 as:
tasks/{taskId}.json
At minimum, each Lambda needs:
TASK_BUCKET=your-task-bucket
TASK_PREFIX=tasks/This repository now supports a TradeboxAPI-style Lambda zip deployment flow adapted to the existing dashboard layout:
- Lambda source remains in the existing
lambdas/root instead of being moved to a newlambda/directory. - Because the current repo has a flat Lambda layout,
deploy_config.jsonmaps those handlers into one deployable module namedtask_controllerwith AWS function names liketask_controller_list_tasks. lambdas/lambda_envis the deployment environment file.[shared]applies to every handler, and handler-specific sections such as[list_tasks]override shared values.lambdas/lambda_permissions.jsondefines the execution-role managed policies and inline IAM statements for the module.lambdas/api-routes.jsonmaps HTTP API routes to Lambda handlers.scripts/deploy_lambdas.pypackages each selected handler intobuild/lambdas/{module}/{function}.zip, creates or updates the module IAM role, updates Lambda code/configuration, publishes an immutable version, and moves the configured alias such asdev.scripts/deploy_api_routes.pycreates or updates API Gateway v2 HTTP API integrations/routes and points integrations at Lambda aliases through the stage variablelambdaAlias..github/workflows/deploy-dev-lambdas.ymldeploys on pushes tomainthat touch Lambda deployment files and can also be run manually. It uses GitHub OIDC viaaws-actions/configure-aws-credentials@v4.
Secrets required for the development deployment workflow:
AWS_REGION- AWS region for Lambda, IAM, SQS, and API Gateway operations.AWS_ACCOUNT_ID- AWS account that owns the Lambda functions and HTTP API.AWS_DEPLOY_ROLE_ARN- IAM role assumed by GitHub Actions through OIDC.API_GATEWAY_ID- Existing API Gateway v2 HTTP API id.API_DEFAULT_AUTHORIZER_ID- Default JWT authorizer id used by routes withauth: true.TASK_BUCKET- S3 bucket used by the task CRUD Lambdas.
Variables:
CORS_ORIGIN- CORS origin passed to Lambda environments. Defaults tohttp://localhost:5173in the workflow if unset.
The GitHub OIDC deploy role needs permissions for these AWS API families:
- IAM role management for the per-module Lambda execution role:
iam:GetRole,iam:CreateRole,iam:UpdateAssumeRolePolicy,iam:PutRolePolicy,iam:AttachRolePolicy, andiam:PassRolescoped toDeveloperModeAdminLambdaRole-*. - Lambda deployment:
lambda:GetFunction,lambda:CreateFunction,lambda:UpdateFunctionCode,lambda:UpdateFunctionConfiguration,lambda:PublishVersion,lambda:GetAlias,lambda:CreateAlias,lambda:UpdateAlias,lambda:AddPermission,lambda:ListEventSourceMappings,lambda:CreateEventSourceMapping, andlambda:UpdateEventSourceMappingscoped totask_controller_*functions. - API Gateway v2 route deployment:
apigateway:GET,apigateway:POST,apigateway:PATCH, andapigateway:PUTon the HTTP API and its stages/routes/integrations. - SQS lookup and trigger setup when
deploy_sqs_triggersis enabled:sqs:GetQueueUrlandsqs:GetQueueAttributesfor referenced queues. - Task chat uses a lightweight
DeveloperModeTaskMessageQueueevent source for planning replies, while full task execution continues to use the existing engine task queue. Create the message queue before enabling SQS trigger deployment. - STS identity lookup:
sts:GetCallerIdentity.
Edit deploy_config.json to control scope before running deployment.
Deploy all configured functions in the existing flat module:
{
"active_modules": ["task_controller"],
"functions": { "task_controller": "*" },
"deploy_lambdas": true,
"deploy_api_routes": true,
"deploy_sqs_triggers": false
}Deploy one function only:
{
"active_modules": ["task_controller"],
"functions": { "task_controller": ["list_tasks"] },
"deploy_lambdas": true,
"deploy_api_routes": true,
"deploy_sqs_triggers": false
}Lambda-only deployment:
{
"deploy_lambdas": true,
"deploy_api_routes": false,
"deploy_sqs_triggers": false
}API-only deployment:
{
"deploy_lambdas": false,
"deploy_api_routes": true,
"deploy_sqs_triggers": false
}SQS-trigger-only mode after functions already exist:
{
"deploy_lambdas": false,
"deploy_api_routes": false,
"deploy_sqs_triggers": true
}No-SQS mode is the default: keep deploy_sqs_triggers set to false.
Set the required environment variables locally, then run:
python -m pip install boto3 botocore
python scripts/deploy_lambdas.py --deploy-config deploy_config.json
python scripts/deploy_api_routes.py --deploy-config deploy_config.json --stage dev --lambda-alias devThe task chat screen treats task.json as the live source of truth while the engine worker runs. It polls the project task endpoint while a task has an active engine flag/phase, then renders a distinct engine progress panel from these fields:
task.status.flag— identifies queued, active, and terminal states such asengine_running,complete,error,awaiting_review, andstopped.task.status.phase— shows the current engine phase, includingstarting,connected,indexing,thinking,doing,building,build_failed,deploy_failed,deploying,deployed,checking, andcontinuing.task.status.message— provides the current human-readable engine update.task.status.lastError— displays a terminal or in-progress engine error without dumping raw logs.task.status.updatedAt— labels when the latest task status was written.task.progress.iteration— shows the current engine iteration when present.task.progress.history[]— contributes the latest concise progress entries, including thinking summaries, action results, build/deploy results, completion checks, incomplete/complete run messages, and errors. Long or sensitive-looking output is truncated/redacted before display.
DeveloperModeAdmin supports two project types:
remote_ec2— the existing autonomous EC2 workflow. MissingprojectTypevalues are treated asremote_ec2for backward compatibility.codex_cloud— a review-oriented Codex Cloud workflow routed through Codex submission and polling workers in the separateDeveloperModeWorkersrepository.
The execution paths stay separate:
remote_ec2 -> TASK_QUEUE_URL -> autonomous EC2 engine worker -> SSH/SFTP -> build -> deploy
codex_cloud -> CODEX_TASK_QUEUE_URL -> Codex submit worker -> runner EC2 -> Codex Cloud -> poll worker -> S3 task.json updates
The frontend never selects queues and never receives raw SSH private keys, AWS credentials, Codex CLI credentials, or Codex tokens. It may display safe per-project connection references such as SSH host/user/port/path and whether a secret reference is configured. The backend routes by projectType.
The React API client continues to use the existing project-scoped routes:
GET /projects
GET /projects/{projectId}
POST /projects
PUT /projects/{projectId}
DELETE /projects/{projectId}
GET /projects/{projectId}/tasks
GET /projects/{projectId}/tasks/{taskId}
POST /projects/{projectId}/tasks
PUT /projects/{projectId}/tasks/{taskId}
DELETE /projects/{projectId}/tasks/{taskId}
POST /projects/{projectId}/tasks/{taskId}/messages
POST /projects/{projectId}/tasks/{taskId}/promote
No route accepts a frontend-selected SQS queue.
Both remote_ec2 and codex_cloud project records share the same top-level Controller App fields:
name
description
projectType
sshHost
sshPort
sshUser
sshPrivateKeySecretName
projectPath
publicUrl
engineInstructions
notes
conventions
The fields intentionally stay top-level and are not nested under codex. SSH private keys remain in AWS Secrets Manager; the Controller App stores only a secret name or ARN reference and must never receive or expose raw private-key material. Project-level engine instructions, notes, and conventions remain available for both project types and are included in safe task context and Codex prompt composition.
For remote_ec2, sshHost is the project server and projectPath is the source-code folder that the remote engine reads, edits, builds, and deploys. For codex_cloud, sshHost is the shared or project-specific EC2 host where the authenticated Codex CLI bridge is installed, and projectPath is that bridge application path, typically /opt/DevMode_CodexCLIRunner. The target GitHub repository is selected by the Codex Cloud environment ID, not by the runner path.
Codex Cloud projects additionally store only this Codex-specific project config:
{
"codex": {
"environmentId": "env_example"
}
}A complete Codex Cloud project record therefore looks like:
{
"projectId": "example-codex-project",
"name": "Example Codex Project",
"description": "Managed through Codex Cloud",
"projectType": "codex_cloud",
"sshHost": "172.31.36.254",
"sshPort": 22,
"sshUser": "ubuntu",
"sshPrivateKeySecretName": "developer-mode/projects/example-codex-project/ssh-private-key",
"projectPath": "/opt/DevMode_CodexCLIRunner",
"publicUrl": "https://example.test",
"engineInstructions": "Use the repository AGENTS.md and report changes clearly.",
"notes": [],
"conventions": [],
"codex": {
"environmentId": "env_example"
}
}Worker-wide polling cadence, malformed-poll limits, retry defaults, post-completion handling, direct SSH execution, and Codex CLI credentials are not Controller App configuration. They remain owned by the separate DeveloperModeWorkers repository.
This Controller App repo retains only one Codex-specific environment variable for handoff to the workers repo:
CODEX_TASK_QUEUE_URL=[CODEX_TASK_QUEUE_URL]
When a Codex Cloud task is promoted or explicitly queued with /run, /queue, or /start, the API Lambda composes a prompt from the task title, goal, notes, success criteria, relevant planning conversation, and project-level safe instructions. The prompt is written to S3 at:
tasks/<projectId>/<taskId>/codex-prompt.txt
The authoritative task JSON stores task.codex.promptS3Key. The SQS message sent to CODEX_TASK_QUEUE_URL is a pointer only:
{
"taskBucket": "<task bucket>",
"taskKey": "tasks/<projectId>/<taskId>.json",
"projectId": "<projectId>",
"taskId": "<taskId>"
}Do not put full prompts or secrets in SQS messages or logs.
Task status remains a nested object. Handlers must use task.status.flag and must never replace task.status with a string.
Codex lifecycle flags include:
queued
submitting_to_codex
waiting_for_codex
codex_running
codex_completed
codex_failed
completed
failed
The initial API queueing status for Codex Cloud is:
{
"flag": "queued",
"phase": "codex_queued",
"message": "Task queued for Codex Cloud.",
"updatedAt": "<timestamp>",
"isComplete": false
}Codex-specific task data lives under task.codex, for example:
{
"promptS3Key": "tasks/<projectId>/<taskId>/codex-prompt.txt",
"taskType": "investigation",
"environmentId": "env_example",
"runnerJobId": "<local-runner-job-id>",
"codexTaskId": "<external-codex-cloud-task-id>",
"codexTaskUrl": "https://...",
"submissionStatus": "submitted",
"submittedAt": "...",
"lastCheckedAt": "...",
"completedAt": "...",
"summary": "...",
"error": "..."
}runnerJobId is the local CLI-runner lookup key. codexTaskId is the external Codex Cloud task identifier.
Codex polling is owned by the separate DeveloperModeWorkers repository. The React app independently polls this API roughly every 3.5 seconds while active task flags are present so it can display worker-written S3 task status updates.
codex_completed means Codex Cloud has completed and produced something to review. It does not mean this dashboard merged, pulled, built, deployed, published, or made changes live.
Add these values to lambdas/lambda_env before deployment:
CODEX_TASK_QUEUE_URL=[CODEX_TASK_QUEUE_URL]
Do not commit real queue URLs or tokens unless the repository intentionally uses placeholder substitution for them. This repo does not require Codex runner host/user/path placeholders, runner-key placeholders, polling settings, retry settings, or post-completion policy settings; per-project connection details live in project JSON and worker-wide policy lives in DeveloperModeWorkers.
The API Lambda execution role needs:
- Existing S3 write access under
tasks/*to store Codex prompt text and task JSON. sqs:SendMessageto the Codex submission queue referenced byCODEX_TASK_QUEUE_URL.
This API repository should not add permissions for Codex polling queue consumption, SSH access, Secrets Manager runner-key reads, or Codex CLI credentials. Those belong to DeveloperModeWorkers.
Before deployment, manually create or confirm:
- The Codex submission SQS queue and its ARN.
- The Codex worker deployment and its own IAM permissions, including any SSH, Secrets Manager, polling, retry, and runner-submission configuration it needs.
- Any Codex Cloud environment IDs referenced by projects.
Recommended checks before deployment:
python -m pytest tests/test_codex_cloud.py
python -m py_compile $(find lambdas -name '*.py' -print)
npm run build
python -m json.tool deploy_config.json >/dev/null
python -m json.tool lambdas/lambda_permissions.json >/dev/nullRun deployment preflight scripts only when you intend to inspect deployment readiness. Do not deploy unless explicitly requested.