The development lifecycle
Building an integration follows a repeatable cycle: init → develop locally → validate → test → publish.1. Initialize (ekanos init)
src/integration.ts— a starter definition with one widget, one tool, and a commented-out example action — plussrc/integration.test.tsekanos.jsonpointing to your entry filepackage.jsonwith the published@ekanos/*dependenciestsconfig.json(withnoUncheckedIndexedAccess),vitest.config.ts, and.gitignore- A pre-commit hook (
scripts/setup-hooks.mjs, wired throughprepare) that runsekanos validate;SKIP_EKANOS_HOOK=1skips it - Agent-context files:
AGENTS.md,CLAUDE.md, a Claude Code skill,docs/api-notes.md,.mcp.json
harness.config.ts is not created by init — the first ekanos dev writes it.
init never prompts for the slug: --slug <name> is required, and a missing slug is a usage error. In a real terminal it shows an interactive summary; CI, --json, and piped output are unaffected. Use --dir . to scaffold in the current directory instead of creating ./<slug>.2. Develop locally (ekanos dev)
.ekanos/harness/ (regenerated on every run) and runs it with your project’s own Next, on http://localhost:3100 by default. The harness:
- Renders your surfaces in real Ekanos chrome — the dashboard grid, the marketplace tile, the activation form, and Triggers
- Serves in fixtures mode by default — requests are answered from your recorded fixtures; an unrecorded request is refused, never sent
- Optionally runs live mode — an entry with a
liveblock can switch the toolbar to Live API and reach your vendor’s real API
Fixtures mode (default)
Declare HTTP exchanges inharness.config.ts:
defineHarnessConfig comes from @ekanos/harness/config and asHarnessDefinition from @ekanos/harness/registry. Variants are default, empty, and error; an omitted variant is not a fallback to default. A request with no matching fixture rejects with NoRecordedResponseError, whose message quotes the fixture line to paste.
Live mode (opt-in)
Add alive block to your harness entry:
egress allowlist. An entry with a live block still opens in fixtures mode unless it declares live: { default: 'live' }.
Local OAuth
For an integration whose definition declaresoauth, live mode runs the complete authorize → callback → onTokens loop against your own developer app (no Ekanos host required).
- Switch the toolbar to Live API (the entry needs a
liveblock) - On the Activation or Triggers surface, open the Local OAuth panel and follow its checklist for your provider’s developer console, including the redirect URI
http://localhost:3100/api/integrations/<slug>/oauth/callback - Paste the client id and secret into the panel’s form and Save credentials — they are written to
.ekanos/local-secrets.json(gitignored, never packed byekanos publish) - Click Connect in the activation dialog — the flow runs on localhost, and
onTokensthenonActivaterun on the live context
3. Validate (ekanos validate)
- Strict schema adherence (unknown keys are errors)
- Naming rules for slugs, widget ids, tool names, webhook/schedule/action names, and storage keys
- No duplicate ids within one integration, and no collisions across all integrations in the project’s
ekanos.json - OAuth endpoint origins covered by your
egresslist, and cron syntax project.slug-agreement— theekanos.jsonslug matches the definition’sproject.vitest-inline-sdk— the vitest config inlines@ekanos/sdkproject.host-compiler-rules(warning) —tsconfig.jsonsetsstrictandnoUncheckedIndexedAccess.validatedoes not compile your code;publishescalates this finding to an error- Advisory warnings such as
icons/pro-onlyanddocs/missing-file
0— No error-severity findings2— Usage error3— Error-severity validation findings (warnings alone do not fail)9— Precondition failed (for example, the project’s@ekanos/integration-schemais newer than the CLI’s)
publish refuses to submit otherwise.
4. Test (ekanos test)
test script through its package manager. For a vitest script, failures get a curated FAILURES appendix; --raw skips that.
The SDK provides testing utilities:
checks/tests stage runs your packed test suite, so keep it green locally.
5. Publish (ekanos publish)
First, authenticate:
use refuses a slug you hold no seat on):
- Runs the same findings pass as
ekanos validate, refusing withgate_failed(exit 10) on any error-severity finding - Packs
ekanos.json,package.json,README.md,src/**, and your roottsconfig*.jsonand vitest/vite config - Submits the archive to the host
- Exits once the submission is accepted — the gate runs asynchronously
publish does not run your tests locally. With several seats and a target taken implicitly from ekanos.json, a terminal run asks you to confirm; a scripted or JSON run needs --source or --yes. The first successful publish saves host and source to ekanos.json.
Watch progress:
checks/typecheck— yourtscagainst your packedtsconfig.jsonchecks/typecheck-host-rules— a second pass under the host’s stricter compiler ruleschecks/tests— your packed test suite
status --watch polls every 30 seconds and exits 0 when the version reaches gated or dev, or 10 (gate_failed) with the findings when it is rejected. ekanos status --findings shows every finding.
The gate is not instant. It runs asynchronously. Watch with
ekanos status --watch or come back later.6. Keep the toolchain current
Check whether a newer toolchain exists (read-only):- Resolves a coherent target version set — from your logged-in host when available, else the npm registry
- Snapshots
package.jsonand the lockfile - Installs the target versions
- Verifies with
ekanos validateand your test script (--no-verifyskips this) - Rolls back
package.json, the lockfile, andnode_modulesif any step fails
--yes skips the interactive confirm and is required outside a real terminal.
Common lifecycle issues
Definition validation throws at import time
Definition validation throws at import time
This is correct behavior.
defineIntegration() validates when the module is imported. The error message names the invalid path and what’s wrong.Harness won't start after scaffolding
Harness won't start after scaffolding
Run
pnpm install. The scaffold creates a package.json that needs a full
install before ekanos dev passes its precondition check.The ekanos.json slug disagrees with the definition
The ekanos.json slug disagrees with the definition
ekanos validate reports a project.slug-agreement error. The slug appears in two places and must agree:- In
ekanos.json:{ "slug": "my-integration" } - In your definition:
defineIntegration({ slug: 'my-integration', … })
ekanos validate again.Validation passes locally but fails the host gate
Validation passes locally but fails the host gate
Common causes: - TypeScript rules drift —
checks/typecheck-host-rules
compiles under stricter rules than a lax local tsconfig.json. Set strict
and noUncheckedIndexedAccess to true. - Config missing from the
archive — CLI versions before 0.1.10 did not pack tsconfig.json or the
vitest config. Upgrade and republish. - Toolchain skew — Run ekanos upgrade --check.I want to develop without publishing
I want to develop without publishing
Use
ekanos dev to iterate locally, and skip publish until you’re ready.