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

@schift-io/sdk

v4.0.0

Published

Workspace SDK for bucket search, documents, jobs, and Structify

Readme

@schift-io/sdk

TypeScript SDK 4.0.0 for bucket search, document upload, jobs, and Structify.

Install

npm install @schift-io/sdk

Install from the private monorepo before npm release

The SDK package is nested under clients/sdk/ts, so an npm Git URL would install the repository root instead of this package. Clone the private monorepo, build the SDK, and install that directory:

git clone --depth 1 [email protected]:schift-io/schift.git .vendor/schift
npm --prefix .vendor/schift/clients/sdk/ts install
npm --prefix .vendor/schift/clients/sdk/ts run build
npm install ./.vendor/schift/clients/sdk/ts

Pin the clone to the reviewed commit in CI rather than tracking a moving branch. This is a pre-release installation path; no npm release or tag is required.

Current scope

The current repository release lane keeps the public SDK focused on bucket search and ingest, documents, jobs, and Structify document extraction. Agent Hub, RAG chat, LLM completions, the agent framework, Warehouse Data, gateway vectors, usage reads, provider-key management, migration, benchmark-suite and drift-monitor clients are removed as of 1.0.0. client.agents (Managed Agents) and the APM versioning client are removed as of 3.0.0. Embeddings, the model catalog, collections, rerank, aggregate, routing, artifacts, bucket context and bucket graph clients are removed as of 4.0.0; use bucket search / retrieve instead (see CHANGELOG).

Quick Start

import { WorkspaceClient } from "@schift-io/sdk";

const client = new WorkspaceClient({
  apiKey: "sch_your_api_key",
  baseUrl: process.env.SCHIFT_API_URL!,
});

// Create or reuse a bucket, upload a document, then search it.
// All bucket methods accept a name or ID — no need to track UUIDs.
await client.createBucket({ name: "company-docs" });
const file = new File([await readFile("manual.pdf")], "manual.pdf", {
  type: "application/pdf",
});
await client.db.upload("company-docs", { files: [file] });

const jobs = await client.listJobs({ bucket: "company-docs", limit: 5 });
const search = await client.bucketSearch("company-docs", {
  query: "refund policy",
  topK: 5,
});

console.log(jobs[0]?.status ?? "queued");
console.log(search.context, search.citations);

Use search({ bucket: ... }) for an answer-ready context block with citations, retrieve(bucketId, { query }) for raw scored chunks.

API Key

Create or copy your API key in Console → API keys (/app/console/keys).

Configure the hosted API origin with baseUrl, SCHIFT_API_URL, or SCHIFT_BASE_URL. The SDK does not assume a production origin when none is configured.

const client = new WorkspaceClient({
  apiKey: process.env.SCHIFT_API_KEY!,
  baseUrl: process.env.SCHIFT_API_URL!,
});

Local Auth Readiness

/v1/auth/me validates a user JWT, not a sch_* API key. Smoke tests should use fixed accounts from .env/.env.local; do not generate timestamped signup emails against production.

import { WorkspaceClient } from "@schift-io/sdk";

const auth = WorkspaceClient.auth({ baseUrl: "http://127.0.0.1:8011" });
await auth.health();
const login = await auth.login({
  email: process.env.SCHIFT_SMOKE_QA_EMAIL!,
  password: process.env.SCHIFT_SMOKE_QA_PASSWORD!,
});
const me = await auth.me(login.token!);
console.log(me.user.email, me.orgs[0]?.name);

With a local API already running:

SCHIFT_API_URL=http://127.0.0.1:8011 \
[email protected] \
SCHIFT_SMOKE_QA_PASSWORD=... \
npm run smoke:auth

Features

Search (answer-ready)

const response = await client.search({
  query: "How does projection work?",
  bucket: "my-docs",
  topK: 10,
});
// response: { status, operational_status, bucket_id, query,
//             context: string, citations: [{ index, title?, page?, ... }],
//             warnings: [{ code, message, severity }] }

