crouter
SDK

Client construction

Configure local socket and remote HTTP connections, authentication, and request options.

Client construction

import Crouter from '@north-light/crouter-sdk';

const localClient = new Crouter();                                          // owner's own daemon, unix socket
const remoteClient = new Crouter({ baseURL: 'http://localhost:8787', token: 'token' }); // TCP with a bearer token
const customSocketClient = new Crouter({ socketPath: '/custom/path/crtrd.sock' }); // an explicit socket

Crouter is the default export and the only class an application constructs. CrtrClient from @north-light/crouter-api is an implementation detail and is not re-exported.

Options

| Option | Type | Default | Meaning | |---|---|---|---| | baseURL | string | CRTR_BASE_URL, else unset | http(s)://host:port of a daemon TCP listener. | | socketPath | string | CRTR_SOCKET, else ${CRTR_HOME}/crtrd.sock, else ~/.crouter/canvas/crtrd.sock | Unix socket. Node only; throws in a browser. | | token | string | CRTRD_TOKEN | Sent as Authorization: Bearer <token>. The owner token or a scoped token from crtr sys connect; a scoped token's ceiling is enforced per request (see Getting started). Ignored by a unix-socket daemon, which authenticates by filesystem permission. | | timeout | number (ms) | 30_000 | Per-request wall clock. Does not apply to a stream. | | maxRetries | number | 2 | Transient-failure retries. Never applied to POST or PATCH — see Errors. | | defaultHeaders | Record<string, string> | {} | Merged into every request. | | headers | Record<string, string> | unset | Merged after defaultHeaders; a matching name wins unless token is set, in which case the generated Authorization header wins at construction. | | fetch | typeof fetch | global fetch, or the socket implementation when socketPath is used | Transport override, for proxies and instrumentation. | | autostart | boolean | true for a socket, false for baseURL | On a cold socket, run crtr sys daemon start and retry once. Node only. |

Environment-variable fallbacks

Every fallback above is read at construction, not at request time.

| Variable | Fills | |---|---| | CRTR_BASE_URL | baseURL | | CRTR_SOCKET | socketPath | | CRTR_HOME | the directory the default socket path is resolved under ($CRTR_HOME/crtrd.sock) | | CRTRD_TOKEN | token |

An explicit option always beats its environment variable.

Transport selection

baseURL wins if it is set; otherwise socketPath; otherwise the default socket path. Passing both baseURL and socketPath throws TypeError at construction — exactly one transport per client. In a browser, new Crouter({}) has no daemon transport; give it the saved baseURL and token before making requests.

There is one request path. The client issues every request through the Web fetch API, which a browser and Node both supply globally. The unix socket is not a second transport — it is a fetch implementation the SDK installs when socketPath is set, built on node:http with { socketPath }, returning a standard Response whose body is a ReadableStream. Streaming, abort, headers, and error parsing therefore have exactly one implementation, and the browser build never sees Node code.

Autostart

With autostart on (the default for a socket), a request that finds a cold socket runs crtr sys daemon start, waits for the daemon to serve, and retries the request once. This is what makes npm i -g @north-light/crouter followed by new Crouter() work on a machine that has never run the daemon.

Autostart is Node-only and applies only to the socket transport. A baseURL client cannot start a daemon it may not even share a machine with, so autostart defaults to false there; setting it to true on a baseURL client throws CrouterError (autostart is only valid for the local socket transport).

Per-request options

Every request-capable method accepts an options object as its last argument, after any path, body, or query arguments. Each field overrides the client-level default for that one request.

const id = 'example-node';
const signal = new AbortController().signal;

await client.nodes.retrieve(id, {
  signal,                       // AbortSignal — real cancellation, passed straight to fetch
  timeout: 5_000,               // ms, this request only
  maxRetries: 0,                // this request only
  headers: { 'x-trace': 't-9' } // merged over defaultHeaders
});

| Field | Type | Effect | |---|---|---| | signal | AbortSignal | Aborts the underlying fetch. Raises APIUserAbortError. | | timeout | number (ms) | Overrides the client timeout. | | maxRetries | number | Overrides the client maxRetries. | | headers | Record<string, string> | Merged over defaultHeaders, client headers, and the token-generated header; a matching name, including Authorization, wins. |

signal is real cancellation. The long-polling helpers (waitForOutcome, createAndWait, parse) honour it between polls and during the in-flight request, and aborting them stops the client — it does not cancel the node. To stop the run itself, call client.nodes.cancel(id) or client.nodes.interrupt(id). Streams use NodeEventsOptions: the same fields except timeout, plus after for nodes.events; see Streaming.

What is not on the client

| Convention | Why it is absent | |---|---| | .withResponse() / .asResponse() | They exist to hand back the raw Response and an x-request-id. The daemon emits no request id, and a caller who needs raw bytes uses client.request(). | | local() and the Environment interface | Deleted. The client is the connection — new Crouter() covers what local() did. See Migration. | | apiKey | The daemon's credential is a token (CRTRD_TOKEN) everywhere in the product. apiKey would be a second name for one thing. | | Resource-prefixed ids | Node ids have their own format. The SDK validates unsafe path identifiers locally with TypeError, and the daemon validates the request too; nothing is re-prefixed. |

Wire naming

Wire fields are snake_caseoutput_schema, root_lifecycle, pin_cwd, final_report_path. The SDK does not camelize them. Namespaces and method names are camelCase (client.nodes.waitForOutcome, client.nodes.worktree).

Timestamps are ISO-8601 strings (created, settled_at, finalized_at, deadline_at), not Unix seconds.