npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arbidocs/sdk

v0.5.75

Published

Shared operations layer for ARBI — auth, config, SSE, WebSocket, and API operations

Readme

@arbidocs/sdk

TypeScript SDK for the ARBI platform. Handles authentication, workspace encryption, and streaming AI queries.

Start below. The complete developer quickstart and compatibility table also ship in the npm package at node_modules/@arbidocs/sdk/docs/; no repository access is required.

Quickstart

npm install @arbidocs/sdk
import { Arbi } from '@arbidocs/sdk'

const arbi = new Arbi({ url: 'https://your-arbi-deployment.example' })
await arbi.login(email, password)
await arbi.selectWorkspace(workspaceId) // an explicit choice
const response = await arbi.responses.create({ input: 'Say hello in one sentence.' })
console.log(response.output_text)

Browser-only code imports the same API from @arbidocs/sdk/browser. For a rendered chat, use the basic React example or the website iframe. Node.js needs no IndexedDB polyfill. Keys stay in memory for this SDK instance unless you supply an explicit persistence adapter:

import { Arbi } from '@arbidocs/sdk'

Add a chat widget

The installed SDK includes a standalone React starter (in releases containing this guide):

cp -R node_modules/@arbidocs/sdk/examples/react-chat ./my-arbi-chat
cd my-arbi-chat
mv gitignore .gitignore
npm install
cp .env.example .env
# Set the public VITE_ARBI_URL, then:
npm run dev

Sign in inside the widget and explicitly choose a workspace. To deploy, run npm run build and serve dist/ as a static site. Never put credentials in VITE_ variables. The deployment must allow this app's origin for API calls. The app uses @arbidocs/blocks/embed, with matching package versions and stylesheet setup already supplied.

For a website without a React build pipeline, use the frontend deployment's hosted panel:

<iframe src="https://YOUR-ARBI-FRONTEND/embed/chat" title="ARBI chat"
  style="width:380px;height:640px;border:0" allow="clipboard-write"></iframe>

The panel owns sign-in and workspace choice. Your deployment must allow the host site to frame this route; the parent CSP must allow the frontend in frame-src. This is authenticated workspace chat. Embedded storage restrictions can require a separate sign-in.

Lifecycle

const arbi = new Arbi({ url: 'https://www.arbidocs.com' })

await arbi.login(email, password)            // or arbi.loginWithKey(email, keyBase64)
await arbi.selectWorkspace(workspaceId)
// ... use the SDK ...
await arbi.logout()

| Property | Description | |----------|-------------| | arbi.isLoggedIn | Whether the user is authenticated | | arbi.hasWorkspace | Whether a workspace is selected | | arbi.getDeploymentDomain() | The domain the user's keys are derived for: the deploymentDomain option, else the hostname of url (e-signing re-derives keys with it) |

Operations

| Namespace | Key methods | |-----------|-------------| | arbi.workspaces | list(), create(), update(), delete(), listUsers(), addUsers(), removeUsers() | | arbi.documents | list(), get(), uploadFile(), uploadUrl(), update(), delete(), download() | | arbi.conversations | list(), getThreads(), updateTitle(), share(), delete() | | arbi.responses | create(), parse(), retrieve(), attach(), cancel() — start here for new integrations | | arbi.assistant | ask(request, handlers), attachRun(responseId, handlers), interject(messageId, text), cancel(responseId), retrieve(query, docIds) | | arbi.tags | list(), create(), update(), delete() | | arbi.doctags | assign(), remove(), generate() | | arbi.contacts | list(), add(), remove() | | arbi.health | check(), models(), mcpTools() |

Streaming AI queries

const stream = await arbi.responses.create({ input: 'What does section 3 say?', stream: true })
for await (const event of stream) {
  if (event.type === 'response.output_text.delta') console.log(event.delta)
  if (event.type === 'response.failed') console.error(event.response.error)
}

Existing callback integrations

assistant.ask remains supported and uses /v1/responses. New integrations can start with responses.create above; keep the callback helper when your UI already uses it.

const result = await arbi.assistant.ask(
  {
    question: 'What does section 3 say?',
    docIds: [docId],
    previousResponseId: lastMessageId, // for multi-turn conversations
  },
  {
    onToken: (token) => { /* append to UI */ },
    onStreamStart: ({ assistant_message_ext_id }) => { /* save for follow-ups */ },
    onOutputItemDone: (event) => { /* an agent step settled */ },
    onError: (error) => { /* the server's report (string) or the connection (Error) */ },
    onComplete: () => { /* done */ },
  }
)