Retrieve (raw chunks)

const retrieved = await client.retrieve("my-docs-bucket-id", {
  query: "refund policy",
  topK: 8,
  options: { rerank: { enabled: true } },
});
// retrieved.results: Array<{ chunk_id, text, score, document_id?, metadata?, ... }>

Provider keys

The 0.16.0 core SDK does not manage provider keys. Configure provider connections in the supported Schift console surface or in the application runtime that owns the provider credentials; do not use a client.providers helper from this package.

Agent SDK Compatibility

The SDK sits underneath the agent framework. The integration point is always the same:

  1. let the agent call a workspace search tool
  2. run retrieval against workspace buckets
  3. return grounded chunks back to the model

That means you can keep your preferred agent SDK and swap only the retrieval layer.

Google Gen AI SDK

import { GoogleGenAI } from "@google/genai";
import { WorkspaceClient } from "@schift-io/sdk";

const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
const client = new WorkspaceClient({
  apiKey: process.env.SCHIFT_API_KEY!,
  baseUrl: process.env.SCHIFT_API_URL!,
});

const firstTurn = await ai.models.generateContent({
  model: "gemini-2.5-flash",
  contents: "What changed in the latest billing policy?",
  config: {
    tools: client.tools.googleGenAI(),
  },
});

const functionCall = firstTurn.functionCalls?.[0];
if (functionCall) {
  const functionResponsePart = await client.tools.googleFunctionResponse({
    name: functionCall.name,
    args: functionCall.args ?? {},
  });

  const secondTurn = await ai.models.generateContent({
    model: "gemini-2.5-flash",
    contents: [
      { role: "user", parts: [{ text: "What changed in the latest billing policy?" }] },
      { role: "model", parts: [{ functionCall }] },
      { role: "user", parts: [functionResponsePart] },
    ],
  });

  console.log(secondTurn.text);
}

Vercel AI SDK

import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { WorkspaceClient } from "@schift-io/sdk";

const client = new WorkspaceClient({
  apiKey: process.env.SCHIFT_API_KEY!,
  baseUrl: process.env.SCHIFT_API_URL!,
});

const result = await generateText({
  model: openai("gpt-4o-mini"),
  prompt: "What changed in the latest billing policy?",
  tools: client.tools.vercelAI(),
  maxSteps: 5,
});

console.log(result.text);

Mastra

If you are using Mastra, wrap client.search() in a Mastra tool and keep the rest of the agent stack unchanged.

import { Agent } from "@mastra/core/agent";
import { createTool } from "@mastra/core/tools";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
import { WorkspaceClient } from "@schift-io/sdk";

const client = new WorkspaceClient({
  apiKey: process.env.SCHIFT_API_KEY!,
  baseUrl: process.env.SCHIFT_API_URL!,
});

const workspaceSearchTool = createTool({
  id: "workspace-search",
  description: "Retrieve context from a workspace bucket.",
  inputSchema: z.object({
    bucket: z.string(),
    query: z.string(),
    topK: z.number().int().min(1).max(10).default(5),
  }),
  execute: async ({ context }) => ({
    search: await client.search({
      bucket: context.bucket,
      query: context.query,
      topK: context.topK,
    }),
  }),
});

const agent = new Agent({
  name: "docs-agent",
  instructions: "Use workspace-search before answering document questions.",
  model: openai("gpt-4o-mini"),
  tools: { workspaceSearchTool },
});

Examples:

Buckets

// List all buckets
const buckets = await client.listBuckets();

await client.db.upload("company-docs", {
  files: [file],
});

const collections = await client.listBucketCollections("company-docs");

Bucket search is permission-scoped: the server searches only the child collections the caller can access and merges the ranked results.

Document Metadata And Connectors

Uploading a document or syncing it in through a connector (Notion, Gmail, Obsidian, …) attaches metadata you can filter on at search time.

