{"_id":"@daniyalfaraz2003/ectl","name":"@daniyalfaraz2003/ectl","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@daniyalfaraz2003/ectl","version":"1.0.0","description":"Project-local CLI to run long-lived tasks on AWS EC2","type":"module","bin":{"ectl":"dist/index.js"},"engines":{"node":">=22"},"scripts":{"build":"tsc","dev":"tsx src/index.ts","start":"node dist/index.js","typecheck":"tsc --noEmit","test":"vitest run","prepublishOnly":"npm run build && npm test"},"keywords":["aws","ec2","cli","ectl","ec2-task-launcher"],"author":{"name":"Daniyal Faraz"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/DaniyalFaraz2003/AWS-Job-Runner.git"},"homepage":"https://github.com/DaniyalFaraz2003/AWS-Job-Runner#readme","bugs":{"url":"https://github.com/DaniyalFaraz2003/AWS-Job-Runner/issues"},"dependencies":{"@aws-sdk/client-ec2":"^3.989.0","@aws-sdk/client-ssm":"^3.1079.0","@inquirer/prompts":"^7.5.4","archiver":"^7.0.1","chalk":"^5.4.1","cli-table3":"^0.6.5","commander":"^13.1.0","ignore":"^7.0.4","node-ssh":"^13.2.1","ora":"^8.2.0","zod":"^3.25.76"},"devDependencies":{"@types/archiver":"^6.0.3","@types/node":"^22.15.30","aws-sdk-client-mock":"^4.1.0","tsx":"^4.19.4","typescript":"^5.8.3","vitest":"^3.2.4"},"gitHead":"38a9a2e3dc40c5f4f47a942a28616f9462e149c8","_id":"@daniyalfaraz2003/ectl@1.0.0","_nodeVersion":"26.3.1","_npmVersion":"11.16.0","dist":{"integrity":"sha512-c9f9bjIHB3cFUY2NJ+melFl/lGOy5Ay9g6AkbgioPsKryKF3fPSJ0Alj2mAPF8MprwynrZIalTaHUGimM6XS3w==","shasum":"c6d6d1ecf556c847ed7646bb49b7e468ff4fab31","tarball":"https://registry.npmjs.org/@daniyalfaraz2003/ectl/-/ectl-1.0.0.tgz","fileCount":307,"unpackedSize":560839,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCpN7K5IdrOk5rNbVKhzo4LHyK6Ko++Dme2T5s0jCRMxwIhAKNc8CA6IoXC7zOP07lCljZMmBpKezxzQqEqK3z5yIfR"}]},"_npmUser":{"name":"daniyalfaraz2003","email":"daniyalfaraz2003@gmail.com"},"directories":{},"maintainers":[{"name":"daniyalfaraz2003","email":"daniyalfaraz2003@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ectl_1.0.0_1783199461386_0.3874624586515176"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-04T21:11:01.193Z","1.0.0":"2026-07-04T21:11:01.595Z","modified":"2026-07-04T21:11:01.854Z"},"maintainers":[{"name":"daniyalfaraz2003","email":"daniyalfaraz2003@gmail.com"}],"description":"Project-local CLI to run long-lived tasks on AWS EC2","homepage":"https://github.com/DaniyalFaraz2003/AWS-Job-Runner#readme","keywords":["aws","ec2","cli","ectl","ec2-task-launcher"],"repository":{"type":"git","url":"git+https://github.com/DaniyalFaraz2003/AWS-Job-Runner.git"},"author":{"name":"Daniyal Faraz"},"bugs":{"url":"https://github.com/DaniyalFaraz2003/AWS-Job-Runner/issues"},"license":"MIT","readme":"# AWS Job Runner - ectl\r\n\r\n**ectl** is a project-local command-line tool that runs long-lived jobs on Amazon EC2. Think of it like **git for cloud tasks**: run `ectl init` once in your project folder, and everything ectl needs — configuration, SSH keys, instance state, and downloaded logs — lives in a `.ectl/` directory beside your code. Nothing is stored in a global config directory.\r\n\r\nInstead of manually clicking through the AWS Console (launch instance → upload files → install dependencies → start your process → download results → terminate), ectl automates the full lifecycle with a handful of commands.\r\n\r\n---\r\n\r\n\r\n\r\n## Table of contents\r\n\r\n- [What ectl does](#what-ectl-does)\r\n- [Requirements](#requirements)\r\n- [AWS setup and credentials](#aws-setup-and-credentials)\r\n- [Installation](#installation)\r\n- [Core concepts](#core-concepts)\r\n- [Quickstart](#quickstart)\r\n- [Project configuration](#project-configuration)\r\n- [Command reference](#command-reference)\r\n  - [Global flags](#global-flags)\r\n  - `[ectl init](#ectl-init)`\r\n  - `[ectl launch](#ectl-launch)`\r\n  - `[ectl push](#ectl-push)`\r\n  - `[ectl run](#ectl-run)`\r\n  - `[ectl deploy](#ectl-deploy)`\r\n  - `[ectl status](#ectl-status)`\r\n  - `[ectl logs](#ectl-logs)`\r\n  - `[ectl pull](#ectl-pull)`\r\n  - `[ectl ssh](#ectl-ssh)`\r\n  - `[ectl stop](#ectl-stop)`\r\n  - `[ectl terminate](#ectl-terminate)`\r\n- [Typical workflows](#typical-workflows)\r\n- [JSON output (scripting)](#json-output-scripting)\r\n- [Security notes](#security-notes)\r\n- [Common errors and recovery](#common-errors-and-recovery)\r\n- [Development scripts](#development-scripts)\r\n- [Further documentation](#further-documentation)\r\n- [License](#license)\r\n\r\n---\r\n\r\n\r\n\r\n## What ectl does\r\n\r\nWhen you run a batch job, build pipeline, or any long-running process on EC2, you normally repeat the same steps every time:\r\n\r\n1. Create an EC2 instance and open SSH access\r\n2. Copy your project files to the instance\r\n3. Install Node.js, dependencies, and a process manager (pm2)\r\n4. Start your command and monitor logs\r\n5. Download output files when finished\r\n6. Terminate the instance so you stop paying for it\r\n\r\n**ectl wraps all of that into one workflow.** You stay in your project directory on Windows PowerShell, run ectl commands, and the tool talks to AWS and your remote instance over SSH — no AWS CLI required, and no manual key-permission fixes on Windows.\r\n\r\nEach project supports **one active task at a time** (v1). The default task name is `default`.\r\n\r\n---\r\n\r\n\r\n\r\n## Requirements\r\n\r\n\r\n| Requirement          | Details                                                                                                                      |\r\n| -------------------- | ---------------------------------------------------------------------------------------------------------------------------- |\r\n| **Node.js**          | Version **22 or newer** (matches the remote Node version ectl installs)                                                      |\r\n| **Operating system** | **Windows PowerShell** (primary v1 target)                                                                                   |\r\n| **AWS account**      | With permissions to manage EC2 (see [AWS setup](#aws-setup-and-credentials))                                                 |\r\n| **Default VPC**      | Your AWS account must have a **default VPC** with a public subnet and auto-assign public IP enabled                          |\r\n| **Internet access**  | Your machine needs outbound HTTPS to AWS APIs; the EC2 instance needs outbound internet for bootstrap (Node/npm/pm2 install) |\r\n\r\n\r\nectl does **not** require the AWS CLI to be installed. It uses the AWS SDK for JavaScript v3 internally.\r\n\r\n---\r\n\r\n\r\n\r\n## AWS setup and credentials\r\n\r\n\r\n\r\n### How ectl authenticates\r\n\r\nectl uses the **standard AWS SDK credential provider chain**. It never stores AWS access keys inside `.ectl/`. Credentials are resolved in this order:\r\n\r\n1. **Environment variables** — `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and optionally `AWS_SESSION_TOKEN`\r\n2. **Shared credentials file** — `%USERPROFILE%\\.aws\\credentials` on Windows\r\n3. **Config file / profiles** — `%USERPROFILE%\\.aws\\config` (used with `AWS_PROFILE`)\r\n4. **SSO and assumed roles** — via named profiles in your AWS config\r\n\r\nDuring `ectl init`, ectl validates credentials by calling EC2 `DescribeRegions`. If credentials are missing or invalid, init fails with a clear error.\r\n\r\n### Option A — Environment variables (quick test)\r\n\r\nIn PowerShell, set credentials for the current session:\r\n\r\n```powershell\r\n$env:AWS_ACCESS_KEY_ID = \"AKIA...\"\r\n$env:AWS_SECRET_ACCESS_KEY = \"your-secret-key\"\r\n$env:AWS_REGION = \"us-east-1\"   # optional; init wizard also asks for region\r\n```\r\n\r\nFor temporary credentials (STS / assumed role), also set:\r\n\r\n```powershell\r\n$env:AWS_SESSION_TOKEN = \"your-session-token\"\r\n```\r\n\r\n\r\n\r\n### Option B — AWS credentials file (recommended for daily use)\r\n\r\n1. Install the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) (optional but convenient for setup).\r\n2. Run `aws configure` and enter your access key, secret key, and default region.\r\n3. This creates `%USERPROFILE%\\.aws\\credentials` and `%USERPROFILE%\\.aws\\config`.\r\n\r\nTo use a named profile:\r\n\r\n```powershell\r\n$env:AWS_PROFILE = \"my-profile\"\r\nectl init\r\n```\r\n\r\nOr set it permanently in your PowerShell profile.\r\n\r\n### Option C — AWS IAM Identity Center (SSO)\r\n\r\nIf your organization uses SSO:\r\n\r\n```powershell\r\naws configure sso\r\naws sso login --profile my-sso-profile\r\n$env:AWS_PROFILE = \"my-sso-profile\"\r\nectl init\r\n```\r\n\r\n\r\n\r\n### Required IAM permissions\r\n\r\nectl needs broad EC2 permissions in v1. At minimum, your IAM user or role should allow:\r\n\r\n\r\n| Action                                                                                    | Used for                                |\r\n| ----------------------------------------------------------------------------------------- | --------------------------------------- |\r\n| `ec2:DescribeRegions`                                                                     | Validate credentials during init        |\r\n| `ec2:CreateKeyPair`, `ec2:ImportKeyPair`                                                  | SSH key management                      |\r\n| `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress`, `ec2:DeleteSecurityGroup` | SSH firewall rules                      |\r\n| `ec2:DescribeImages`                                                                      | Resolve Ubuntu LTS AMI                  |\r\n| `ec2:RunInstances`, `ec2:TerminateInstances`                                              | Launch and tear down instances          |\r\n| `ec2:DescribeInstances`                                                                   | Status reconciliation, public IP lookup |\r\n| `ec2:CreateTags`                                                                          | Tag instances and security groups       |\r\n\r\n\r\nFor development, an administrator or PowerUser policy is typically sufficient. For production, scope policies to the default VPC and tag-based conditions.\r\n\r\n### Default VPC requirement\r\n\r\nectl launches instances in your account's **default VPC** with a **public IP**. If your account has no default VPC, or subnets do not auto-assign public IPs, `ectl launch` and `ectl deploy` will fail. Create or restore a default VPC in the AWS Console or via CLI before using ectl.\r\n\r\n---\r\n\r\n\r\n\r\n## Installation\r\n\r\n\r\n\r\n### From npm (recommended)\r\n\r\n```powershell\r\nnpm install -g @daniyalfaraz2003/ectl\r\n```\r\n\r\nVerify:\r\n\r\n```powershell\r\nectl --version\r\nectl --help\r\n```\r\n\r\n\r\n\r\n### From source\r\n\r\n```powershell\r\ngit clone https://github.com/DaniyalFaraz2003/AWS-Job-Runner.git\r\ncd AWS-Job-Runner\r\nnpm install\r\nnpm run build\r\nnpm link   # optional: expose `ectl` globally from your clone\r\n```\r\n\r\n---\r\n\r\n\r\n\r\n## Core concepts\r\n\r\n\r\n\r\n### Project-local `.ectl/` directory\r\n\r\nAfter `ectl init`, your project contains:\r\n\r\n```\r\nmy-project/\r\n├── .ectl/\r\n│   ├── config.json          # Region, instance type, AMI, artifact paths, etc.\r\n│   ├── keys/\r\n│   │   └── ectl-key.pem     # Private SSH key (never commit this)\r\n│   ├── tasks/\r\n│   │   └── default/         # One folder per task name\r\n│   │       ├── state.json   # Instance ID, IP, status, security group\r\n│   │       └── run.json     # Command that was run, pm2 process name\r\n│   ├── logs/                # Default destination for `ectl pull` downloads\r\n│   └── run.sh               # Optional default command script\r\n├── .ectlignore              # Files excluded from upload (like .gitignore)\r\n└── .gitignore               # ectl init appends `.ectl/` if missing\r\n```\r\n\r\n\r\n\r\n### Task lifecycle\r\n\r\n\r\n| Status         | Meaning                                                    |\r\n| -------------- | ---------------------------------------------------------- |\r\n| `provisioning` | Instance is being created or waiting for status checks     |\r\n| `running`      | Instance is up and the pm2 process is (or was) started     |\r\n| `stopped`      | Instance is up but pm2 process was stopped via `ectl stop` |\r\n| `failed`       | Something went wrong (e.g. deploy partial failure)         |\r\n| `completed`    | Task finished successfully (reserved for future use)       |\r\n| `terminated`   | Instance destroyed; safe to launch again                   |\r\n\r\n\r\n**Active** statuses (`provisioning`, `running`, `stopped`, `failed`) block new `launch` or `deploy` until you run `ectl terminate`.\r\n\r\n### Run command resolution\r\n\r\nWhen starting a process (`ectl run` or `ectl deploy`), ectl needs a shell command:\r\n\r\n1. `--run \"<command>\"` **flag** — highest priority; runs the command you pass on the command line\r\n2. `.ectl/run.sh` — used if no `--run` flag is provided\r\n3. **Error** — if neither exists, ectl fails with instructions to add one\r\n\r\nThe remote instance runs your command under **pm2** so it survives SSH disconnects.\r\n\r\n### Upload exclusions\r\n\r\n`ectl push` (and `ectl deploy`) zip your project and upload it. Patterns in `.ectlignore` are excluded — same syntax as `.gitignore`. Default patterns exclude `node_modules/`, `.git/`, `.ectl/`, `dist/`, etc.\r\n\r\n---\r\n\r\n\r\n\r\n## Quickstart\r\n\r\n```powershell\r\ncd C:\\Projects\\my-batch-job\r\n\r\n# 1. One-time project setup (interactive wizard)\r\nectl init\r\n# Wizard prompts: AWS region, instance type, Ubuntu AMI\r\n\r\n# 2. Optional: tell ectl what to download when the job finishes\r\n# Edit .ectl/config.json → \"artifactPaths\": [\"output/\", \"logs/\"]\r\n\r\n# 3. Optional: default run script instead of passing --run every time\r\n# Create .ectl/run.sh with contents like:\r\n#   npm install && npm start\r\n\r\n# 4. Full happy path — launch, upload, and run in one step\r\nectl deploy --run \"npm install && npm run build\"\r\n\r\n# 5. Monitor\r\nectl status\r\nectl logs default --follow\r\n\r\n# 6. Retrieve output files (requires artifactPaths in config or --paths)\r\nectl pull\r\n\r\n# 7. Cleanup (keeps SSH key pair for the next launch)\r\nectl terminate\r\n```\r\n\r\n\r\n\r\n### Step-by-step (debug-friendly)\r\n\r\nUse individual commands when you want to inspect each phase:\r\n\r\n```powershell\r\nectl launch          # Create EC2 instance + security group\r\nectl push            # Upload project zip via SFTP\r\nectl run --run \"npm install && npm start\"\r\nectl status          # Reconcile local state with AWS\r\nectl logs default --follow\r\nectl ssh             # Interactive shell on the instance\r\nectl stop            # Stop pm2 process, keep instance running\r\nectl terminate       # Destroy instance and security group\r\n```\r\n\r\n---\r\n\r\n\r\n\r\n## Project configuration\r\n\r\n\r\n\r\n### `.ectl/config.json`\r\n\r\nWritten by `ectl init`. Key fields:\r\n\r\n\r\n| Field           | Description                                                                         |\r\n| --------------- | ----------------------------------------------------------------------------------- |\r\n| `region`        | AWS region for all resources (e.g. `us-east-1`)                                     |\r\n| `instanceType`  | EC2 instance type (default `t3.medium`)                                             |\r\n| `amiId`         | Ubuntu LTS AMI ID (auto-resolved during init if not set)                            |\r\n| `sshUser`       | SSH login user (default `ubuntu`)                                                   |\r\n| `remoteWorkDir` | Remote directory for project files (default `/home/ubuntu/ectl-workspace`)          |\r\n| `keyPairName`   | AWS EC2 key pair name                                                               |\r\n| `keySource`     | `\"generated\"` or `\"imported\"`                                                       |\r\n| `nodeVersion`   | Node.js major version to install remotely (from your local Node at init)            |\r\n| `artifactPaths` | Remote paths to download with `ectl pull` (relative to `remoteWorkDir` or absolute) |\r\n| `projectSlug`   | Derived from your folder name; used in AWS resource names and tags                  |\r\n| `tags`          | Optional extra AWS tags merged with required `ectl:*` tags                          |\r\n\r\n\r\nExample snippet:\r\n\r\n```json\r\n{\r\n  \"version\": 1,\r\n  \"region\": \"us-east-1\",\r\n  \"instanceType\": \"t3.medium\",\r\n  \"artifactPaths\": [\"output/\", \"logs/run.log\"],\r\n  \"remoteWorkDir\": \"/home/ubuntu/ectl-workspace\"\r\n}\r\n```\r\n\r\n\r\n\r\n### `.ectlignore`\r\n\r\nDefault patterns created by init:\r\n\r\n```\r\nnode_modules/\r\n.git/\r\n.ectl/\r\ndist/\r\nbuild/\r\n.next/\r\ncoverage/\r\n```\r\n\r\nAdd project-specific patterns (e.g. `.env`, `tmp/`, `*.log`). **Important:** `.env` is **not** excluded by default. Add it manually if you must not upload secrets.\r\n\r\n### `.ectl/run.sh` (optional)\r\n\r\nBash script executed on the remote instance from `remoteWorkDir`:\r\n\r\n```bash\r\n#!/usr/bin/env bash\r\nset -euo pipefail\r\nnpm install\r\nnpm run build\r\nnpm start\r\n```\r\n\r\nUsed automatically when you omit `--run`.\r\n\r\n---\r\n\r\n\r\n\r\n## Command reference\r\n\r\nEvery command supports `--help` for built-in usage text:\r\n\r\n```powershell\r\nectl --help\r\nectl deploy --help\r\nectl init --help\r\n```\r\n\r\nFlags can be placed **before or after** the subcommand (Commander's pass-through options), and most commands accept `--json` and `--verbose` either globally or per-command:\r\n\r\n```powershell\r\nectl --json status\r\nectl status --json\r\n```\r\n\r\nBoth work.\r\n\r\n---\r\n\r\n\r\n\r\n### Global flags\r\n\r\nThese apply to the root `ectl` program and are inherited by subcommands (subcommands also declare their own copies for convenience).\r\n\r\n\r\n| Flag        | Short | Description                                                                                                                                                          |\r\n| ----------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| `--version` | `-V`  | Print the ectl version number and exit                                                                                                                               |\r\n| `--help`    | `-h`  | Show help for ectl or a specific subcommand                                                                                                                          |\r\n| `--json`    |       | Emit structured JSON to stdout (see [JSON output](#json-output-scripting)). Suppresses decorative human output. Some interactive features are disabled in JSON mode. |\r\n| `--verbose` |       | Enable debug logging to stderr, including AWS request IDs where available                                                                                            |\r\n\r\n\r\n**JSON mode caveats:**\r\n\r\n\r\n| Command                             | Behavior with `--json`                                               |\r\n| ----------------------------------- | -------------------------------------------------------------------- |\r\n| `ectl ssh`                          | **Not supported** — interactive SSH cannot produce JSON output       |\r\n| `ectl logs --follow`                | **Not supported** — streaming and JSON are mutually exclusive        |\r\n| `ectl terminate`                    | Skips the destructive confirmation prompt (use with care in scripts) |\r\n| `ectl init --force`                 | Skips the reinitialize confirmation prompt                           |\r\n| `ectl launch/deploy --allow-any-ip` | Auto-confirms the security warning (warning still printed to stderr) |\r\n\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl init`\r\n\r\n**Purpose:** One-time setup. Creates the `.ectl/` directory tree, validates AWS credentials, generates or imports an SSH key pair, resolves a Ubuntu LTS AMI, writes `config.json`, creates `.ectlignore`, and appends `.ectl/` to `.gitignore`.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl init [options]\r\n```\r\n\r\n**Interactive wizard:** If you omit flags, ectl prompts for:\r\n\r\n- **AWS region** (default: `AWS_REGION`, `AWS_DEFAULT_REGION`, or `us-east-1`)\r\n- **EC2 instance type** (choices: `t3.micro`, `t3.small`, `t3.medium`, `t3.large`, `t3.xlarge`)\r\n- **Ubuntu LTS AMI** (22.04 / 24.04 / 26.04 candidates for the region)\r\n\r\nIn `--json` mode, prompts are skipped where possible; AMI defaults to Ubuntu 24.04 if available, otherwise the first candidate.\r\n\r\n\r\n| Option                   | Description                                                                                                                                                             |\r\n| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| `--region <region>`      | AWS region (e.g. `us-east-1`). Skips the region prompt.                                                                                                                 |\r\n| `--instance-type <type>` | EC2 instance type (e.g. `t3.medium`). Skips the instance type prompt.                                                                                                   |\r\n| `--ami-id <amiId>`       | Specific Ubuntu LTS AMI ID. Skips the AMI selection prompt.                                                                                                             |\r\n| `--import-key <path>`    | Import an existing PEM private key instead of generating a new one. The key is copied to `.ectl/keys/ectl-key.pem` and registered in AWS if needed.                     |\r\n| `--force`                | Reinitialize an existing `.ectl/` directory. **Destructive** — deletes the current `.ectl/` tree after confirmation (confirmation skipped when combined with `--json`). |\r\n| `--json`                 | Machine-readable output envelope                                                                                                                                        |\r\n| `--verbose`              | Debug logging                                                                                                                                                           |\r\n\r\n\r\n**Examples:**\r\n\r\n```powershell\r\n# Interactive setup\r\nectl init\r\n\r\n# Non-interactive setup (CI or scripting)\r\nectl init --region us-east-1 --instance-type t3.medium --json\r\n\r\n# Use your own existing key pair\r\nectl init --import-key C:\\Users\\me\\.ssh\\my-ec2-key.pem\r\n\r\n# Start over (after terminating any active task)\r\nectl init --force\r\n```\r\n\r\n**After init:** Add a run command (`.ectl/run.sh` or plan to use `--run`) and run `ectl deploy` or the step-by-step commands.\r\n\r\n**Failure cases:**\r\n\r\n- `.ectl/` already exists → run `ectl terminate` if a task is active, then `ectl init --force`\r\n- Invalid AWS credentials → fix credentials (see [AWS setup](#aws-setup-and-credentials)) and retry\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl launch`\r\n\r\n**Purpose:** Provision AWS resources for a task — security group (SSH on port 22), EC2 instance in the default VPC with a public IP, required tags, and local state file. Waits for the instance to pass status checks and verifies SSH connectivity.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl launch [options]\r\n```\r\n\r\n\r\n| Option           | Description                                                                                                                                                                                                                  |\r\n| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| `--name <task>`  | Task name. Default: `default`.                                                                                                                                                                                               |\r\n| `--allow-any-ip` | Allow SSH from **any IP** (`0.0.0.0/0`) instead of only your current public IPv4. Shows a security warning and requires confirmation (auto-confirmed with `--json`). **Not recommended** unless you understand the exposure. |\r\n| `--json`         | Machine-readable output                                                                                                                                                                                                      |\r\n| `--verbose`      | Debug logging                                                                                                                                                                                                                |\r\n\r\n\r\n**What it creates:**\r\n\r\n- Security group named `ectl-<projectSlug>-<taskName>`\r\n- EC2 instance tagged with `ectl:project`, `ectl:task`, `ectl:created-at`, `ectl:created-by`\r\n- State file at `.ectl/tasks/<name>/state.json`\r\n\r\n**Default SSH rule:** Port 22 open only to **your detected public IPv4 /32**. ectl detects your IP automatically; if your IP changes, use `ectl status` to reconcile or relaunch.\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl launch\r\nectl launch --name default\r\nectl launch --allow-any-ip          # Opens SSH worldwide (with warning)\r\nectl launch --json\r\n```\r\n\r\n**Next steps:** `ectl push` then `ectl run`, or use `ectl deploy` to do all three.\r\n\r\n**Failure cases:**\r\n\r\n- Another active task exists → `ectl terminate` first\r\n- No default VPC or quota limits → check AWS Console\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl push`\r\n\r\n**Purpose:** Upload your project to the running EC2 instance. Builds a zip archive honoring `.ectlignore`, uploads via SFTP, and extracts into `remoteWorkDir` on the instance (replacing previous content).\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl push [options]\r\n```\r\n\r\n\r\n| Option          | Description                                      |\r\n| --------------- | ------------------------------------------------ |\r\n| `--name <task>` | Task name. Default: the current **active task**. |\r\n| `--json`        | Machine-readable output                          |\r\n| `--verbose`     | Debug logging                                    |\r\n\r\n\r\n**Requires:** An active task in `running` or `stopped` status with a reachable public IP.\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl push\r\nectl push --name default\r\nectl push --verbose\r\n```\r\n\r\n**Next step:** `ectl run` to bootstrap Node/pm2 and start your process.\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl run`\r\n\r\n**Purpose:** Connect to the instance, bootstrap the environment on first use (install `curl`, `unzip`, Node.js matching `config.nodeVersion`, npm, and pm2), then start your command under pm2. Writes `.ectl/tasks/<name>/run.json` and sets task status to `running`.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl run [options]\r\n```\r\n\r\n\r\n| Option            | Description                                                                                  |\r\n| ----------------- | -------------------------------------------------------------------------------------------- |\r\n| `--name <task>`   | Task name. Default: active task.                                                             |\r\n| `--run <command>` | Shell command to execute on the remote instance. **Overrides** `.ectl/run.sh` when provided. |\r\n| `--json`          | Machine-readable output                                                                      |\r\n| `--verbose`       | Debug logging                                                                                |\r\n\r\n\r\n**Run command priority:**\r\n\r\n1. `--run \"<command>\"` if provided\r\n2. `.ectl/run.sh` if it exists\r\n3. Error if neither is available\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl run --run \"npm install && npm start\"\r\nectl run --run \"node scripts/batch-job.js\"\r\nectl run                                    # uses .ectl/run.sh\r\nectl run --name default --run \"npm test\"\r\n```\r\n\r\n**Next steps:** `ectl logs default --follow` or `ectl status`.\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl deploy`\r\n\r\n**Purpose:** One-shot workflow — runs **launch → push → run** in sequence. Best for the common \"just run my job\" path.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl deploy [options]\r\n```\r\n\r\n\r\n| Option            | Description                                     |\r\n| ----------------- | ----------------------------------------------- |\r\n| `--name <task>`   | Task name. Default: `default`.                  |\r\n| `--run <command>` | Shell command to run (same rules as `ectl run`) |\r\n| `--allow-any-ip`  | Same as `ectl launch --allow-any-ip`            |\r\n| `--json`          | Machine-readable output                         |\r\n| `--verbose`       | Debug logging                                   |\r\n\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl deploy --run \"npm install && npm run build\"\r\nectl deploy --run \"python3 main.py\" --name default\r\nectl deploy --verbose --run \"npm start\"\r\nectl deploy --allow-any-ip --run \"npm start\"   # not recommended\r\n```\r\n\r\n**Partial failure behavior:** If deploy fails partway (e.g. push succeeds but run fails), **AWS resources are left running** so you can debug. ectl prints recovery hints: `ectl status`, `ectl ssh`, `ectl terminate`. Task state may be set to `failed`.\r\n\r\n**Next steps:** `ectl logs default --follow`, `ectl status`, `ectl pull` when done.\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl status`\r\n\r\n**Purpose:** Show the current task's state in a human-readable table. Automatically **reconciles** with AWS (`DescribeInstances`, security groups) and queries pm2 over SSH when the instance is reachable. Updates local state if the public IP changed or the instance was terminated externally.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl status [options]\r\n```\r\n\r\n\r\n| Option          | Description                      |\r\n| --------------- | -------------------------------- |\r\n| `--name <task>` | Task name. Default: active task. |\r\n| `--json`        | Machine-readable output          |\r\n| `--verbose`     | Debug logging                    |\r\n\r\n\r\n**Displayed fields (human mode):**\r\n\r\n\r\n| Field                          | Description                                                        |\r\n| ------------------------------ | ------------------------------------------------------------------ |\r\n| Task                           | Task name                                                          |\r\n| Status                         | Local lifecycle status (color-coded)                               |\r\n| Instance                       | EC2 instance ID                                                    |\r\n| Public IP                      | Current public IPv4                                                |\r\n| Security group                 | Security group ID                                                  |\r\n| Region                         | AWS region                                                         |\r\n| AWS instance                   | Live EC2 state from AWS (`running`, `stopped`, `terminated`, etc.) |\r\n| pm2                            | Process status and PID, or `unreachable` / `n/a`                   |\r\n| Run command / source / started | From `run.json` if the process was started                         |\r\n| Last reconciled                | Timestamp of last AWS sync                                         |\r\n\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl status\r\nectl status --json | ConvertFrom-Json\r\nectl status --name default\r\n```\r\n\r\nIf no active task exists, ectl prints `No active task.` and exits with code 0.\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl logs`\r\n\r\n**Purpose:** Fetch or stream **pm2 logs** for the task's process on the remote instance.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl logs [task] [options]\r\n```\r\n\r\n\r\n| Argument / option | Description                                                                                     |\r\n| ----------------- | ----------------------------------------------------------------------------------------------- |\r\n| `[task]`          | Optional task name. Default: active task. Example: `ectl logs default`                          |\r\n| `--lines <n>`     | Number of log lines to show for a one-shot fetch. Default: **100**. Must be a positive integer. |\r\n| `-f`, `--follow`  | Stream logs in real time until you press **Ctrl+C**. Cannot be combined with `--json`.          |\r\n| `--json`          | Machine-readable output (one-shot fetch only)                                                   |\r\n| `--verbose`       | Debug logging                                                                                   |\r\n\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl logs default\r\nectl logs default --lines 500\r\nectl logs --follow\r\nectl logs default -f\r\nectl logs default --lines 50 --json\r\n```\r\n\r\nIn `--follow` mode, ectl connects via SSH and streams stdout/stderr from pm2 until interrupted.\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl pull`\r\n\r\n**Purpose:** Download artifact files from the remote instance to your local machine. Paths come from `artifactPaths` in `config.json` unless overridden.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl pull [options]\r\n```\r\n\r\n\r\n| Option            | Description                                                                                                                          |\r\n| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ |\r\n| `--name <task>`   | Task name. Default: active task.                                                                                                     |\r\n| `--output <path>` | Override the local destination directory. Default: `.ectl/logs/<task-name>/`                                                         |\r\n| `--paths <paths>` | Comma-separated list of remote paths for this run only. Overrides `artifactPaths` in config. Example: `--paths output/,logs/run.log` |\r\n| `--json`          | Machine-readable output                                                                                                              |\r\n| `--verbose`       | Debug logging                                                                                                                        |\r\n\r\n\r\n**Requires:** At least one path — either configured in `config.json` → `artifactPaths` or passed via `--paths`. Fails with a clear message if no paths are configured.\r\n\r\n**Examples:**\r\n\r\n```powershell\r\n# Uses artifactPaths from .ectl/config.json\r\nectl pull\r\n\r\n# One-off paths without editing config\r\nectl pull --paths \"output/results.csv,logs/\"\r\n\r\n# Custom local folder\r\nectl pull --output C:\\Downloads\\my-job-output\r\nectl pull --paths \"output/\" --output .\\results\r\n```\r\n\r\n**Next step:** `ectl terminate` when you no longer need the instance.\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl ssh`\r\n\r\n**Purpose:** Open an **interactive shell** on the task instance as `config.sshUser` (default `ubuntu`). Uses `.ectl/keys/ectl-key.pem` via `node-ssh` — no Windows OpenSSH configuration required.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl ssh [options]\r\n```\r\n\r\n\r\n| Option          | Description                      |\r\n| --------------- | -------------------------------- |\r\n| `--name <task>` | Task name. Default: active task. |\r\n| `--verbose`     | Debug logging                    |\r\n\r\n\r\n**Note:** `--json` is **not supported** (interactive session). Exit the shell with **Ctrl+D** or type `exit`.\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl ssh\r\nectl ssh --name default\r\nectl ssh --verbose\r\n```\r\n\r\n**Requires:** Active task with reachable public IP.\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl stop`\r\n\r\n**Purpose:** Stop the **pm2 process** on the instance without destroying the EC2 instance. Useful when you want to pause compute work but keep the machine for faster restart or inspection.\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl stop [options]\r\n```\r\n\r\n\r\n| Option          | Description                      |\r\n| --------------- | -------------------------------- |\r\n| `--name <task>` | Task name. Default: active task. |\r\n| `--json`        | Machine-readable output          |\r\n| `--verbose`     | Debug logging                    |\r\n\r\n\r\nUpdates task status to `stopped`. The instance keeps running (you continue paying for EC2).\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl stop\r\nectl stop --name default\r\n```\r\n\r\n**Next steps:** `ectl run` to restart the process, `ectl status`, or `ectl terminate` when finished.\r\n\r\nIf the process is already stopped, ectl reports that and suggests next commands.\r\n\r\n---\r\n\r\n\r\n\r\n### `ectl terminate`\r\n\r\n**Purpose:** Tear down AWS resources for the task — terminate the EC2 instance, wait until terminated, delete the security group, and update local state to `terminated`. **Does not** delete the SSH key pair in `.ectl/keys/` (reused on next launch).\r\n\r\n**Usage:**\r\n\r\n```powershell\r\nectl terminate [options]\r\n```\r\n\r\n\r\n| Option          | Description                                                                                          |\r\n| --------------- | ---------------------------------------------------------------------------------------------------- |\r\n| `--name <task>` | Task name. Default: active task.                                                                     |\r\n| `--json`        | Machine-readable output. **Skips the confirmation prompt** — intended for automation; use carefully. |\r\n| `--verbose`     | Debug logging                                                                                        |\r\n\r\n\r\n**Interactive confirmation:** In normal (non-JSON) mode, ectl asks you to confirm before terminating. This cannot be undone.\r\n\r\n**Examples:**\r\n\r\n```powershell\r\nectl terminate\r\nectl terminate --name default\r\nectl terminate --json    # no prompt; for scripts only\r\n```\r\n\r\n**Next steps:** `ectl launch` or `ectl deploy` to start a new task.\r\n\r\n---\r\n\r\n\r\n\r\n## Typical workflows\r\n\r\n\r\n\r\n### Run a Node.js batch job end-to-end\r\n\r\n```powershell\r\ncd C:\\Projects\\my-app\r\nectl init\r\nectl deploy --run \"npm install && node scripts/process-data.js\"\r\nectl logs default --follow\r\nectl pull --paths \"output/\"\r\nectl terminate\r\n```\r\n\r\n\r\n\r\n### Iterate on code without relaunching the instance\r\n\r\n```powershell\r\nectl push\r\nectl run --run \"npm install && npm test\"\r\nectl logs default --follow\r\n```\r\n\r\n\r\n\r\n### Debug a failed deploy\r\n\r\n```powershell\r\nectl status                    # See AWS vs local state\r\nectl ssh                       # Inspect files, run commands manually\r\nectl logs default --lines 200\r\nectl terminate                 # Clean up when done\r\n```\r\n\r\n\r\n\r\n### Stop work overnight, resume next day\r\n\r\n```powershell\r\nectl stop\r\n# ... next day ...\r\nectl run --run \"npm start\"\r\nectl logs default --follow\r\n```\r\n\r\n\r\n\r\n### Scripting with JSON\r\n\r\n```powershell\r\n$result = ectl status --json | ConvertFrom-Json\r\nif ($result.ok -and $result.data.status -eq \"running\") {\r\n  ectl logs default --lines 20 --json\r\n}\r\n```\r\n\r\n---\r\n\r\n\r\n\r\n## JSON output (scripting)\r\n\r\nAll commands except interactive `ssh` and `logs --follow` support `--json`. Output is a consistent envelope on stdout:\r\n\r\n**Success:**\r\n\r\n```json\r\n{\r\n  \"ok\": true,\r\n  \"command\": \"status\",\r\n  \"data\": { },\r\n  \"error\": null\r\n}\r\n```\r\n\r\n**Failure:**\r\n\r\n```json\r\n{\r\n  \"ok\": false,\r\n  \"command\": \"deploy\",\r\n  \"data\": null,\r\n  \"error\": {\r\n    \"code\": \"ACTIVE_TASK_EXISTS\",\r\n    \"message\": \"Task 'default' is still running. Run ectl terminate first.\"\r\n  }\r\n}\r\n```\r\n\r\nExit codes: **0** on success, **non-zero** on failure.\r\n\r\nDecorative spinners and tables are suppressed in JSON mode. Use `--verbose` for debug details on stderr.\r\n\r\n---\r\n\r\n\r\n\r\n## Security notes\r\n\r\n\r\n| Topic                  | Guidance                                                                                                      |\r\n| ---------------------- | ------------------------------------------------------------------------------------------------------------- |\r\n| **Private keys**       | Never commit `.ectl/`. Init appends `.ectl/` to `.gitignore`. Do not share `.ectl/keys/`.                     |\r\n| **SSH access**         | By default, only **your current public IP** can SSH to the instance. Avoid `--allow-any-ip` unless necessary. |\r\n| **Secrets in uploads** | `.env` is **not** in the default `.ectlignore`. Add `.env` manually if your project contains secrets.         |\r\n| **AWS credentials**    | Stored only in the standard AWS chain — never in `.ectl/config.json`.                                         |\r\n| **Terminate**          | Always confirm termination in interactive mode. Use `--json` terminate only in trusted automation.            |\r\n\r\n\r\nRequired AWS tags on instances and security groups: `ectl:project`, `ectl:task`, `ectl:created-at`, `ectl:created-by`.\r\n\r\n---\r\n\r\n\r\n\r\n## Common errors and recovery\r\n\r\n\r\n| Error code                | Meaning                                  | What to do                                                                 |\r\n| ------------------------- | ---------------------------------------- | -------------------------------------------------------------------------- |\r\n| `NOT_INITIALIZED`         | No `.ectl/` in project                   | Run `ectl init`                                                            |\r\n| `ACTIVE_TASK_EXISTS`      | A task is already running or provisioned | `ectl status`, then `ectl terminate` if done                               |\r\n| `NO_ACTIVE_TASK`          | No task to operate on                    | `ectl launch` or `ectl deploy`                                             |\r\n| `AWS_CREDENTIALS_INVALID` | Bad or missing AWS credentials           | Fix [AWS setup](#aws-setup-and-credentials)                                |\r\n| `RUN_COMMAND_MISSING`     | No `--run` and no `.ectl/run.sh`         | Add one before `run` or `deploy`                                           |\r\n| `ARTIFACT_PATHS_EMPTY`    | Nothing to pull                          | Set `artifactPaths` in config or use `--paths`                             |\r\n| `SSH_CONNECTION_FAILED`   | Cannot reach instance                    | Check security group IP, instance state, `ectl status`                     |\r\n| `DEPLOY_PARTIAL_FAILURE`  | Deploy stopped mid-flow                  | Resources left running — `ectl ssh`, fix, retry `run`, or `ectl terminate` |\r\n| `INSTANCE_NO_PUBLIC_IP`   | Instance has no public IPv4              | Check default VPC / subnet settings                                        |\r\n\r\n\r\nEvery error message includes a suggested next command where possible.\r\n\r\n---\r\n\r\n\r\n\r\n## Development scripts\r\n\r\nFor contributors working on the ectl source code:\r\n\r\n\r\n| Script              | Description                             |\r\n| ------------------- | --------------------------------------- |\r\n| `npm run build`     | Compile TypeScript to `dist/`           |\r\n| `npm run dev`       | Run CLI via `tsx` without building      |\r\n| `npm run start`     | Run compiled CLI (`node dist/index.js`) |\r\n| `npm run typecheck` | Typecheck without emit                  |\r\n| `npm test`          | Run vitest unit tests                   |\r\n\r\n\r\n---\r\n\r\n\r\n\r\n## Further documentation\r\n\r\n- [Software Requirements Specification (SRS)](docs/SRS.md) — full requirements, schemas, and architecture\r\n- [Manual test checklist (Windows)](docs/MANUAL-TEST.md)\r\n- [Contributing](CONTRIBUTING.md)\r\n\r\n---\r\n\r\n\r\n\r\n## License\r\n\r\n[MIT](LICENSE) — free to use, modify, and distribute.","readmeFilename":"README.md","_rev":"1-f9de10d19c3315c3a0c58c4bfbf2a31a"}