Commit 6a4b09d
committed
INF-1307 initial twoctl
Public Go CLI for the Two merchant APIs.
Coverage
- Every operation in checkout, billing-account, repay, recourse, company,
and limits is surfaced as `twoctl <api> <op>` (72 operations total).
- The CLI is spec-driven: cmd/twoctl/cli/operations.go embeds the six
processed OpenAPI specs and walks them at startup, so the command tree
tracks the upstream OpenAPI surface 1:1 with no per-operation Go code
to maintain. Path, query, and header parameters surface as flags; the
request body is passed via --file or --data.
- internal/preprocess rewrites OAS 3.1 nullable patterns into a 3.0-
compatible shape (anyOf:[T,null], type:[T,null], bare type:null,
const-without-required) so the openapi3 parser can consume the upstream
specs.
Contexts (kubectl-style)
- Named contexts each bundle a base URL and an OS-keychain entry holding
the API key. `twoctl ctx set/use/list/current/delete` manage them.
- Built-in env aliases: prod, sandbox, staging, cyber, perf, release.
Unknown names fall back to https://api.<name>.two.inc.
- Per-invocation overrides without switching: --env / --context / --url /
--api-key. Key resolution order: --api-key, TWO_API_KEY, keychain.
Agent + human UX
- `twoctl catalog [-a <api>]` emits a JSON catalog of every operation
(command path, method, URL template, flags with kind/type/required,
body status) for agent planners.
- `twoctl <api> <op> --describe` prints the operation's parameter,
request body, and response schemas as JSON without a network call.
- `twoctl <api> <op> --dry-run` shows the HTTP request that would be
sent (URL, redacted headers, body presence). No credentials needed.
- Structured stderr error envelope on failure: {error, message, status,
response}. Stable exit codes: 0 ok, 1 generic, 2 usage, 3 auth,
4 not-found, 5 rate-limited, 6 server, 7 network.
- Required parameters render as "(path, required)" in --help.
- Shell completion for bash/zsh/fish/powershell via cobra; enum-valued
parameters get value completions registered automatically.
Self-upgrade
- Every invocation runs an at-most-daily check against the GitHub
releases API. If a newer release is available and the user has not
skipped it, a prompt offers [y] install / [n] not now / [s] skip.
- `twoctl upgrade` runs the check + install on demand;
--reset-skips clears the skip list (escape hatch);
--disable-autocheck / --enable-autocheck toggle the daily check;
--no-upgrade-check is a per-run script bypass.
- State persisted to $XDG_CONFIG_HOME/twoctl/state.json. Network
failures during the check are silent so they never block the command.
- Binary swap via minio/selfupdate; supports tar.gz and zip archives.
Tooling + CI
- Makefile with build / install / test / test-cover / lint / regen /
tidy / release-dry / clean targets.
- scripts/lint.sh runs every Go Report Card check (gofmt -s, go vet,
ineffassign, misspell, gocyclo > 15, golint) and exits non-zero on
any output - lint job in CI enforces it.
- Self-managed test coverage at 80.8% across the project.
- .github/workflows/regenerate.yml: repository_dispatch from two-inc/docs
on openapi/* changes pulls the fresh specs, runs codegen, opens a PR.
Nightly cron as safety net.
- .github/workflows/release.yml + .goreleaser.yaml: tag push builds
darwin/linux/windows binaries and updates two-inc/homebrew-tap.
- docs/notify-twoctl.yml.example: drop-in workflow for two-inc/docs to
fire the dispatch.
Linear: https://linear.app/tillit/issue/INF-13070 parents commit 6a4b09d
39 files changed
Lines changed: 30324 additions & 0 deletions
File tree
- .github/workflows
- cmd/twoctl
- cli
- specs
- docs
- internal
- config
- httpx
- preprocess
- updater
- openapi
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
0 commit comments