{"id":51676902,"url":"https://github.com/codeyoma/formctl","last_synced_at":"2026-07-15T07:33:45.354Z","repository":{"id":360261248,"uuid":"1249354323","full_name":"codeyoma/formctl","owner":"codeyoma","description":"Turn browser-recorded web forms into safe, repeatable CLI commands.","archived":false,"fork":false,"pushed_at":"2026-05-25T18:17:13.000Z","size":137,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T18:29:00.510Z","etag":null,"topics":["agent","browser-automation","cli","forms","playwright","rpa"],"latest_commit_sha":null,"homepage":"https://github.com/codeyoma/formctl#readme","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codeyoma.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"docs/agents.md","dco":null,"cla":null}},"created_at":"2026-05-25T16:00:01.000Z","updated_at":"2026-05-25T18:17:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/codeyoma/formctl","commit_stats":null,"previous_names":["codeyoma/formctl"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/codeyoma/formctl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeyoma%2Fformctl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeyoma%2Fformctl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeyoma%2Fformctl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeyoma%2Fformctl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codeyoma","download_url":"https://codeload.github.com/codeyoma/formctl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeyoma%2Fformctl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35496718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-15T02:00:06.706Z","response_time":131,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","browser-automation","cli","forms","playwright","rpa"],"created_at":"2026-07-15T07:33:44.452Z","updated_at":"2026-07-15T07:33:45.347Z","avatar_url":"https://github.com/codeyoma.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# formctl\n\nformctl turns any browser form into a safe, repeatable CLI command.\n\nRun a saved workflow. Preview first. Approve only when ready.\n\n`formctl` is for developers, operators, and AI agents that need reliable automation for web forms with no useful API. One person records a form workflow into reviewable YAML, then everyone else can run it as a CLI command with dry-run artifacts and explicit approval before real submission.\n\n[Workflow request guide](docs/WORKFLOW_REQUESTS.md) · [Example before/after posts](docs/EXAMPLE_POSTS.md) · [Growth log](docs/GROWTH_LOG.md) · [Trust and security notes](docs/TRUST.md) · [Multi-step recording metadata](docs/MULTI_STEP_RECORDING.md) · [Navigation step design](docs/NAVIGATION_STEPS.md) · [Comparison with Playwright, browser agents, and RPA](docs/COMPARISON.md) · [Why browser agents need form-specific CLIs](docs/WHY_FORM_CLIS.md)\n\n```bash\nformctl submit expense-report --values values.json --dry-run --json\nformctl submit expense-report --values values.json --approve --json\n```\n\n![formctl demo](docs/assets/demo.svg)\n\n[Watch the real local demo video](docs/assets/demo.mp4)\n\n## Trust Artifacts\n\n![Dry-run preview](docs/assets/dry-run-preview.svg)\n\n![Selector mismatch](docs/assets/selector-mismatch.svg)\n\n![Audit log](docs/assets/audit-log.svg)\n\nSee [Trust and security notes](docs/TRUST.md) for dry-run, approval, audit log, selector breakage, and secret-handling details. See [Comparison with Playwright, browser agents, and RPA](docs/COMPARISON.md) for when `formctl` is the right layer.\n\n## Install\n\n```bash\nnpm install -g formctl\nnpx formctl --version\nnpx formctl --help\nnpx formctl doctor\n```\n\n`doctor` checks Node, the current workspace, and the Playwright Chromium browser used by record and submit. If the browser is missing, run:\n\n```bash\nnpx playwright install chromium\n```\n\n## Two-Minute Local Demo\n\nRun the demo locally:\n\n```bash\nnpm install\nnpm run demo\n```\n\nIn a second terminal:\n\n```bash\nnpm run formctl -- workflows --json\nnpm run formctl -- validate expense-report --json\nnpm run formctl -- submit expense-report --values demo/expense-values.json --dry-run --json --headless\nnpm run formctl -- submit expense-report --values demo/expense-values.json --approve --json --headless\n```\n\nThat is the main loop: discover, validate, dry-run, approve.\n\nThe demo workflows are already checked in under `.formctl/workflows/`. Run `npm run formctl -- inspect \u003cworkflow-name\u003e --json` to see required fields for `expense-report`, `admin-invite`, `support-refund`, `vendor-onboarding`, `procurement-approval`, `procurement-handoff`, `crm-update`, and `compliance-attestation`.\nThe `procurement-approval` demo also proves bounded multi-step replay: the workflow opens a named approval modal, fills fields, clicks a reviewed `after-fields` confirmation step, and still stops before final submit during dry-run.\nThe `procurement-handoff` demo proves same-origin path-only navigation replay: the workflow fills a request page, clicks a reviewed continue button, waits for `/procurement-handoff/confirm`, and still dry-runs before the final approval submit.\nWorkflows can also use same-origin path-only navigation steps for known multi-page handoffs.\n\nUse `--values \u003cpath\u003e` when field flags would be hard to quote. Unknown JSON keys or unknown submit field flags are rejected as `field_values_invalid` before opening the browser.\nUse `--storage-state \u003cpath\u003e` to replay a protected form with a local Playwright storageState JSON file after the user has already logged in.\nStorage state files can contain cookies and must stay local; do not commit or paste them into agent chat.\nUse `--resume-after-interaction` only in a local interactive submit run after completing login, MFA, or CAPTCHA in the browser.\n\nInteractive submit shows the `dry-run.png` screenshot path before asking you to type `approve`.\n\nRun artifacts are written under `.formctl/runs/\u003crun-id\u003e/`:\n\n- `summary.json`\n- `field-diff.json`\n- `audit.jsonl`\n- `dry-run.png` for previews\n- `post-submit.png` for approved submissions\n- `failure.json` and `failure.png` for selector mismatches and interaction-required safe stops\n\nField diffs list the resolved values that will be set before submission, with file inputs redacted as `[file]`.\nAudit logs record selector checks, redacted field values, approval source, screenshots, field diff paths, and final result.\nUse `formctl cleanup --max-age-days 7 --dry-run --json` to preview old local run artifacts before deleting them. Remove them with `formctl cleanup --max-age-days 7`.\nFor sensitive local runs, add `--protect-artifacts --artifact-passphrase-env FORMCTL_ARTIFACT_KEY` to write encrypted `.protected` artifacts instead of plaintext JSON, JSONL, or screenshot files. Reveal a protected text artifact only when needed:\n\n```bash\nformctl artifacts reveal .formctl/runs/\u003crun-id\u003e/summary.json.protected --passphrase-env FORMCTL_ARTIFACT_KEY\n```\n\nWorkflow files include safety metadata for dry-run first, required approval, selector drift failure, and file-input redaction.\nWorkflow names may contain only letters, numbers, dots, underscores, and dashes.\nRun `formctl validate \u003cworkflow-name\u003e --json` before reviewing or sharing workflow YAML.\nInvalid workflow checks include `message` and `fix` fields so agents can report a concrete repair.\nUnreadable workflow YAML returns a `readable-yaml` check with `message` and `fix` fields.\nSelector mismatch JSON may include `error.repair` when a missing field, submit, or workflow-step selector has exactly one bounded candidate. Treat it as a review hint, update workflow YAML manually, and rerun dry-run.\nValidation rejects unredacted `recording.events` metadata when present.\nValidation also rejects `recording.events` entries that do not match a known workflow field and selector.\nValidation rejects duplicate workflow field names before any browser work.\nValidation rejects reserved or unsafe workflow field names before any browser work.\nValidation rejects unsupported workflow field types before any browser work.\nValidation rejects missing or non-http workflow target URLs before any browser work.\nInvalid workflow names return `invalid_workflow_name` in JSON mode.\nMissing workflows return `workflow_not_found` in JSON mode.\nUnreadable workflows return `workflow_unreadable` in JSON mode for inspect and submit.\nInvalid workflows return `workflow_invalid` in JSON mode for inspect and submit.\n\n## Create A New Workflow\n\nUse `record` only when you need to create a workflow that does not exist yet.\n\n```bash\nformctl record expense-report https://example.internal/expense\nformctl record expense-report https://example.internal/expense --manual\nformctl record expense-report https://example.internal/expense --storage-state ./storage-state.json --headless\n```\n\nUse `--manual` when login, navigation, or form setup needs a human-visible browser before saving selectors.\nUse `--storage-state \u003cpath\u003e` with `record` or `submit` only after the user has completed login, MFA, or setup in a local browser session.\nManual recording stores redacted `recording.events` entries for changed fields and file inputs.\nManual recording labels text input as `input`, select controls as `select`, file inputs as `file`, named non-submit button clicks as `click`, and page navigation as `wait` so the YAML is easier to review.\nReviewed `steps` entries can describe named `before-fields` setup clicks, preflighted `after-fields` review clicks, or same-origin path-only navigation steps that need per-step screenshots.\nWhen present, `submit` uses the first recorded event for each field to replay fields in the same order the human recorded them.\nSee [Multi-step recording metadata](docs/MULTI_STEP_RECORDING.md) for the current safe boundary: bounded named setup clicks can open known UI before field checks, preflighted `after-fields` clicks can reveal a final submit control after fields are filled, same-origin path-only navigation steps can hand off to a known page, recording `wait` events remain review metadata, and side effects still require dry-run/approval.\nIf terminal input closes before Enter, `record --manual` cancels without writing a workflow file.\nCommit or share the generated `.formctl/workflows/\u003cworkflow-name\u003e.yml` file so other users can start from `submit --dry-run`.\n`record` also saves a baseline screenshot next to the workflow file.\n\n## Commands\n\n```bash\nformctl submit \u003cworkflow-name\u003e --dry-run [flags]\nformctl submit \u003cworkflow-name\u003e --approve [flags]\nformctl submit \u003cworkflow-name\u003e [flags]\nformctl inspect \u003cworkflow-name\u003e [--json]\nformctl workflows [--json]\nformctl validate \u003cworkflow-name\u003e [--json]\nformctl artifacts reveal \u003cartifact-path\u003e --passphrase-env \u003cenv\u003e\nformctl record \u003cworkflow-name\u003e \u003curl\u003e\nformctl doctor [--json]\n```\n\n## Browser mode defaults\n\n- `record` defaults to `--headed` so humans can watch login and form discovery.\n- `submit --dry-run` defaults to `--headless` for repeatable agent and CI previews.\n- Use `--headed` or `--headless` to override the default for any browser-backed command.\n\nWorkflow files are stored at:\n\n```text\n.formctl/workflows/\u003cworkflow-name\u003e.yml\n```\n\n## Safety Contract\n\n- Dry-run never clicks the recorded submit selector.\n- Real submission requires `--approve` or an interactive terminal confirmation.\n- Recorded selectors must match exactly one element.\n- Field selectors and workflow step selectors fail before filling fields or submitting.\n  For workflows without `after-fields` steps, the submit selector also fails before field filling.\n  For workflows with reviewed `after-fields` steps, the final submit selector is checked after those steps and still before the real submit click.\n  Navigation steps must be same-origin and path-only, and login/CAPTCHA/MFA checks run again after navigation.\n  Selector mismatch failures write `failure.json`, `failure.png`, and `audit.jsonl`.\n- Login, CAPTCHA, and MFA walls fail before filling fields or submitting.\n  Interaction-required failures write `failure.json`, `failure.png`, and `audit.jsonl` with `interaction_required`, `captcha_required`, or `mfa_required`.\n- File inputs are redacted as `[file]` in summaries.\n- Audit logs are written for successful dry-run, approved, and selector-mismatch failed runs.\n- JSON output is available for agent and automation callers.\n\n## Exit codes\n\n```text\n0 success\n1 user/input error\n2 workflow not found\n3 selector mismatch\n4 dry-run failed\n5 approval required\n6 interaction required\n10 unexpected runtime error\n```\n\nDry-run browser runtime failures return `dry_run_failed` in JSON mode with `failure.json` and `audit.jsonl` artifacts.\nLogin, CAPTCHA, and MFA walls return `interaction_required`, `captcha_required`, or `mfa_required` in JSON mode with failure artifacts.\n\n## Agent Usage\n\nAgents should call `submit --dry-run --json` first, inspect the returned artifacts, and only use `--approve` when the user or policy explicitly allows submission.\n\nSee the [Agent safety guide](docs/agents.md) for Codex, Claude Code, Cursor, Copilot CLI, and other coding agents.\n\nSee [Why browser agents need form-specific CLIs](docs/WHY_FORM_CLIS.md) for JSON branching examples and the agent-specific rationale. See the [MCP setup guide](docs/MCP.md) for local-checkout and npm-based client configuration.\n\n## MCP Server\n\n`formctl-mcp` exposes the dry-run-safe parts of the CLI to MCP clients:\n\n```bash\nnpx formctl-mcp\n```\n\nTools:\n\n- `formctl_doctor`\n- `formctl_workflows`\n- `formctl_inspect`\n- `formctl_validate`\n- `formctl_submit_dry_run`\n\nThe MCP server does not expose approved submit. Agents still need a human or policy-approved CLI call to run `formctl submit ... --approve`.\n\nApproval-required JSON looks like:\n\n```json\n{\n  \"status\": \"error\",\n  \"workflow\": \"expense-report\",\n  \"exitCode\": 5,\n  \"submitted\": false,\n  \"requiresApproval\": true,\n  \"error\": {\n    \"code\": \"approval_required\",\n    \"message\": \"Approval required: run with --dry-run to preview or --approve to submit.\"\n  }\n}\n```\n\n## Current Scope\n\nThis is an early MVP. It currently records named form fields and a submit selector from a live page. It detects common login, CAPTCHA, and MFA walls as safe stops, but does not bypass them. It does not yet implement arbitrary event-history replay, credential storage, hosted execution, or selector healing.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodeyoma%2Fformctl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodeyoma%2Fformctl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodeyoma%2Fformctl/lists"}