Reserved keys — populated structurally (connector API field mapping or Markdown frontmatter parsing, never LLM-inferred). Availability depends on what the source provides.

| Key | Meaning | Always set? | |---|---|---| | source_kind | Kind of source (notion_page, email, markdown_note, upload, …) | yes | | source_created_at | Original creation time (ISO 8601) | if source provides it | | source_modified_at | Original last-modified time (ISO 8601) | if source provides it | | source_author | Author | if source provides it | | source_uri | Deep link back to the original source | if source provides it | | source_filename | File name | uploads always; connectors if provided | | source_tags | Tags/labels from the source (JSON-encoded array string) | if source provides it | | ingested_by | Who/what ingested it (upload or connector:<provider>) | yes | | ingested_at | When Schift received the document | yes | | cclg_source | Internal lineage identifier | yes |

Avoid naming custom metadata keys the same as these — the system value takes precedence.

For Markdown uploads (.md/.markdown), pass parseFrontmatter: true to promote YAML frontmatter (created/date/author/tags) into the reserved keys above:

await client.db.upload("company-docs", {
  files: [file],
  parseFrontmatter: true,
});

Custom metadata is always explicit — Schift does not infer business metadata from document content:

await client.db.upload("company-docs", {
  files: [file],
  metadata: { department: "finance", priority: "high" },
});

Custom metadata is set per document and inherited by every chunk derived from it. Values must be strings, numbers, or booleans (coerced to strings server-side).

Filtering — reserved and custom keys are filtered the same way, with flat, top-level keys (not a nested { metadata: {...} } object):

const resp = await client.search({
  query: "revenue guidance",
  bucket: "company-docs",
  filters: {
    source_kind: "email",
    department: "finance",
  },
});

There is no contains operator today — use like ({ like: "%urgent%" }) for substring matches, including against JSON-encoded fields like source_tags.

Configuration

const client = new WorkspaceClient({
  apiKey: "sch_...",                      // required
  baseUrl: process.env.SCHIFT_API_URL,    // required unless SCHIFT_API_URL/SCHIFT_BASE_URL is set
  timeout: 60_000,                        // default, in milliseconds
});

Error Handling

import {
  WorkspaceClient,
  AuthError,
  QuotaError,
  PlatformError,
} from "@schift-io/sdk";

const client = new WorkspaceClient({
  apiKey: process.env.SCHIFT_API_KEY!,
  baseUrl: process.env.SCHIFT_API_URL!,
});

try {
  await client.search({ bucket: "my-docs", query: "test" });
} catch (err) {
  if (err instanceof AuthError) {
    // 401: Invalid or expired API key
  } else if (err instanceof QuotaError) {
    // 402: Insufficient credits
  } else if (err instanceof PlatformError) {
    // Other API errors (403, 422, 429, 500, 502)
    console.error(err.message, err.status);
  }
}

Releases

Published from the schift-io/schift monorepo to npm via .github/workflows/sdk-publish-ts.yml. Cut a release with:

# 1. Bump version
$EDITOR clients/sdk/ts/package.json   # version: "0.X.Y"
git add clients/sdk/ts/package.json && git commit -m "chore(sdk-ts): bump 0.X.Y"
git push origin main

# 2. Create the release (tag pattern: npm-v*)
gh release create npm-v0.X.Y \
  --title "@schift-io/sdk v0.X.Y" \
  --notes "..."

The workflow verifies the tag matches package.json, runs tsc, npm test, npm run build, then npm publish --access public.

The public mirror at schift-io/schift-ts is sync-only (no workflows there). All publish automation lives in the monorepo.

License

MIT

이 SDK는 직접 설정한 Schift API와 그 OpenAI 호환 프록시에 Schift-Version: 2026-10-01을 보내며, 독립 OpenAI 호환 헬퍼는 알려진 Schift 주소 또는 PLATFORM_*·SCHIFT_* API URL 설정과 정확히 같은 origin(스킴·호스트·포트)에만 헤더를 추가한다.