@possibl/rcrt-sdk
v0.5.1
Published
TypeScript SDK for the RCRT multi-tenant AI BaaS — isomorphic (web + node + React Native)
Maintainers
Readme
@possibl/rcrt-sdk
TypeScript client for the RCRT multi-tenant AI BaaS. Isomorphic — works in browsers, Node 18+, and React Native with a small polyfill.
Install
npm install @possibl/rcrt-sdk
# or pnpm / yarn / bunQuickstart
import { RcrtClient, staticTokenProvider } from '@possibl/rcrt-sdk';
const rcrt = new RcrtClient({
apiUrl: 'https://rcrt-api-gateway-<hash>.run.app',
tokenProvider: staticTokenProvider(process.env.WORKSPACE_API_KEY!),
});
// 1. Resolve workspace (for tests — UI apps go through Firebase):
const { tenants } = await rcrt.auth.me().then((m) => ({ tenants: m.tenants }));
rcrt.setTenantId(tenants[0].id);
// 2. Send a chat:
const { session_id } = await rcrt.chat.send({
message: 'Summarise my inbox',
target_agent: 'life-coordinator',
});
// 3. Stream the reply:
const stream = rcrt.chat.sessionStream(session_id, {
onDelta: ({ delta }) => process.stdout.write(delta),
onMessage: (bc) => {
if (bc.tags.includes('interpret:pending-action')) {
const card = extractCard(bc);
console.log('card', card?.layout, card?.header.title);
}
},
});
// 4. When the user taps an action on a card:
await rcrt.cards.resolve(cardBreadcrumbId, { status: 'approved' });
stream.close();Browser + Firebase
import { getAuth } from 'firebase/auth';
import { RcrtClient, TokenProvider } from '@possibl/rcrt-sdk';
const auth = getAuth();
const tokenProvider: TokenProvider = {
async getIdToken() {
const user = auth.currentUser;
if (!user) throw new Error('not signed in');
return user.getIdToken(false);
},
};
const rcrt = new RcrtClient({
apiUrl: import.meta.env.VITE_RCRT_API,
tokenProvider,
});React Native
React Native has no built-in EventSource. Install a polyfill and
pass it through:
npm install react-native-sseimport EventSource from 'react-native-sse';
const stream = rcrt.chat.sessionStream(sessionId, handlers, {
eventSource: EventSource,
useHeaderAuth: true, // RN polyfill supports custom headers
});Fleet fan-out (multi-workspace apps)
A console that reads dozens of workspaces in parallel should never mutate one client's tenant. Get a cheap, cached, permanently-bound client per workspace instead:
const reports = await Promise.all(
workspaces.map(async (ws) => {
const c = rcrt.forTenant(ws.tenantId);
return c.marketplace.listInstalledBundles();
}),
);Escape hatch
Any gateway endpoint the SDK hasn't wrapped yet is one call away — with the same auth, tenant header, retries and error envelope:
const res = await rcrt.request<{ items: unknown[] }>('/v1/some/new/endpoint', {
method: 'POST',
body: { plain: 'object' }, // SDK serialises JSON
query: { limit: 50 },
});What's exported
| Symbol | Purpose |
| --- | --- |
| RcrtClient | The client. One instance per workspace session; forTenant() for fan-out. |
| TokenProvider | Interface — wire any IdP. |
| staticTokenProvider | Static-token convenience (tests, tk_* workspace keys). |
| ApiError, SdkError | Error classes. Typed + discriminable by .status and .detail.code. |
| MarketplaceModule, OrgModule, FilesModule | Bundle lifecycle, org admin + LLM tiers, file upload. |
| Breadcrumb, Card, Row, ChartSpec, ... | Every public type from the RCRT contract. Re-exportable as your UI types. |
Browse src/index.ts for the full re-export list.
Design notes
- Isomorphic: zero DOM or Node-only APIs in the core. The only
platform-touching code is the SSE client, and it takes an
EventSourceconstructor through config. - Token provider is the seam: the SDK never touches Firebase /
Auth0 / Clerk directly. You bring a
TokenProviderand it asks. - Everything is a breadcrumb:
rcrt.breadcrumbs.*is the fundamental CRUD surface; all other modules build on it.cards,grants,authare conveniences. - Optimistic locking just works:
breadcrumbs.update(id, req, { autoRetryConflict: true })andcards.resolve(...)both refetch and retry on 409. - Errors are normalised: the backend is mid-migration from
{"error": "string"}to a typed{"error": {"code", "message", "details"}}envelope.ApiError.detail.codeis set either way.
Matching docs
Full narrative + concepts + operations playbook in
packages/docs/. Start with:
Status
0.2.0 — the convergence release: marketplace/org/files modules,
forTenant() fan-out and the request() escape hatch cover the full
surface the production MSP console uses. @possibl/rcrt-api is
deprecated in favour of this package. Shape is stable for the
documented surface; minor adjustments possible before 1.0.
Licence
MIT.