// result.text — full accumulated response
// result.assistantMessageExtId — use as previousResponseId for follow-ups

Which workspace a turn runs in

A turn runs in workspaceId (default: the current workspace), read when ask or startResponse is called and named on the request, so a workspace switch while it is sent cannot move it. A workspace other than the current one is opened for this session first (its key is deposited; the current workspace does not change). A follow-up runs in its thread's workspace: if the server refuses it with 409 workspace_mismatch, the turn is sent once more there, and onWorkspace(workspaceId) reports where it was accepted. A 409 workspace_key_required deposits the key again and resends once. When the workspace cannot be opened, nothing is sent and the call rejects with ArbiWorkspaceUnavailableError. workspaceConflictOf(error) reads either 409. assistant.submit and assistant.retrieve take workspaceId the same way.

Session recovery

Browsers persist the signing key encrypted in IndexedDB. Without IndexedDB, each SDK client keeps its own key in memory; it is not shared with other instances or saved to disk. Recover a session without a password:

await arbi.loginWithKey('[email protected]', signingKeyBase64)

The SDK also auto-recovers on 401 responses using the stored key.

Security

  • Zero-knowledge auth — passwords never leave the client
  • E2E workspace encryption — symmetric keys wrapped with your public key
  • Encrypted key storage — private keys in IndexedDB encrypted with non-extractable AES-GCM
  • Session cleanup — logout ends the live session; the stored identity supports offline unlock and default storage is cleared on logout-everywhere

React

For React apps, use @arbidocs/react instead — it wraps this SDK with hooks and a context provider. See the @arbidocs/react README.

Requirements

  • Browser: Any modern browser with Web Crypto API
  • Node.js: native fetch/Web Crypto runtime; Node 22+ is used by the executable example and package smoke check (no fake-indexeddb needed)
  • TypeScript: 5.0+ (optional)

Automations

The workflows namespace exposes create/get/list/update/delete/run and listWorkflowRuns. Triggers use schedules or notification types; every saved rule can also run immediately. A document action (annotate_documents or email_documents) returns an execution reference rather than a conversation. Its persisted history contains selected documents, run-label references, result metadata and failures; annotation answers remain document fields. Every execution has a WorkflowRuns record; chat actions also link to a conversation.

import { workflows } from '@arbidocs/sdk'
const run = await workflows.runWorkflow(arbi, 'wfl-example')
if (run.error) throw new Error(run.error)
if (run.execution_id) {
  const history = await workflows.listWorkflowRuns(arbi, 'wfl-example')
  console.log(history)
}

Document selectors can specify IDs, labels resolved at execution time, or latest documents since the previous successful run. Unattended document actions require an assistant with standing workspace access; creating a rule does not grant that access.

For latest-document digests, the first run covers the preceding 24 hours; later runs use the previous successful run cutoff. Failed delivery does not advance it. Emails contain document names and authenticated platform links, without attachments.

Releases

Published versions are listed on npm. Maintainers with repository access can also read the detailed GitHub release notes.

Errors and streaming outcomes

SDK HTTP failures retain their structured server body in ArbiApiError.apiError, with status, detail, and optional code, requestId, and responseId. AssistantTurnRefused remains an ArbiApiError when ask() rejects. Network failures use ArbiTransportError and retain the original cause; native AbortError objects propagate unchanged.

ask(), attachRun(), and streamSSE() retain their existing callbacks and result fields. Add onErrorDetails(error) to inspect typed errors alongside the legacy onError callback, and onOutcome(outcome) to observe the terminal result. SDK stream results also include outcome and errorDetails. The outcome preserves the existing assistant msg- identifier and raw terminal response payload. Its status is completed, failed, cancelled, incomplete, disconnected, or acknowledged. A control-only acknowledgement has no response identity. The legacy completed boolean still indicates receipt of response.completed; use outcome.status to distinguish the server's explicit terminal status.

