@anaralabs/sdk
v0.9.0
Published
Official TypeScript SDK for the Anara public API — research, library search, documents, notes, and chat over your authenticated Anara workspace.
Readme
@anaralabs/sdk
Official TypeScript SDK and CLI for the Anara API. It
mirrors Anara's in-product code-agent SDK — the anara.* surface (library
entities, retrieval, documents, spreadsheets, notes, images, chats, workspace,
sandbox) — as a typed HTTP client. Give a coding agent (Claude Code, Codex, …)
the same tools the Anara agent has, over a single API key.
One runtime dependency (zod, for the method schemas). Works in Node 18+, Deno,
Bun, edge runtimes, and the browser (anywhere fetch exists). The CLI needs
Node.
Install
npm install @anaralabs/sdkCustom citation styles
Use anara.citations.listStyles({ query: 'APA' }) and
anara.citations.getStyle({ id: 'apa' }) to read a base CSL style. After editing
its XML, call previewStyle({ xml, samples }) to check both inline citations
and references, then saveStyle({ name, xml }) to save it in the selected
workspace. Identical saves reuse the same style ID.
Saving does not apply the style. setFormat({ format: saved.id }) changes the
user's default for chats, notes and exports; getFormat() reads that default.
Save and apply require anara:write. Preview and discovery require
anara:read. Saved styles remain scoped to their workspace.
The CLI exposes the same methods: anara methods citations lists them and
anara citations.previewStyle --help shows the preview input schema.
Authenticate
Mint an API key (Better Auth) from your signed-in browser session:
curl -X POST https://anara.com/api/better-auth/api-key/create \
-H 'content-type: application/json' \
-H 'cookie: <signed-in-browser-cookie>' \
-d '{"name":"CLI","permissions":{"anara":["read","write"]}}'Keys look like anara_…. Reads need anara:read; library writes need
anara:write; the sandbox.* methods need the opt-in anara:sandbox
permission (and sandbox.fetchDocument also needs anara:read). Keep keys
secret; never commit them.
Use
import { createAnaraClient } from '@anaralabs/sdk';
// `apiKey` defaults to the ANARA_API_KEY environment variable.
const anara = createAnaraClient();
// Retrieval — compose it from the raw primitives: recall, rerank, read.
const candidates = await anara.retrieval.vectorSearch({
queries: ['wooden pallet recycling drop-off', 'where to recycle pallets'],
});
const winners = await anara.retrieval.rerank({
query: 'wooden pallet recycling drop-off',
candidates,
topK: 6,
});
// Read a winner's full page when snippets aren't enough.
const page = await anara.documents.getPages(
winners[0].documentId,
winners[0].pageNumber != null ? [winners[0].pageNumber] : undefined,
);
// Library entities (CRUD). Writes need an `anara:write` key.
const drafts = await anara.entities.query({
type: 'NOTE',
nameContains: 'draft',
});
const folder = await anara.entities.create({ type: 'GROUP', name: 'Drafts' });
for (const d of drafts) await anara.entities.move(d.id, folder.id);
// Add files. `uploadFile` presigns and PUTs the bytes straight to storage, so
// it is not bound by the API request-body limit; `import` fetches a URL.
await anara.documents.uploadFile({ filename: 'paper.pdf', bytes: pdfBytes });
await anara.documents.import({ url: 'https://arxiv.org/pdf/1706.03762' });CLI
The package ships an anara binary. Every registry method is callable with a
JSON argument (or JSON on stdin):
export ANARA_API_KEY=anara_…
anara methods # list methods with the scopes they need
anara methods retrieval # filter by prefix
anara me # the user + workspace the key resolves to
anara --version
anara retrieval.vectorSearch '{"queries":["pallet recycling"]}'
echo '{"type":"DOCUMENT"}' | anara entities.queryWithout a global install: npx -p @anaralabs/sdk anara methods.
| Variable | Meaning |
| ----------------------- | ---------------------------------- |
| ANARA_API_KEY | required |
| ANARA_BASE_URL | default https://anara.com |
| ANARA_ORGANIZATION_ID | optional workspace pin (see below) |
Exit codes: 0 success, 1 the API returned an error (message on stderr),
2 usage error (unknown method, bad JSON, missing key).
How it works
The package is a thin client: every anara.* method is one
POST /api/v1/sdk/{method} call whose JSON body is the method's arguments,
dispatched server-side against your authenticated workspace. anara.me() hits
GET /api/v1/me. There is no Anara logic in the package itself — just typed
calls over your key.
Surface
entities.query / get / create / update / move / deleteretrieval.vectorSearch / keywordSearch / rerankdocuments.getPages / expandContext / readToc / readMedia / extractdocuments.import / upload / createUpload / uploadFile / uploadAll / importAllspreadsheets.query / export / applyEditsviews.list / create / addColumn / updateColumn / rename / run / deleteColumn / deletenotes.read / edit·images.create·chats.searchorganizations.list·me·forOrganization(id)sandbox.create / runShell / writeFile / editFile / readFile / listDir / attachFiles / fetchDocument / stop(requiresanara:sandbox)
invoke(method, args) is the escape hatch for any registry method not wrapped
above. ANARA_METHODS exports the registry itself (zod input/output schemas,
scopes, summaries), and buildOpenApiDocument() renders it as OpenAPI.
Errors are thrown as AnaraApiError (.status, .code, .details) for API
responses and AnaraError for client-side failures (missing key, no fetch,
failed storage PUT); isAnaraApiError(e) / isAnaraError(e) are type guards.
Workspaces
A key targets your default workspace. To aim at another one you belong to,
list them with anara.organizations.list() and pass organizationId (sent as
X-Anara-Organization-Id) or derive a scoped client with
anara.forOrganization(id).
Drop-in agent guide
ANARA_AGENT_GUIDE is a ready-made system-prompt block teaching an agent when
and how to call the SDK:
import { ANARA_AGENT_GUIDE } from '@anaralabs/sdk';Configuration
createAnaraClient({
apiKey: '…', // or ANARA_API_KEY
baseUrl: 'https://anara.com', // override for self-hosted / preview
organizationId: 'org_…', // pin a workspace you belong to
fetch: customFetch, // override the fetch implementation (used for every request, including storage PUTs)
headers: { 'x-trace': '…' }, // extra headers on every request
});Browser sign-in from the CLI (0.6.1)
On macOS or Linux, run anara login --url https://your-preview.example.com to authorize
Anara in your browser. Authorization uses PKCE and a temporary loopback callback;
credentials are bound to that environment. macOS uses Keychain; Linux uses owner-only token files under $XDG_CONFIG_HOME/anara/oauth (default ~/.config/anara/oauth). Linux token files are not encrypted. Then run
commands with the same ANARA_BASE_URL. An explicit ANARA_API_KEY takes priority.
anara logout --url https://your-preview.example.com revokes tokens and removes
local credentials. Login requires the environment's existing public OAuth client
registration endpoint; it does not configure a provider or grant staff access.
Node applications can import login, getAccessToken, and logout directly from
@anaralabs/sdk/oauth. These native helpers are separate from the browser-safe
SDK entry point. getAccessToken({ baseUrl }) refreshes expired credentials before
returning a bearer token. An absent or revoked login returns undefined; network
failures throw without deleting credentials. Other platforms can supply their
own CredentialStore from @anaralabs/sdk/oauth-types; no plaintext file fallback
is enabled. Automated QA can inject a store and use
login({ baseUrl, store, openBrowser: false, onAuthorizationUrl: async url => ... }).
Upload a file from the shell
npx -y @anaralabs/sdk documents.uploadFile "/path/to/paper.pdf"The command reads a local or sandbox file, reserves an upload through the API,
and sends its bytes directly to storage. It prints the document ID; ingestion
continues asynchronously. An optional second argument selects a folder.
ANARA_API_KEY, ANARA_BASE_URL, and ANARA_ORGANIZATION_ID select the
credentials, server, and workspace. No JavaScript installation setup is needed.
Use npx -y @anaralabs/sdk documents.upload --help to inspect a method's input
schema before calling it. documents.upload takes base64 bytes, not a file path.
Keep upload commands free of | head, which can hide a failing exit status.
Sandbox commands keep generated files in the sandbox by default. Pass attachFiles: ["report.pdf"] to sandbox.runShell to publish selected files, or call sandbox.attachFiles after inspecting the result. Callers that previously relied on automatic file URLs must now select the files explicitly.
Saved extraction views
A view persists the same extraction columns across an explicit set of source documents in a folder. Creating or editing columns does not run extraction automatically; compose the operations in one script:
const view = await anara.views.create({
folderId: 'folder-id',
name: 'Study comparison',
sourceIds: ['paper-1', 'paper-2'],
columns: [
{
name: 'Sample size',
instruction: 'Report the study sample size.',
type: 'number',
},
],
});
await anara.views.run({ viewId: view.id });
const views = await anara.views.list({ folderId: 'folder-id' });The sandbox CLI exposes the same methods: anara views --help and anara views.create --help. Reads require source access; mutations require write access to the owning folder and an anara:write credential. Runs use the existing extraction billing and cell limits. Delete operations permanently remove the view or column and its cells, never the source documents. External SDK callers must obtain any required user confirmation before invoking them.
