Install the TypeScript SDK
@wexa-fabric/sdk is a thin HTTP client for the Wexa gateway. Zero runtime dependencies, ESM and
CommonJS, Node 18 or later, and it also runs in browsers and edge runtimes.
Install
npm install @wexa-fabric/sdk
The current published version is 0.2.3; the Wexa repository carries 0.3.0, which adds the
schedule, trigger and Data Table methods. The package exports its own version as VERSION, and
that constant is asserted equal to the one in package.json at build time, so what the client
reports is what you installed:
import { VERSION } from '@wexa-fabric/sdk'
console.log(VERSION) // 0.2.3
Configure
Two values, and the client reads both from the environment when you do not pass them:
export WEXA_WORKSPACE=https://fabric.wexa.ai # your workspace URL
export WEXA_API_KEY=fab_sk_… # any bearer credential the gateway accepts
WEXA_WORKSPACE is the workspace URL, not the API base. The client discovers the API base itself
from GET /v1/connection-info, which means the gateway decides its own address and moving it needs
no change on your side. Trailing slashes are stripped for you.
WEXA_API_KEY takes any bearer credential — a fab_sk_… API key or an OAuth 2.1 access token. See
REST authentication for which kind you want.
Construct the client
import { Fabric } from '@wexa-fabric/sdk'
const fabric = new Fabric() // reads the two environment variables
// or
const fabric = new Fabric({
workspace: 'https://fabric.wexa.ai',
apiKey: process.env.MY_KEY,
})
An explicitly-passed empty string falls through to the environment variable, and an environment variable set to the empty string counts as absent — so a half-configured deployment fails the same way as an unconfigured one rather than silently pointing at nothing.
Constructor options
| Option | Default | What it does |
|---|---|---|
workspace | WEXA_WORKSPACE | Workspace URL. |
apiKey | WEXA_API_KEY | Bearer credential. |
timeout | 70 | Seconds. Governed tool routes cap near 60s upstream, so 70 leaves the server's own deadline to fire first. |
retries | 3 | Attempts when a call passes retry: true. Total, not extra. |
fetch | global fetch | An injected fetch, for tests, a proxy agent, or connection pooling. |
Discovery is deferred
fabric.base is undefined until the first request. A TypeScript constructor cannot await, so
the /v1/connection-info round trip is deferred to the first call and memoised — including its
rejection, so a bad workspace URL does not re-fetch on every call.
const fabric = new Fabric()
console.log(fabric.base) // undefined
await fabric.whoami()
console.log(fabric.base) // http://localhost:7123/v1
console.log(fabric.issuer) // http://localhost:7123
This is the one structural difference from the Python client, which performs discovery inside its constructor. Credential validation happens synchronously in both.
Verify the install
import { Fabric } from '@wexa-fabric/sdk'
const fabric = new Fabric()
console.log(await fabric.whoami())
fabric.close()
That prints:
{
"user_id": "u_c10", "role": "OWNER",
"org_id": "org_c10", "dept_id": "dept_c10", "project_id": "proj_c10",
"grants": ["fabric:query.read", "fabric:docs.read", "fabric:ontology.write",
"fabric:orchestrate.read", "fabric:orchestrate.write", "fabric:agent.run",
"fabric:skill.write", "fabric:model.write"]
}
If that works, the workspace URL resolves, discovery succeeded, and your credential is accepted.
Releasing the client
close() aborts any in-flight request and any approval poll. Calling it twice is a no-op, and the
client cannot be reused afterwards. It is also wired to Symbol.asyncDispose:
await using fabric = new Fabric()
// released at the end of the scope
Python has no equivalent, because urlopen closes per request and there is nothing to release.
One SDK ported to the other
The TypeScript and Python clients are deliberate ports of one another, not two independent libraries and not a stack — neither wraps the other, and the TypeScript package has no dependency on Python. They share one tool table, one set of server-bound arguments, one error taxonomy, one retry policy and one response envelope, so an integration written against either reads the same way.
They are not byte-identical, and pretending otherwise would cost you a handler. The documented differences are listed on error handling and Python SDK — install.