A local abort or truncated connection reports disconnected, with reason aborted, connection_closed, or connection_error; it never proves that the server cancelled execution. Reader failures reject as well as notifying the outcome. Explicit terminal server events finish reading without waiting for the connection to close. The first terminal frame wins: later frames, including conflicting terminals or tokens in the same chunk, are ignored. Background response APIs continue to expose the original Responses envelope and its status and error fields.

Consumer callback exceptions propagate unchanged. They do not trigger a second typed error or outcome notification and are not classified as server failures or transport disconnections. onOutcome fires once when SDK consumption reaches an outcome; an earlier consumer exception interrupts consumption before that notification.

Composable response handles

assistant.startResponse(request, { signal }) uses the same request builder and workspace selection as ask. It returns either an observer for the existing msg-… response, or a control acknowledgement without inventing a message. Existing ask, callbacks, and attachRun remain supported.

const started = await arbi.assistant.startResponse({ question: 'Summarize this document', docIds })
if (started.kind === 'response') {
  const handle = started.handle
  const unsubscribe = handle.subscribe((snapshot) => render(snapshot.displayText))
  try {
    const outcome = await handle.outcome
    if (outcome.status === 'disconnected') {
      const stored = await arbi.assistant.getResponse(handle.id)
      // Inspect stored status; attachResponse(handle.id) again if still active.
    }
  } finally {
    unsubscribe()
    handle.detach()
  }
}

Use await handle.cancel() for an explicit authenticated server cancellation request. detach() and the optional AbortSignal only close this reader. A cancellation request does not settle the handle as cancelled: the terminal server event decides the outcome. Workspace and native-agent permissions continue to be enforced by the server.

attachResponse('msg-…') observes the existing watch stream, with its bounded replay retention; a 404 requires stored-response/history reconciliation. Handles do not retry or reconnect in the background, and an attachment's disconnected outcome does not mean server execution stopped.

Snapshots and their output items are immutable. Subscriptions immediately receive the current snapshot, including fast completion. Exceptions in a snapshot listener are rethrown in a microtask without stopping the reader or other listeners. Unknown server statuses and raw terminal results are preserved. displayText is the existing rendering projection, not a final-answer selector.

handle.events() provides one live raw iterator, beginning when called. It preserves unknown JSON events/fields and SSE IDs; reasoning frames retain their raw text. Malformed JSON and conflicting response IDs produce arbi.sdk.protocol_error diagnostics with the original frame. This is not retrospective replay or a durable resume cursor. Keep existing callbacks when every startup frame is needed. The iterator buffers at most 256 frames, covering existing tool and control fixtures; overflow rejects its reads with code RESPONSE_EVENT_OVERFLOW, while snapshot reduction continues. Return/break the iterator to release its buffer; a second iterator is rejected.

Node example and package check

Run examples/node-response.mjs with Node 22+ after installing @arbidocs/sdk (node node_modules/@arbidocs/sdk/examples/node-response.mjs). Set ARBI_URL, ARBI_EMAIL, ARBI_PASSWORD and ARBI_WORKSPACE_ID; ARBI_QUESTION is optional. It explicitly selects the current workspace, submits one Responses turn, distinguishes disconnect from completion and refetches an accepted response rather than resubmitting it. Ctrl-C detaches the reader; it does not claim to cancel background execution.

Default Node storage lasts only for that client instance. For persistence across process restarts, pass persistence: { getSession, saveSession, clearSession } to Arbi or createArbiClient. The existing SessionPersistence interface is retained; clearSession is optional for existing adapters and is invoked by logout-everywhere when provided. Protect keys in any custom persistent store. Normal logout retains the stored identity for the existing offline-unlock behavior; logout-everywhere clears the default memory adapter. Browser encrypted storage remains unchanged.

Repository maintainers can run node scripts/sdk-node-smoke.mjs after building the client and SDK. It packs both packages, installs their tarballs/runtime dependencies in a fresh temporary npm consumer, checks ESM/CommonJS/browser/testing imports and exercises key login, recovery and Responses over a local fake fetch. It requires the npm registry for dependencies, no deployment credentials, and no IndexedDB polyfill. It verifies package/runtime behavior, not backend access control or live deployment integration.

Configuring HTTP transport

React Query still manages React caches, mutations and refetching. The SDK transport underneath it can be configured once for typed requests, Responses streams and multipart API uploads:

