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_case — output_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.