Reference

CLI

Reference for the Monospace CLI — its commands, flags, target and credential resolution, JSON output, and exit codes.

Overview

The @monospace/cli package provides the monospace command. It signs in to a Monospace instance (a running Monospace deployment), generates TypeScript types for the SDK, and scaffolds and builds data connector extensions.

This page describes @monospace/cli 0.1.0.

CommandDescription
monospace loginStore a credential for an instance
monospace logoutRemove a stored credential
monospace whoamiShow the targeted instance and workspace, and which credential answers for it
monospace sdk initCreate a monospace.config.ts
monospace sdk generateGenerate TypeScript types from a workspace schema
monospace extension createScaffold a data connector project
monospace extension buildBundle an extension and optionally install it

Install the CLI

Add the CLI to your project as a development dependency:

npm install -D @monospace/cli

Run it through your package manager or from a package.json script:

npx @monospace/cli whoami

The CLI requires Node.js 22.12 or later, the minimum its command parser supports.

Do not run npx monospace. monospace is an unrelated package on the npm registry, and npx fetches it when the CLI is not installed locally. Use npx @monospace/cli.

Use Global Flags

Every command accepts these flags:

FlagEffect
--jsonPrint one JSON result on stdout. See Read JSON Output
--no-inputNever prompt. A question the command needs answered fails it with cli.confirmation_required, naming the flag that answers it. Questions with a default for unattended runs, such as the directory and display name in extension create, take that default
-h, --helpShow help for the command, with examples
-V, --versionPrint the CLI version

The CLI rejects unknown flags and extra arguments with cli.parse_failed.

Results print to stdout. Prompts, notes, warnings, and errors print to stderr, so you can redirect a command's result without losing its questions.

Prompts are also off when stdin or stderr is not a terminal, or when the CI environment variable is set to anything other than an empty value, 0, or false in any letter case.

Environment Variables

VariableEffect
MONOSPACE_URLInstance URL. See Resolve the Target
MONOSPACE_WORKSPACEWorkspace name. See Resolve the Target
MONOSPACE_API_KEYAPI key for this invocation. See Resolve Credentials
CITurns prompts off
NO_COLORA non-empty value turns color off. TERM=dumb does the same
FORCE_COLORTurns color on, unless set to 0 or false. NO_COLOR and TERM=dumb take precedence

The CLI reads a .env file in the working directory before it runs. Variables already set in the environment take precedence over the file. An empty flag or variable counts as unset.


Resolve the Target

Commands that contact an instance resolve its URL and, for commands that take --workspace, the workspace. Each value comes from the first of:

  1. The --url or --workspace flag
  2. The MONOSPACE_URL or MONOSPACE_WORKSPACE environment variable
  3. url or workspace in monospace.config.ts, or monospace.config.js, in the working directory

The CLI looks for the config file in the working directory only, not in its parents. login, logout, and whoami skip the config file when flags and environment variables supply every value they need.

An instance URL is the base URL of the instance, such as https://example.monospace.io. It must use http or https, and cannot contain a user name, password, query, or fragment. The CLI does not follow redirects; a redirected request fails with cli.request_redirected.

A workspace name becomes one segment of a request path, so it cannot be . or .., or contain /, \, ?, #, whitespace, or control characters.

Requests to an instance time out after 10 seconds.

The config file is executed when the CLI loads it. It is rejected with cli.config_carries_credential when it sets apiKey, api_key, password, secret, or token. Store credentials with monospace login or MONOSPACE_API_KEY instead.

Resolve Credentials

Commands that call an instance use the first credential they find:

  1. The --api-key flag, on commands that accept it
  2. The MONOSPACE_API_KEY environment variable
  3. The credential monospace login stored for the instance

Stored credentials live in the operating system's keyring under the service name monospace-cli, one per instance URL. Instances at different paths on the same host keep separate credentials.

A stored credential is either an API key or a session from an email and password sign-in. When the instance answers a session's request with 401 or 403, the CLI refreshes the session once, stores the new tokens, and retries. When the instance rejects the refresh, the CLI removes the stored session; sign in again. A refresh that fails for any other reason leaves the session stored.

