@mochabug/adaptkit
v2.0.0-beta.19
Published
Create, build, emulate, and publish Mochabug Adapt apps
Downloads
1,450
Readme
@mochabug/adaptkit
Adaptkit is the Node.js 24 CLI for creating, extending, emulating, and publishing Mochabug Adapt apps.
Node.js 24 or newer is required on the development machine, including for
init. The CLI rejects older Node versions before creating project files.
npm install --global @mochabug/adaptkit@latest
adaptkit initCommands
adaptkit init [options]
adaptkit add [options]
adaptkit skill [--dir <project>]
adaptkit emulator setup
adaptkit emulator [--tag <tag>] [--no-open]
adaptkit emulator stop
adaptkit emulator logs
adaptkit emulate [--dir <project>] [--upload dist/upload.json]
adaptkit publish [--dir <project>] [--upload dist/upload.json] [--staging] [-c <client>]
adaptkit version patch
adaptkit version minor
adaptkit version majorinit and add accept every scaffold choice directly. Supplied values bypass
the wizard; an interactive terminal prompts only for missing choices. With
--no-interactive or non-TTY stdin, missing required choices are errors. Invalid
choices, names, folders, and program selections fail before files or dependencies
change.
adaptkit init --dir ./my_app --name my_app --vertex greeting \
--capabilities browser configurator --endpoints http --api connect \
--frontend react --ssg --no-interactive
adaptkit add --dir ./my_app --name worker --capabilities external \
--endpoints tcp --executor-program new --no-interactive
adaptkit add --dir ./my_app --name transform --capabilities none \
--executor-program 1 --no-interactive| Option | Applies to |
| --- | --- |
| --dir <directory> | Both commands; defaults to . |
| --name <name> | App name for init, vertex name for add |
| --vertex <name> | init first vertex |
| --capabilities <values...> | browser, configurator, cron, external, or none alone |
| --endpoints <values...> | Full executor set: http, tcp, datagram; required for Browser/External, forbidden otherwise |
| --api http\|connect | Required for Configurator or executor HTTP; forbidden without HTTP roles |
| --frontend typescript\|react | Required for Browser/Configurator, forbidden otherwise |
| --ssg / --no-ssg | Explicit choice required for browser pages, forbidden otherwise |
| --folder <path> | add; defaults to src/<name> |
| --executor-program <number\|new> | add; required without interactive input |
| --configurator-program <number\|new> | add with Configurator; required without interactive input, forbidden otherwise |
| --no-interactive | Both commands; fail on missing choices without prompting, even in a terminal |
Browser requires http in the executor endpoint set. Datagrams require External.
Connect is an API protocol over HTTP, not another executor endpoint. For example,
--capabilities external configurator --endpoints tcp --api connect generates a
TCP executor and a separate Connect configurator; it adds no executor HTTP service
or executor proto.
Program numbers are one-based indices in the corresponding runtime.json array.
new appends a program using js_1.0, Node 24 compatibility, 64 MB memory,
30 seconds wall time, and 100 ms CPU time. Existing definitions and array order
are preserved. add is entirely local.
When Connect RPC is selected for the first time, add creates the Buf
configuration and validated role-specific sample proto files, installs any
missing local project dependencies, updates the build commands once, and
generates the TypeScript. Later Connect vertices reuse that setup.
Agent skills
init installs the bundled adapt-app and adapt-emulator skills automatically.
Install or refresh them in an existing project without running init:
adaptkit skill
adaptkit skill --dir ./my_appBoth commands install in .agents/skills at the nearest Git checkout root.
Outside Git, they use the directory containing the nearest manifest.json.
skill defaults to the current directory and also works from project subdirectories
or a Git checkout root containing multiple apps.
Each run replaces those two skill directories with the bundled files, including references.
Claude Code gets a relative symlink for each skill in .claude/skills, pointing
to the shared copy in .agents/skills. Claude Code explicitly supports
symlinked project skill folders.
If the system cannot create symlinks, Adaptkit installs copies in .claude/skills
and refreshes both locations on each run.
Other skills and instruction files remain unchanged.
Skills can be installed before an app exists. For an uninitialized app directory,
adapt-app asks the user about the app and vertex structure, then runs
adaptkit init before writing app code or configuration.
App READMEs are public-facing instructions for non-technical users; keep developer
setup and implementation details in separate documentation when needed.
Adaptkit updates
Interactive commands check npm for Adaptkit updates in a detached background
process, at most once a day. A small notice after the command shows the current
and available versions plus the appropriate install command for a global,
project-local, or npx installation. Long commands can show a fresh result;
short commands use it on the next invocation. Notices appear at most once a day
for each available version. Updates always follow npm’s latest tag, regardless
of the installed version’s suffix. No beta or other release channels are used.
Checks never delay commands for network access, and offline/cache failures stay
silent. CI and non-interactive output skip both checking and notices. Set
NO_UPDATE_NOTIFIER=1 or pass --no-update-check to opt out. Updates are installed
only when you run the suggested command. Output uses the same subtle colors as
Adapt Builder and respects NO_COLOR and redirected output.
Local emulator
Install Docker Desktop (or
Docker Engine on Linux) and mkcert.
On macOS, brew install mkcert nss also enables Firefox trust-store support.
Start Docker, then run:
adaptkit emulator setup # once per user; rerun to renew certificates
npm run emulator # start in the background, wait until ready, open browser
npm run emulate # build and upload
npm run emulator:logs # follow logs; Ctrl+C leaves the emulator running
npm run emulator:stop # stop and discard temporary stateSetup explains and installs local certificate trust using mkcert. It may ask for
an administrator password or OS approval so the browser can trust app and
configurator pages on *.emulator.localhost. Run Adaptkit as your normal user,
without sudo. Certificates are private to your user under
~/.config/adaptkit/emulator/certs; only the server key/certificate and public
CA certificate are mounted read-only into Docker. The CA private key stays with
mkcert. Restart your browser after setup, and restart a running emulator after
renewing certificates. Setup uses your installed tools and does not install Docker
or mkcert or change Docker socket permissions.
The emulator image is about 1 GB. The Adapt runtime itself is only tens of
megabytes; most of the size comes from FileForge's PDF and image-processing
dependencies. When starting a new session, the default
mochabug/adapt-emulator:latest always checks Docker Hub for updates, reusing cached
layers. Version tags are immutable and are pulled only when missing locally:
npm run emulator -- --tag 1.0.6
# Equivalently: adaptkit emulator --tag 1.0.6Startup waits for the emulator’s ready signal and checks that its local page
responds before opening http://localhost:51001 in your browser. Use
adaptkit emulator --no-open to skip opening a browser (also skipped in CI).
The container keeps running after the command exits or the terminal closes.
A startup failure or timeout points to adaptkit emulator logs. Ports 51001–51003
TCP and 51003 UDP are bound to localhost.
Repeated starts report already running and preserve the current session,
without pulling or restarting it, and reopen the ready emulator in your browser.
Stop first to change tags or refresh latest.
adaptkit emulator stop shuts down services and removes the container, discarding
uploaded apps, configuration, and run state while keeping the downloaded image.
Repeated stops succeed with already stopped. One container named
adapt-emulator is shared across projects in the selected Docker context.
Adaptkit only manages containers bearing its own label; a conflicting name gets
an actionable error. Unexpectedly exited containers retain their logs until the
next start or stop.
npm run emulate uploads to the running emulator; it does not start Docker.
If Docker is missing, install it and reopen your terminal. If the daemon is not ready, start Docker Desktop or your Docker Engine service and check your Docker context. On Linux, follow Docker's user-access setup if socket access is denied; do not run the whole workflow with sudo. Startup checks Docker and certificate access before downloading the image. Docker's original pull/start errors remain visible, including occupied ports, blocked certificate-directory sharing, unavailable tags, connectivity, and disk space.
Generated projects
init creates a project that builds without an adapt.config.ts:
manifest.json
runtime.json
README.md
package.json
tsconfig.json # solution with separate environments
tsconfig.runtime.json
tsconfig.browser.json
tsconfig.tools.json
buf.yaml # when Connect RPC is selected
buf.gen.yaml # when Connect RPC is selected
buf.lock # generated when Connect RPC is selected
proto/
<app>/<vertex>/
exec/exec.proto # package <app>.<vertex>.exec
config/config.proto # package <app>.<vertex>.config
src/
gen/<app>/<vertex>/ # generated when Connect RPC is selected
exec/exec_pb.ts
config/config_pb.ts
<vertex>/
vertex.json
metadata.json
executor.ts
configurator.ts # when selected
ui/ # for Browser or Configurator
api.ts # typed API objects and real requests
api.mock.ts # local mock responses
styles.css # linked from each HTML head
<role>.html
<role>.main.ts # .tsx for React
<role>.ssg.ts # .tsx for React; only with SSG
<role>.App.tsx # React only; shared by rendering and hydrationThe generated project explicitly installs npm's latest tag for the Adapt
toolchain and application dependencies, with Node declarations kept on major 24.
Scaffold installs explicitly include development dependencies and
executable links, even if npm is configured to omit them. Initialization checks
the local adapt-builder executable before reporting success. npm output and
errors stream directly to stdout.
The generated README.md is a starter for public app documentation. Replace its
placeholders with the app's purpose, setup and usage instructions, data use,
connected services, limitations, and support information before publication.
The root TypeScript config references three independent projects:
| Config | Sources | Types |
| --- | --- | --- |
| tsconfig.runtime.json | Runtime entries and their imports | Builder runtime (includes Node 24), TypeScript DOM |
| tsconfig.browser.json | UI and UI shared code; excludes SSG and tests | TypeScript DOM and vite/client, no Node |
| tsconfig.tools.json | Local config, SSG, tests, and scripts | Node, DOM and Vite |
These are self-contained project configs, copied by Adaptkit. The root owns
common compiler options and excludes SSG/tests from application projects; the
browser and runtime configs inherit those exclusions. Tools explicitly reset
exclude so local SSG/tests are included. Put shared overrides in the root and
environment-only settings in the relevant leaf. Arrays such as types and
include replace inherited arrays. For Vitest globals, set the tools config's
types to ["node", "vite/client", "vitest/globals"]; install Vitest when adding
tests. DOM declarations supply types; DOM-dependent tests also need the test
runner's DOM environment.
The backend uses standard DOM declarations for Fetch types required by Connect RPC.
These declarations also support server rendering; they do not provide browser
globals such as window or document at runtime. Keep browser API use in browser code.
Generated projects install the latest Node 24 typings to match their node24 runtime profile.
Native handler and environment types are imported from
@mochabug/adapt-builder/runtime. Shared protobuf and utility code stays neutral
and is checked in each environment that imports it. The builder checks all three
projects with strict application checking and skipLibCheck: true by default.
Dependency declarations still supply application types but are not checked
themselves. Environment configs store TypeScript's caches in node_modules/.cache/typescript,
with no root .tsbuildinfo files or dedicated Git ignore rule.
Native outbound calls use
env.service.http(url, options) or http(url, options) from mb:http; write the
body through the returned exchange. See the
builder's outbound examples
for configured bindings, direct URLs, and transport options.
Native outbound modules each have one named export:
import { http } from 'mb:http';
import { connect } from 'mb:connect';
import { datagram } from 'mb:datagram';Call these from an active handler. connect opens a raw TCP connection;
datagram opens an RFC 9221 QUIC DATAGRAM session. Connect RPC remains an HTTP
operation. Handler and socket types come from @mochabug/adapt-builder/runtime.
Useful scripts are ready immediately:
npm run build
npm run build:staging
npm run build:emulate
npm run dev
npm run emulate
npm run publish
npm run publish:staging
npm run addConnect projects also provide npm run generate for locked protobuf generation
and npm run protobuf:update for explicitly refreshing Buf dependencies. Normal
build and dev commands generate sources without changing buf.lock.
Browser code imports executorApi or configuratorApi from ui/api.ts and calls
methods such as await executorApi.greet({ name: "Adapt" }). api.ts selects
api.mock.ts once during local development. Connect uses the generated service
types for both its real client and its in-memory mock transport; mock handlers
satisfy ServiceImpl<typeof Service>. Plain HTTP declares an Api interface in
api.ts, which the mock imports with import type. Neither needs another API
file or custom builder configuration. All artifact builds exclude the mocks.
External HTTP routers advertise HTTP/3 by default. Browser vertices always have
one executor HTTP entrypoint. Browser and External capabilities share that
entrypoint and its <VertexName>ExecService; they do not create duplicate hosts
or services. Configurators use a separate <VertexName>ConfigService. Executor
and configurator services are always emitted into separate proto files and
generated modules. Their package and folder namespace is only
<app>.<vertex>.exec or <app>.<vertex>.config—there is no framework root
or API-version directory. Both roles use the minimal GreetRequest and
GreetResponse message names.
Connect registrations expose Connect, gRPC, and gRPC-Web,
validate requests with Protovalidate, and use the role-specific browser client
from ui/api.ts. Browser clients explicitly use the Connect transport; the
runtime selects Connect, gRPC-Web, or gRPC for each incoming client. A Connect
scaffold does not add a duplicate conventional API
route; non-Connect browser paths serve the frontend. The HTTP API example uses /greet. Raw
TCP and datagram handlers use the builder's runtime contracts directly.
Generated HTTP and Connect operation routes require Adapt bearer authorization. Browser and configurator page shells remain public for initialization. Add new operation routes to the protected paths. External-only HTTP routers protect all routes by default. Raw TCP and datagram scaffolds close connections until you implement protocol authorization.
Generated proto comments call out the transport boundary: browser clients do not support client- or bidirectional-streaming RPCs. An External-only executor keeps full gRPC enabled over HTTP/2, including the complete streaming surface; the HTTP/3 alternative-service advertisement does not replace HTTP/2.
Environments
npm run dev is local Vite development: mode development, with api.mock.ts.
Production, hosted staging, and emulation are full artifact builds using mode
production and real APIs. CDN/destination differences never select mocks.
The generated scripts keep the build and destination aligned:
| Workflow | Browser CDN | Upload service | Authentication |
| --- | --- | --- | --- |
| npm run publish | https://cdn.mochabug.com/adapt/plugins | https://adapt.mochabugapis.com | OAuth over HTTPS |
| npm run publish:staging | https://cdn-dev.mochabug.com/adapt/plugins | https://adapt-dev.mochabugapis.com | OAuth over HTTPS |
| npm run emulate | https://localhost:51002 | http://localhost:51001 | none |
adaptkit publish --staging selects the staging authorization, audience, account,
and upload services. Each workflow owns its destination and transport. Production
and staging require verified TLS; emulation uses the local HTTP API without
authentication. There are no endpoint or insecure-TLS overrides.
Publishing opens your browser for sign-in and always prints a link you can open manually. If the browser launcher fails, Adaptkit reports the error and keeps waiting for sign-in through that link. Sign-in allows 10 minutes; the browser launcher allows 60 seconds, and the token exchange allows 2 minutes. OAuth and network errors are reported in the terminal. Press Ctrl+C to cancel and close the local callback listener.
Staging publishing accepts the optional -c, --client <id> OAuth client override.
Choose a custom staging organization when building, using Adapt Builder's -o:
npm run build:staging -- -o YOUR_ORGANIZATION_ID
adaptkit publish --staging -c YOUR_CLIENT_IDAdapt Builder 2.0.0-beta.5 or newer records the build organization in upload.json.
Adaptkit reads that staging organization, applies it to the manifest in memory
before validation, and sends it in the upload's start message. Source manifests
are never rewritten. Adaptkit has no organization override option.
Both tools' overrides are optional and only allowed with --staging. Omitting
the builder's -o uses the manifest organization; omitting -c uses the default
OAuth client. Older artifacts without an organization use the source manifest.
Production and emulation reject artifacts whose organization differs from it.
Publishing streams protobuf messages over gRPC/HTTP/2 to
/mochabugapis.adapt.plugins.v1.PluginService/UploadPlugin on the shared API
origin. Mochabug's public Connect handler serves this RPC alongside the REST API.
Program grouping
runtime.json partitions vertices into executor and configurator programs. Every definition owns its RuntimeSpec and resource limits. Vertices in the same definition share one emitted program; choosing “Create a new program” isolates the new vertex.
{
"executors": [
{
"vertices": ["receive", "transform"],
"runtime": {
"runtime": "js_1.0",
"jsCompatibility": "node24"
},
"maxMemoryMb": 64,
"wallTimeSec": 30,
"cpuTimeMs": 100
}
],
"configurators": []
}Runtime sources export named handlers such as internal_http, external_http, external_connect, and external_datagram. Internal ingress is HTTP-only. The builder generates the final <vertex>_<internal|external>_<protocol> program exports.
DATAGRAM handlers use RFC 9221 QUIC DATAGRAM ingress through
external_datagram.datagram(socket, env, ctx). Returning from a TCP or datagram
handler ends that session; await its receive/send loop.
Browser and resources
Each ui/<role>.html is a native Vite 8 HTML entry. Its .main module, imports, CSS, framework plugins, and assets use the standard Vite pipeline. The completed HTML is private under assets/browser/<vertex>/<role>/index.html; public browser output and public/ files are emitted under cdn/browser/.
Static rendering is opt-in during init and add. When selected, Adaptkit
creates a matching SSG module for every generated browser page. Plain TypeScript
pages use ui/<role>.ssg.ts. React pages use ui/<role>.ssg.tsx, render
<App /> with renderToString, and bootstrap the result with hydrateRoot;
React pages without SSG continue to use createRoot. Every SSG default export
returns the HTML inserted at the single <!-- adapt:ssg --> marker, following
Adapt Builder's .ssg discovery convention. Each role has its own parameterless
renderer: export default function render(): string. The executor and configurator
own their markup; there is no role dispatcher. Plain TypeScript pages without SSG
put that markup directly in HTML.
All pages link styles.css from their HTML head. Its prefers-color-scheme media
query chooses light/dark CSS from the embedding frame's color-scheme, before
JavaScript executes. This is identical for static and client-rendered pages;
there are no theme messages or server substitutions. Electron hosts use
nativeTheme.themeSource application-wide with the same app CSS.
Each browser entry calls init({ resize: true }) once. The SDK reports immediately
and as content changes; the parent decides whether to apply those reports. Set
resize: false for viewport-height layouts such as height: 100vh. Theme support
is independent of resize reporting.
Runtime JSON imports are bundled by default. .txt, .bin, and .wasm imports are copied as relative program modules. Advanced projects can add adapt.config.ts to use ordinary Rolldown module types/plugins and Vite options while retaining Adapt-owned entrypoint, target, and output layout.
Upload artifact
npm run build writes dist/upload.json. It contains only build-owned upload
information: the target environment, CDN/assets prefixes, program modules, and
executor/configurator entrypoints with their runtime descriptors. All paths are
relative to the project directory containing manifest.json.
The command and artifact environments must match: normal publish accepts only
production, publish --staging accepts only staging, and emulate accepts
only emulation. Adaptkit checks this before authentication or RPC setup.
publish and emulate:
- read that descriptor before authentication or RPC setup;
- read
manifest.jsonand everysrc/**/vertex.jsonfrom the selected project; - normalize vertex references and Protovalidate the manifest, vertices,
metadata, and completed
StartUploadcontract; - upload the exact declared program modules, files below the declared CDN/assets prefixes, and source-owned README/logo/config/metadata files.
Adaptkit never infers program entrypoints or legacy bundle names and never
rebuilds the app. Vertex metadata JSON is encoded to protobuf bytes while
streaming. -d, --dir selects the project root; --upload overrides the
descriptor path relative to that root.
All upload entrypoints resize raster logos in memory: app logos to 80×80 pixels and vertex logos to 40×40, with a centered crop and the original image format. A source shared by both roles uses 80×80. SVGs are sent unchanged. Processed logos must fit the 25,600-byte upload limit; errors identify the file. Source files, logo references, and built programs remain unchanged.
Publish and emulation print only their meaningful phases—artifact validation, hosted authentication when applicable, and transfer—followed by one ✓ completion line. Failures use one ✗ diagnostic without dumping a stack trace; Commander owns standard option and usage errors.
The upload primitives are also exported for tests and integrations:
import { readUploadArtifact, sendArtifact } from "@mochabug/adaptkit/upload";
const artifact = await readUploadArtifact({
root: ".",
upload: "dist/upload.json",
expectedEnvironment: "production"
});License
Apache-2.0