const arbi = new Arbi({
  url: 'https://arbi.example.com',
  transport: {
    fetch: customFetch, // optional fetch-compatible adapter
    timeoutMs: 30_000, // optional; no deadline by default
    onResponse: ({ method, url, status, durationMs }) => {
      recordRequest({ method, url, status, durationMs })
    },
  },
})

For a host-owned session, configure transport on createArbiClient and pass that client to Arbi; its wrapper inherits the host transport and session gate. Configure it on the host, rather than supplying a different transport to the wrapper. Existing host client adapters without http retain the previous global-fetch fallback for raw SDK operations.

The timeout lasts until the adapter returns a Response: response headers for native fetch, or the buffered reply for the XMLHttpRequest upload-progress adapter. It does not cap a live stream's body. AbortSignal cancellation and sign-out still stop the body after headers. Metadata hooks receive method, sanitized URL (no query, fragment or credentials), status and elapsed time; no headers or bodies are passed. onError receives the original fetch failure. Hooks are opt-in and synchronous; a thrown hook exception rejects the request. No telemetry or logging is enabled by default.

Default browser uploads retain XMLHttpRequest byte progress. An injected fetch takes precedence over XHR and reports upload completion once rather than promising byte progress. The existing presigned storage PUT adapter, public booking helpers, media downloads and WebSockets are separate transports and are not changed by this API-transport option.

Connection failures are retried only for generated GET/HEAD requests, with the existing bounded backoff. Mutations and response creation are never retried after an ambiguous connection failure. Existing explicit authentication/workspace-state refusal recovery remains, through the same configured transport. This option adds no workspace selector, header, permission or switching.

Validated structured responses

const format = {
  type: 'json_schema' as const,
  name: 'invoice',
  strict: true,
  schema: {
    type: 'object',
    properties: { total: { type: 'number' } },
    required: ['total'],
    additionalProperties: false,
  },
}
const response = await arbi.responses.parse<{ total: number }>({
  input: 'Extract the total from: Invoice total 125.50',
  text: { format },
})
// response.output_parsed.total is validated; output, usage and metadata are retained.

The same text option is accepted by ask, startResponse and background submit. For streaming/background work, retain the format and validate a completed resource with await parseStructuredResponse(response, format). Live JSON fragments are provisional. The parser expects one final assistant output message; it does not join narrative and answer messages or treat metadata.content as a replacement for standard output items.

Invalid JSON, schema mismatch, refusal, incomplete/cancelled/nonterminal responses and invalid schemas have distinct error codes. ArbiStructuredOutputError.response preserves the original response; request/response IDs remain available. No type coercion, default insertion, Markdown repair or automatic resubmission is performed. The TypeScript generic describes your schema's shape; runtime validation comes from JSON Schema, not the generic parameter.

Schemas use draft 2020-12 and local references; format annotations are not asserted. Client validation loads Ajv on demand and uses runtime compilation, with logging disabled. A runtime policy that prohibits compilation produces STRUCTURED_OUTPUT_VALIDATOR_UNAVAILABLE before sending a request; raw server-validated Responses remain available through the existing APIs without this client parser.

Requires the backend structured-output capability. It validates the existing model/agent loop's final answer without changing workspace, identity or tool permissions. Native-agent and slash/delegation paths that cannot honor this contract reject it explicitly with STRUCTURED_OUTPUT_UNSUPPORTED; they never silently ignore it. Other request fields and old callback consumers keep their existing behavior.

Familiar Responses operations

const response = await arbi.responses.create({ input: 'Hello', model: 'cfg-my-config' })
console.log(response.output_text)
const stream = await arbi.responses.create({ input: 'Hello', stream: true })
for await (const event of stream) {
  if (event.type === 'response.output_text.delta') process.stdout.write(event.delta)
}
const queued = await arbi.responses.create({ input: 'Review this', background: true })
const current = await arbi.responses.retrieve(queued.id)

The current workspace supplies the ARBI workspace context. workspace_ext_id is a wire-level escape hatch subject to existing server checks; it does not switch the session or grant access. create and retrieve retain the envelope's output, metadata, usage and terminal state, and add an output_text convenience field. create also exposes _request_id when the server provides it. A failed JSON response remains a failed response; inspect status and error.