A command that needs a credential and finds none fails with cli.not_authenticated. An API key must be visible ASCII characters with no spaces; otherwise the command fails with cli.api_key_invalid.

Where the keyring is unavailable, such as on a server without a secret service, commands that need it fail with cli.credential_store_unavailable. Set MONOSPACE_API_KEY instead.


Sign In

monospace login stores a credential for an instance:

npx @monospace/cli login --url https://example.monospace.io
FlagDescription
--url <url>Instance to sign in to
--api-key-stdinRead an API key from standard input instead of prompting

The command resolves the instance from --url, MONOSPACE_URL, or the config file. Without one, it prompts for the URL.

It then asks whether to sign in with an API key or with an email and password:

  • API key -- stores the key. The CLI does not contact the instance, so run monospace whoami to check the key.
  • Email and password -- signs in and stores the resulting session. A rejected email or password fails with cli.login_rejected.

Signing in again replaces the credential stored for that instance.

To sign in without a terminal, pipe an API key in:

terminal
echo "$KEY" | npx @monospace/cli login --url https://example.monospace.io --api-key-stdin

login warns when MONOSPACE_API_KEY is set, because that variable takes precedence over the credential it stores.


Sign Out

monospace logout removes the credential stored for the targeted instance:

npx @monospace/cli logout --url https://example.monospace.io
FlagDescription
--url <url>Instance to sign out of

For a session, the CLI first asks the instance to revoke it. When the instance does not confirm the revocation, the CLI removes the local credential anyway and warns that the refresh token stays valid until it expires.

For an API key, the CLI removes only the local copy. The key keeps working until you revoke it in the instance.

When nothing is stored for the instance, the command prints Nothing to remove. and succeeds.


Check the Target and Credential

monospace whoami shows which instance and workspace the working directory targets, where each value came from, and which credential the CLI uses:

npx @monospace/cli whoami
FlagDescription
--url <url>Instance to target
--workspace <name>Workspace to target
--api-key <key>API key to use for this invocation
--localReport the target and credential without contacting the instance

The command needs an instance URL; without one, it fails with cli.target_missing. The workspace is optional.

When a credential is found, the command reads the signed-in user from the instance and reports its email address, or its ID when it has no email address. A rejected API key fails the command with cli.credential_rejected. With --local, it makes no request.

Without a credential, or when the keyring is unavailable, the command still succeeds and reports what it found. In JSON output, authenticated means a credential was found and verified means the instance was asked about it.


Create an SDK Config

monospace sdk init writes a monospace.config.ts for type generation:

npx @monospace/cli sdk init
FlagDescription
--url <url>Instance URL to write into the config
--workspace <name>Workspace to write into the config
--dir <dir>Output directory for the generated types
-c, --config <path>Config file to create, instead of ./monospace.config.ts
--yesReplace an existing config file without asking

The command prompts for each value you don't supply. The URL and workspace also resolve from MONOSPACE_URL and MONOSPACE_WORKSPACE. The output directory prompt suggests ./src/generated/monospace. Without a terminal, pass all three values:

npx @monospace/cli sdk init --url https://example.monospace.io --workspace blog --dir ./src/generated/monospace

A config path ending in .ts, .mts, or .cts gets a TypeScript file. Any other path gets a JavaScript file typed with a JSDoc comment:

monospace.config.ts
import type { MonospaceConfig } from '@monospace/sdk/config';

export default {
    url: "https://example.monospace.io",
    workspace: "blog",
    output: "./src/generated/monospace",
} satisfies MonospaceConfig;

When the config file already exists, the command asks before replacing it; without a terminal, pass --yes. Declining leaves the file unchanged and ends the command with outcome cancelled. The new file keeps the existing URL and workspace unless flags or environment variables supply others, and drops every other option the old file set.


Generate SDK Types

monospace sdk generate reads a workspace schema and writes the generated client to index.ts in the config's output directory:

npx @monospace/cli sdk generate
FlagDescription
--url <url>Instance to read the schema from, overriding the config
--workspace <name>Workspace to read the schema from, overriding the config
--api-key <key>API key to use for this invocation
-c, --config <path>Config file to use, instead of searching the working directory

