init
Scaffold a new integration project with all necessary files and configuration.Flags
string
required
Kebab-case identifier for the integration (e.g.,
"acme-crm"): lowercase
[a-z0-9] segments joined by single hyphens. Required — init fails with a
usage error (exit 2) naming the flag rather than prompting.string
Target directory. Defaults to
./<slug> — a new directory under the current working directory. Pass --dir . to scaffold in the current directory (useful for existing projects).boolean
Skip interactive confirmations. Accepted for non-interactive ergonomics;
init never prompts regardless.string[]
An http(s) URL or project-relative path to the target API’s documentation.
Repeat the flag for multiple entries. Written into
ekanos.json as the docs
field; without the flag the field is omitted. An absolute path, a ..
segment, or a non-http(s) scheme is a usage error (exit 2).boolean
Overwrite files that already exist. Without it, an existing file is left
untouched and reported in
data.skipped[].What it creates
ekanos.json— Project configuration (slug, entry)src/integration.ts— A workingdefineIntegration()startersrc/integration.test.ts— A starter testtsconfig.jsonandvitest.config.tspnpm-workspace.yaml— Approvesesbuild’s build script (allowBuilds: { esbuild: true }) so pnpm 10+ does not fail withERR_PNPM_IGNORED_BUILDS; npm and yarn ignore itscripts/setup-hooks.mjs— Wired via thepreparescriptpackage.json— Scripts and dependencies (merged into an existing one). Pins@ekanos/sdk,@ekanos/uiand@ekanos/harnessto^0.4.0,@ekanos/clito^0.3.3, and includesnext-intl(^4.14.7), the peer of@ekanos/uiand@ekanos/harnessdocs/api-notes.mdand.mcp.json— Docs-grounding files for coding agents- Agent-context files —
AGENTS.md,CLAUDE.md, and Claude Code skills under.claude/skills/(ekanos,build-integration,polish-widgets) .gitignore— Includes.ekanos/
Special handling for existing projects
Wheninit runs on a project that already has a package.json:
- Missing dependencies are added; existing ones are left alone
- npm’s stub test script (
"echo \"Error: no test specified\" && exit 1") is replaced with a real one "type": "module"is set (or kept) even if you had"commonjs", with a loud note. The scaffolded files are ESM TypeScript and cannot load under CommonJS.
init does not write is reported with a hint saying what to do about it: data.skipped[] carries { file, reason, hint }, and data.manifest.notes[] carries the same for package.json fields it could not resolve.
validate
Parse the integration definition and validate it against the canonical schemas.Flags
string
Project directory (contains
ekanos.json). Defaults to the current working
directory.Exit codes
0— No error-severity findings (warnings may still be listed indata.findings)3— Error-severity finding(s) found (seedata.findings)6— Noekanos.jsonin the directory, or anentrypath that does not exist9—ekanos.jsonis invalid,package.jsonis not"type": "module", or schema version skew (the project’s schema is newer than the CLI’s)
What it checks
- Schema version — Fails fast (exit 9) before loading anything if the project resolves a newer schema than the CLI bundles
- Schema validation — Loads your definition via esbuild and validates it against the real
@ekanos/integration-schemazod schemas - Slug agreement —
sluginekanos.jsonmust match the definition (an error) - Collisions — Slugs, widget ids, and tool names across every integration in the project
- Project checks — The required vitest SDK-inline block, local
docspaths, and version agreement betweenpackage.jsonand the definition (a warning)
data.findings — an array of { check, severity, file?, line?, message, hint }, where severity is error, warn, or info. Fix error-severity findings to reach exit 0.
publish runs the same findings pass before submitting, and refuses on any
error-severity finding.dev
Scaffold the local harness and run it with Next.js.Flags
string
Project directory (contains
ekanos.json). Defaults to the current working
directory.string
Port for the dev server. Defaults to
3100.string
Interface to bind. Defaults to
127.0.0.1 (loopback only). Pass another
address to expose on your network — prints a warning first.boolean
Rewrite every generated file even when its bytes already match.
dev always materializes the harness first (harness.config.ts once, then the generated .ekanos/harness/ shell). Whether it then starts the server depends on the output mode: in an interactive terminal a bare dev starts it, and in JSON mode (--json, a pipe, or CLAUDECODE=1) a bare dev returns after scaffolding, so an agent or script is never handed a process that never exits. --start forces the start in either mode; --no-start forces scaffold-only. Use dev --start in anything you document or automate.
boolean
Start the dev server even in JSON mode. By default, JSON mode (
--json, a pipe, or CLAUDECODE=1) scaffolds and returns without starting; data.server carries { started: false, command, url, port } so the caller can start it.boolean
Scaffold only; don’t start the server. Useful for inspecting the generated
files or starting Next manually.
Before you run
The harness needs these dependencies in your project:dev fails with precondition_failed (exit 9) before writing anything if any of those packages is missing, and with invalid_state (exit 7) if .ekanos/harness exists but was not generated by dev (pass --force to overwrite it). If the started server exits non-zero, dev passes Next’s exit code through with error code dev_server_failed.
What it generates
The harness generates files into.ekanos/harness/ (gitignored, rewritten freely):
harness.config.ts at your project root. Everything else under .ekanos/ is generated and never edited by hand.
Modes
Fixtures mode (default) renders your integration with test data fromharness.config.ts. No vendor API calls, no OAuth loop.
Live API mode (selected from the toolbar’s Data dropdown; the harness entry needs a live block) can, for an integration that declares oauth, run the full OAuth authorize → callback → code-exchange → onTokens loop against your own developer app:
- In the Local OAuth panel on the Activation surface, paste the secrets your definition names and Save credentials — they are written to
.ekanos/local-secrets.json(gitignored, never packed) - Click Connect —
onTokensruns, thenonActivate - Widgets read what
onTokens,onActivate, and schedules wrote
Security
The harness has no authentication and renders your fixtures (or real vendor data in live mode). Binding to0.0.0.0 by default would expose this to anyone on the network.
devbinds to127.0.0.1(loopback only) by default--host 0.0.0.0opts in explicitly after printing a warning- The shell’s API routes (OAuth, storage) refuse non-loopback requests unless
EKANOS_HARNESS_EXPOSED=1is set
test
Delegate to the project’s own test script.Flags
string
Project directory (contains
ekanos.json). Defaults to the current working
directory.boolean
Skip the structured appendix and pass through the runner’s output as-is.
Useful if the auto-detection guesses wrong.
-- is forwarded verbatim to the test runner.
Exit code
Once the test script runs, the CLI’s exit code is a transparent passthrough of the runner’s exit code; a failing run carries error codetest_failed. (A project that cannot be loaded fails first, with exit 6 or 9.)
Structured output
When your project’s test script uses vitest, the CLI also asks for a JSON report alongside the normal output. On failure, a curated “FAILURES” appendix renders after the runner finishes, mirroring the server-side gate’s own grading. Pass--raw to skip the appendix.
login
Authenticate this machine against an Ekanos deployment.Flags
string
Ekanos host origin (http or https). Falls back to the
EKANOS_HOST
environment variable or the sole stored host. See Host
resolution.boolean
Start a fresh login even if a working session is already stored.
How it works
- The CLI prints a short code and a browser URL to stderr
- Open the URL in any browser on any device
- Approve the request — no need to be on the same machine
- The CLI polls and confirms when approved
Credentials
Your session (Auth0 refresh token + Ekanos JWT) is stored in~/.ekanos/credentials.json with mode 0600, keyed by host. If the file or directory is group/world-accessible, the CLI tightens permissions and warns on stderr.
Tokens are never printed — not by output, not in logs, not at any verbosity level.
Idempotence
- Logging in with a working session reports
already-authenticatedand exits 0 without opening a browser - Use
--forceto start a fresh login regardless - Two processes sharing one credentials file can race on a token refresh; the loser exits 4 (
auth_required) — re-runekanos login
logout
Revoke the session and delete local credentials.Flags
string
Ekanos host. Falls back to
EKANOS_HOST, then host in the current
directory’s ekanos.json, then the sole stored host (see Host
resolution). With several stored hosts
and no other answer, it fails with a usage error.boolean
Sign out of every host in the credentials file.
Idempotence
Logging out twice exits 0. The second run reportscleared: false — information, not a failure.
Revocation is attempted at Auth0 but never blocks the local delete. A logout that only forgets the token would leave a rotating credential live; a logout that refuses because the network is down would leave the token on disk. So: try to revoke, always delete, report both.
whoami
Print the authenticated identity and seats.Flags
string
Ekanos host. Falls back to host resolution.
Exit codes
0— Authenticated and verified server-side2— No host could be resolved4— No session, expired session that couldn’t be renewed, or revoked session8— The host was unreachable
seats field is a best-effort add-on from the source-visibility endpoint. Its failure (network, MFA, old deployment) degrades to seats: null rather than failing the whole command.
sources
List every source you hold a developer or source-admin seat on.Flags
string
Ekanos host. Falls back to host resolution.
string
Project directory whose
ekanos.json source field is marked as the current
target. Defaults to the current working directory.Output
data is { host, sources, activeSource }, where each source is { id, slug, name, roles } and activeSource is the project’s source field (or null). Human mode marks the current publish target with *.
Run this before publish to see which sources you can actually publish to.
use
Set this project’s publish target to a source you hold a seat on.Flags
string
Ekanos host. Falls back to host resolution.
string
Project directory (contains
ekanos.json). Defaults to the current working
directory.Behavior
The source slug is a required positional argument (missing → usage error, exit 2), anduse must run inside a project. The CLI verifies that you actually hold a seat on that source — a typo or stale slug is refused with forbidden (exit 5) and your real seat list in the hint.
On success use writes source to ekanos.json. It writes host only if ekanos.json has no host yet; an existing host is never overwritten.
status
Read the login state, project configuration, and submission history.Flags
string
Ekanos host. Falls back to host resolution.
string
Project directory. Defaults to the current working directory.
boolean
Block until the project’s current submission (its slug at the
package.json
version) reaches gated, dev, or rejected, polling every 30 seconds.
Requires a live session and a source in ekanos.json. Changes the exit-code
contract (see below).boolean
Render each version’s full finding list, not just error/warn counts.
boolean
Human mode only. Expand “OTHER INTEGRATIONS IN SOURCE” from a summary line
into the same version/state/age/findings table.
Exit codes
Normally:0 whenever the report could be assembled — being logged out is reported as authenticated: false, not an error.
With --watch: 0 when the submission reaches gated or dev; 10 (gate_failed) when it is rejected, with the gate’s findings in data.findings. No session exits 4; no project or no source exits 9.
Three sections
Session — Your server-side identity (orauthenticated: false if not logged in).
Project — The ekanos.json in --dir (omitted if no project exists).
Submissions — Your submission history from this source (only when both a session and a source exist).
publish
Validate, pack, and submit the integration.Flags
string
Ekanos host. Falls back to host resolution.
string
Project directory (contains
ekanos.json). Defaults to the current working
directory.string
Ekanos source slug. Falls back to
ekanos.json’s "source" field. When
passed explicitly, acts as consent and skips the interactive confirm.boolean
Skip the interactive confirm when the source is implicit (from
ekanos.json)
and you hold multiple developer seats. No effect when --source is explicit
(already consent) or when you hold exactly one seat (nothing to disambiguate).Publish-target verification
Before packing or submitting anything, the CLI fetches your seat list and applies safety rules:
For agents and CI: pass
--source explicitly or run ekanos sources first to choose deliberately. Never pass --yes through an ambiguity you haven’t resolved.
What it does
- Requires a stored session (exit 4) and runs the schema-skew gate (exit 9)
- Refuses if the host has declared this CLI version unsupported (exit 9)
- Resolves the source (
--source, elseekanos.json); none →validation(exit 3) - Runs the same findings pass as
validate, withproject.host-compiler-rulesescalated fromwarntoerror. Any error-severity finding refuses the publish withgate_failed(exit 10) and the findings indata.findings - Fetches your seat list and applies the verification rules above
- Packs a gzipped tarball of
ekanos.json,package.json,README.md, roottsconfig*.jsonand vitest config, and everything undersrc/(4 MiB limit) - Submits it to the host at the
package.jsonversion and returnsdata.source: { id, slug, name },submissionId, andstate
0 success, 2 usage, 3 validation, 4 auth required, 5 forbidden, 6 not found, 7 version already submitted, 8 network, 9 precondition failed, 10 gate failed.
upgrade
Move the toolchain to a coherent target version with verify-and-rollback.Flags
string
Project directory (contains
ekanos.json). Defaults to the current working
directory.boolean
Read-only. Report current vs. target versions and exit 0. Changes nothing.
Safe to run anytime, including from CI.
boolean
Skip the interactive confirm. Required outside a real TTY; without it a
non-interactive run fails with
validation (exit 3) and changes nothing.boolean
Skip the post-install verification (validate + test script). Not recommended.
string
Ekanos host for fetching the target version set. Falls back to host
resolution. When no host session is available, the CLI uses the npm registry
instead.
Coherence guarantee
upgrade resolves a coherent target version set: @ekanos/sdk, @ekanos/ui, and @ekanos/integration-schema must all share one version, and the SDK release must pin exactly that schema. A mid-publish registry state is refused rather than turned into an uninstallable tree.
Verification
On success, the CLI:- Snapshots
package.jsonand the lockfile - Installs the target versions of the toolchain packages that are direct dependencies of the project (transitive ones follow from whatever pins them and are reported in
data.skippedTransitive) - Verifies with
ekanos validateand yourtestscript - Rolls back
package.jsonand the lockfile, then reinstalls to restorenode_modules, on any failure. The error code names the step:upgrade_install_failed(exit 9),upgrade_validate_failed(exit 3), orupgrade_test_failed(your test script’s exit code), with the step’s output indata.output
AGENTS.md, CLAUDE.md, the Claude Code skill) are regenerated from the newly installed CLI’s templates, byte-compared against what the pre-upgrade CLI would have rendered. Files you haven’t touched are refreshed; edited ones are left alone and reported as needing manual attention.
A stale AGENTS.md teaches an agent yesterday’s contract, which is as harmful as a stale binary.