create({ stream: true }) returns an async iterable of events with a standard type discriminator. The existing message observer buffers events from the first frame and applies backpressure, so fast replies and slow consumers do not lose the beginning. Iteration throws on a disconnected reader; failed/incomplete terminal frames remain observable as events. Breaking iteration detaches the reader and leaves server execution running. Explicit cancellation uses responses.cancel(id). The optional stream.handle exposes ARBI snapshots, outcomes, detach and cancellation.

The existing responses.stream and assistant.startResponse methods still return ARBI handles (or slash-command acknowledgements). Use those helpers for control commands. Streaming background creation is currently unsupported and rejected before HTTP; create with background: true, then use responses.attach(id) for existing replay/live events. One con- contains many msg- responses.

For structured results, responses.parse<T>({ input, text: { format } }) returns output_parsed alongside output_text and the original output fields. It validates the JSON Schema before sending one synchronous request; failures/refusals/incomplete results throw the existing structured error. The schema is plain JSON Schema, not a Zod-specific helper. assistant.parseResponse and the standalone parseStructuredResponse remain supported for existing consumers. These SDK patterns follow the official Responses streaming guide and structured-output guide.

Compatibility means the documented /v1/responses subset, not drop-in support for every OpenAI, Anthropic or Vercel feature. Input is currently a string; tool context uses ARBI's documented tool map. Authentication, cfg selection, agt identity, workspace grants and deferred execution remain ARBI contracts. Native local-function tools and arbitrary provider options are not implemented. See the backend's docs/responses-api.md for the exact supported fields.

Compose an existing configuration

Start from the configuration fetched by agentConfig.get(configId) so omitted settings keep their current values. No helper creates an identity, grants permissions or contacts MCP servers.

import { composeConfiguration, instructions, capabilities, mcpTools,
  documentTools, composeTools } from '@arbidocs/sdk/browser'

const existingConfiguration = { ...await arbi.agentConfig.get(configId), title: 'Evidence review' }
const updated = composeConfiguration(existingConfiguration,
  instructions('Answer with evidence.'),
  capabilities({ webSearch: true, codeExecution: false }),
  mcpTools(existingConfiguration, ['approved_search'], 'add'))
await arbi.agentConfig.save(updated)
const tools = composeTools(existingTools, documentTools({ search: [documentId] }), 'add')
await arbi.responses.create({ input: 'Summarise the evidence', tools })

Configuration sections merge one level; arrays replace; explicit null clears. MCP and response-tool helpers require an explicit add or replace choice. Adding a tool replaces the same name while preserving other names. Instructions apply to both QueryLLM and the built-in agent persona. Ordinary cfg- configurations do not require agt- identities. Pinning a configuration to an existing agent uses the existing agent_ext_id; authorizing its task remains a separate action.

Choose a public layer

| Need | Package / entry | | --- | --- | | Responses and domain operations, Node | @arbidocs/sdk | | Same operations, browser-safe imports | @arbidocs/sdk/browser | | Typed HTTP and advanced crypto/session access | @arbidocs/client | | Headless React state and React Query hooks | @arbidocs/react | | Standalone chat widget | @arbidocs/blocks/embed | | Rendered composable views | @arbidocs/blocks and capability subpaths | | Connected rendered views | @arbidocs/blocks/connect | | Fake fetch, SSE frames, replay scenarios | @arbidocs/sdk/testing (tests only) |

Existing exports remain available. Import domain helpers through the documented package entries; never import src/ or generated dist/ internals. A React Query hook uses the same session and request builder as the SDK; applications do not need another chat engine.

The published examples/ directory includes node-response.mjs, stream-response.mjs, background-response.mjs and structured-extraction.mjs. response-handle.mjs shows the optional ARBI lifecycle controls. Install the package, set the environment variables documented in examples/session.mjs, and run with Node 22+. Background inspection accepts an existing response ID and never resubmits automatically. Tests can import frame and createSseFake from /testing to exercise streams without a deployment (see the exported helper signatures).

Check setup

Run await arbi.diagnose() or arbi doctor --json to check deployment, session and current-workspace setup without logging in, switching workspaces or granting access. See the packaged diagnostics guide.

Human review

A runnable example ships at node_modules/@arbidocs/sdk/examples/human-review. Copy it to a local folder, rename gitignore to .gitignore, install dependencies, configure .env, then run npm start -- start. The example prints the existing conversation ID and commands to inspect, send back, and explicitly approve its draft. It resumes the same workflow conversation across processes using existing step APIs. See its README.