The command requires a config file; without one, it fails with sdk.config_not_found. The config needs output, plus either input, or an instance URL and workspace from the config, flags, or environment variables:

OptionTypeDescription
outputstringOutput directory for the generated types. Required
urlstringInstance URL. --url and MONOSPACE_URL take precedence
workspacestringWorkspace whose schema to read. --workspace and MONOSPACE_WORKSPACE take precedence
inputstringPath to a local OpenAPI document in JSON
outputOptionsobjectOptions for the generated output. See Config Reference

Without input, the command reads /api/{workspace}/openapi with the resolved credential. The credential needs the openApiSchema:read entitlement; see OpenAPI Specification. A missing workspace fails the command with sdk.workspace_missing.

With input, the command reads the local file, needs no credential, and warns that any URL or workspace from flags or environment variables is ignored. input takes precedence when the config also sets url and workspace. The file must be a JSON document with an openapi field. Relative input and output paths resolve against the working directory.

The command overwrites index.ts on every run. It refuses to write when index.ts is a symbolic link.


Scaffold a Data Connector

monospace extension create creates a data connector extension project. It needs no instance and no sign-in. Run it with npx before the project exists:

terminal
npx @monospace/cli extension create ./acme-store --id acme/store --name "Acme Store"
Argument or flagDescription
[directory]Directory to create the project in. It must not exist yet, or be empty apart from .git
--id <id>Extension id, as namespace/name. Each part is lowercase letters and digits, optionally joined by single hyphens
--name <name>Display name shown in Studio. Defaults to one derived from the id

The command prompts for each value you don't supply. Without a directory, it asks for one. When prompts are off, it uses the working directory, which must then be empty apart from .git. A named directory that has files makes the command ask for another.

The project contains these files:

FileContents
extension.config.jsonThe extension config, with one data connector entry. See Extension Config
package.jsonbuild and typecheck scripts, @monospace/extension-kit as a dependency, and @monospace/cli, rolldown, and typescript as development dependencies
tsconfig.jsonStrict TypeScript settings for src
src/data-connectors/<namespace>/index.tsThe connector. It returns two sample items from every read and ignores the query
src/data-connectors/<namespace>/schema.tsThe connector's schema: one collection, items, with id and name fields and only an unfiltered readMany operation
README.mdBuild and install instructions
.gitignoreIgnores dist and node_modules

The entry id is <namespace>/data-connector, where <namespace> is the first part of --id.

The command does not install dependencies. It prints the install and build commands for the package manager it detects, falling back to npm.


Build an Extension

monospace extension build reads extension.config.json from the working directory and writes the built extension to ./dist. Run it from the project root:

npm run build
FlagDescription
--out-dir <path>Write the build here instead of ./dist
--install-to <path>Copy the build into this extension install directory
--watchRebuild whenever a source file or extension.config.json changes, until you stop it with Ctrl-C

The build requires rolldown ^1.2.0 installed in the project, and uses the project's copy. A scaffolded project already declares it; otherwise the build fails with extension.rolldown_missing or extension.rolldown_unsupported.

The output directory holds manifest.json, one <entry id>.js file per entry, and a .monospace-build marker that records which extension owns the directory. Each file bundles the entry's code and its dependencies into a single ES module with a default export. A bundle that still imports a module fails with extension.built_not_self_contained; Node.js built-ins such as node:fs are not available to extensions. A bundle without a default export fails with extension.built_without_default_export.

The build does not type-check your code. Run the project's typecheck script for that.

Without --watch, the build writes to a staging directory and swaps it in only when every entry has built, so a build that fails to bundle or validate leaves the previous output in place. With --install-to, the install happens before the swap.

The CLI replaces an output directory only when it is missing, empty, or already holds a build of the same extension. Otherwise the build fails with extension.artifact_dir_not_ours. The output and install directories cannot contain the project or its sources, or each other.

Install into an Instance

--install-to names an instance's extension install directory. The CLI copies the build into a subdirectory named after the extension id, with / replaced by __, so acme/store installs to acme__store:

npm run build -- --install-to ../monospace/extensions

Each install replaces the previous one whole, so files you added to that subdirectory are deleted. The CLI refuses a subdirectory that holds a different extension, with extension.install_dir_not_ours.

An instance's install directory is set by MONOSPACE_EXTENSIONS__INSTALL_LOCATION and defaults to ./extensions, relative to the instance's working directory. In the Docker image, the default resolves to /monospace/extensions.

An instance loads extensions when it starts. With MONOSPACE_EXTENSIONS__AUTO_RELOAD=true, it also reloads an extension when an install replaces it.

Watch for Changes

--watch builds, then rebuilds whenever a source file or extension.config.json changes:

npm run build -- --watch --install-to ../monospace/extensions

Watch mode writes into the output directory in place. A failed rebuild prints a warning and keeps watching. With --install-to, the CLI installs only after a rebuild in which every entry built. The JSON result prints once, when watching stops, and its builds field counts every rebuild, failed ones included.

Stopping with Ctrl-C ends the command with outcome cancelled when the last rebuild succeeded, and fails it with that rebuild's error when it did not.

Extension Config

extension.config.json describes the extension and its entries. It is strict JSON: comments, trailing commas, and repeated keys are rejected. The build also rejects unknown fields.

FieldRequiredDescription
idYesExtension id, as namespace/name
versionYesExact SemVer version, such as 0.1.0
nameYesDisplay name
entriesYesAt least one entry
description, icon, iconColorNoOptional metadata
$schemaNoEditor schema location. Left out of the built manifest

Each entry takes these fields:

FieldRequiredDescription
typeYesdataConnector
idYesEntry id, as namespace/name. Unique within the extension; it names the built file
versionYesExact SemVer version
nameYesDisplay name
engine.entrypointYesPath to the entry's source file, relative to the project directory
engine.permissionsNoNetwork hosts the entry needs, each as { "permission": "net:<host>[:<port>]", "reason": "..." }. reason is required and shown to whoever approves the data source
description, icon, iconColorNoOptional metadata

A net: permission names a host name, optionally with a *. subdomain wildcard, an IPv4 address or CIDR range, or a bracketed IPv6 address, each with an optional port. For example: net:api.example.com:443, net:*.example.com, net:10.0.0.0/8.


Read JSON Output

With --json, a command prints exactly one JSON object on stdout when it finishes, including when it fails:

response.json
{
  "schema": "monospace.sdk.generate/1",
  "outcome": "failed",
  "effectState": "none",
  "data": null,
  "error": {
    "category": "configuration",
    "code": "sdk.config_not_found",
    "message": "Config file not found. Searched: monospace.config.ts, monospace.config.js",
    "fix": "Run `monospace sdk init` to create one."
  }
}
FieldDescription
schemaVersioned name of the result shape. monospace.error/1 when the CLI cannot attribute a failure to a command
outcomeok, failed, or cancelled
effectStateWhether the command changed anything: complete, none, or unknown
dataThe command's result, or null
errornull, or an object with category, code, message, and, when the CLI has a suggestion, fix

Check outcome, not the exit code, to tell a completed command from a cancelled one.

Commandschemadata fields
loginmonospace.login/1instance, kind (api-key or session), store, url
logoutmonospace.logout/1removed: null, or instance, kind, and revoked
whoamimonospace.whoami/2instance, workspace, authenticated, credential, identity, store, verified
sdk initmonospace.sdk.init/1path, url, workspace, dir
sdk generatemonospace.sdk.generate/1output, source, bytes
extension createmonospace.extension.create/1id, entryId, name, dir, files, agent, next
extension buildmonospace.extension.build/1artifactDir, entries, installedTo, builds

--json does not turn prompts off; pass --no-input in scripts. --help and --version print to stderr under --json, so stdout stays empty.


Handle Exit Codes

Exit codeMeaning
0Outcome ok or cancelled
1Outcome failed

The first Ctrl-C, or SIGTERM, asks the running command to stop. The command ends with outcome cancelled, except a watch session whose last rebuild failed, which ends with outcome failed. Cancelling does not undo changes, so check effectState. A second Ctrl-C or SIGTERM stops the CLI immediately with a non-zero exit code.

Running monospace without a command prints help and exits 0.


See Also

Copyright © 2026