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

@diskd-ai/sdk

v5.1.14

Published

SDK for DiskD platform APIs (Drive, LLM Router, Agent Hub, MCP Hub, Telegram Userbot, Web Navigator)

Readme

@diskd-ai/sdk

Unified TypeScript SDK for the Upgraide platform APIs.

All services are accessible via the diskd factory, which provides a consistent diskd.auth.* + namespaced service pattern across diskd.os.*, diskd.platform.*, and diskd.utils.*:

import { diskd } from '@diskd-ai/sdk';

const auth = diskd.auth.apiKey({ workspaceId: '...' });

const drive      = diskd.os.drive({ version: 'v1', auth });
const sessions   = diskd.platform.sessions({
  auth,
  scope: { scopeType: 'project', projectId: 'proj-1' },
});
const crontab    = diskd.platform.crontab({
  auth,
  scope: { scopeType: 'project', projectId: 'proj-1' },
});
const db         = diskd.os.database({ auth, dbName: '...', schema: { ... } });
const ds         = diskd.os.datasource({ auth, dbName: '...', entities: [...] });
const llm        = diskd.os.llm({ auth });
const agents     = diskd.os.agents({ auth, workspaceId: '...' });
const mcp        = diskd.os.mcp({ auth, workspaceId: '...' });
const messages   = diskd.os.messagesStore({ auth });
const routines   = diskd.platform.routines({ auth });
const operatives = diskd.platform.operatives({ auth });
const calendar   = diskd.platform.calendar({ auth });
const tg         = diskd.utils.tgUserBot({ auth, workspaceId: '...' });
const webNav     = diskd.utils.webNavigator({ auth, workspaceId: '...' });

Installation

  1. Configure .npmrc in your project (or ~/.npmrc):

    @diskd:registry=https://gitlab.iosya.com/api/v4/projects/80/packages/npm/
    //gitlab.iosya.com/api/v4/projects/80/packages/npm/:_authToken=${NPM_TOKEN}
  2. Set NPM_TOKEN to a GitLab personal access token with read_api scope:

    export NPM_TOKEN=glpat-xxxxxxxxxxxx
  3. Install:

    bun add @diskd-ai/sdk

Install / build (repo)

cd mono/platform-api
bun install
bun run build

Authentication

The SDK supports two authentication modes via the AuthModule interface.

External clients (OAuth2)

Use diskd.auth.credentials() for OAuth2 service-account or PKCE browser flows:

import { diskd } from '@diskd-ai/sdk';

const auth = await diskd.auth.credentials({
  scopes: ['openid'],
  keyfilePath: 'credentials.json',
});

const drive = diskd.os.drive({ version: 'v1', auth });
const sessions = diskd.platform.sessions({
  auth,
  scope: { scopeType: 'project', projectId: 'proj-1' },
});
const crontab = diskd.platform.crontab({
  auth,
  scope: { scopeType: 'project', projectId: 'proj-1' },
});

Internal services (API key)

Use diskd.auth.apiKey() for service-to-service communication within the cluster:

import { diskd } from '@diskd-ai/sdk';

const auth = diskd.auth.apiKey({ workspaceId: process.env.WORKSPACE_ID! });

const drive = diskd.os.drive({ version: 'v1', auth });

diskd.auth.apiKey() reads APIS_API_KEY from the environment and fails fast when either APIS_API_KEY or APIS_BASE_URL is missing.

Both auth modes produce identical client instances.

Environment variables

All resource APIs resolve from the centralized gateway base URL:

| Env Variable | Default | |--------------|---------| | APIS_BASE_URL | https://apis.diskd.local:8080 | | APIS_API_KEY | none |

The gateway is the single resource entrypoint. Public gateway URLs follow the versioned convention https://apis.example/v1/{namespace}/{module}. The SDK derives API paths from the same namespace structure as the public SDK surface and lets the gateway handle API orchestration and auth strategy.

Derived default paths:

  • /v1/os/drive
  • /v1/os/database
  • /v1/os/llm
  • /v1/os/agents
  • /v1/os/mcp
  • /v1/platform/sessions
  • /v1/platform/crontab
  • /v1/platform/operatives
  • /v1/platform/projects
  • /v1/platform/routines
  • /v1/platform/events
  • /v1/platform/calendar
  • /v1/utils/tg-userbot
  • /v1/utils/web-navigator

You can still override a client with an explicit url, but the default mode is the centralized gateway.

Gateway Decision

This SDK does not treat resource APIs as independently-discovered hosts. The canonical model is one centralized apis gateway behind APIS_BASE_URL.

That means:

  • no per-service default env vars such as LLM_ROUTER_BASE_URL, AGENT_HUB_BASE_URL, or MCP_HUB_BASE_URL
  • resource clients derive their route from APIS_BASE_URL plus a namespace-derived path prefix
  • the gateway is responsible for request routing, API orchestration, and auth-strategy handling

Per-client url remains available only as an explicit override.

Drive API

Path operations

await drive.init();
const entries = await drive.list({ path: '/' });
const dir = await drive.create({ dirName: 'my-folder' });
await drive.rename({ inode: dir.inode, newName: 'renamed-folder' });
await drive.delete({ inodes: [dir.inode], recursive: true });
const resolved = await drive.resolve({ inodes: ['inode1', 'inode2'] });
await drive.updateMetadata({ inode: 'abc', metadata: { key: 'value' } });
await drive.updateAttributes({ inode: 'abc', attributes: ['pinned'] });

Upload (buffer + stream)

// Buffer upload
const result = await drive.upload.file({
  name: 'hello.txt',
  data: new TextEncoder().encode('Hello, world!'),
  mimeType: 'text/plain',
  onProgress: (uploaded, total) => console.log(`${uploaded}/${total}`),
});

// Stream upload (large files)
const result = await drive.upload.file({
  name: 'large.bin',
  stream: readableStream,
  size: 1_000_000_000,
  sha256Root: 'precomputed-hex',
});

Download (streaming)

const file = await drive.download.file({
  inode: 'abc123',
  onProgress: (downloaded, total) => console.log(`${downloaded}/${total}`),
});
await file.stream.pipeTo(writableStream);

File metadata, disk usage, tools

const meta = await drive.files.metadata({ inode: 'abc' });
const usage = await drive.diskUsage();
const ls = await drive.tools.ls({ path: '/', recursive: true });
const grep = await drive.tools.grep({ pattern: 'TODO' });

Sessions

The SDK exposes these session methods on diskd.platform.sessions({ auth, scope }):

  • start
  • open
  • save
  • list
  • delete
  • message
const session = await sessions.start({
  title: 'Deployment help',
});

await session.append([
  sessions.message({ role: 'user', content: 'How do I deploy to production?' }),
]);

const sessionList = await sessions.list();

Crontab scheduler

The SDK exposes these scheduler methods on diskd.platform.crontab({ auth, scope, timezone? }). If timezone is omitted, the SDK uses the caller runtime timezone by default.

  • save
  • get
  • getStatus
  • listJobs
  • runJob
  • createJob
await crontab.createJob({
  job: {
    jobId: '01JABCD2FGH3JK4MNP5QRST6VW',
    enabled: true,
    schedule: {
      minute: '*/5',
      hour: '*',
      dayOfMonth: '*',
      month: '*',
      dayOfWeek: '*',
    },
    request: {
      method: 'POST',
      url: 'https://example.internal/hooks/sync',
      payload: {
        kind: 'json',
        value: { source: 'sdk-example' },
      },
    },
  },
});

const status = await crontab.getStatus();

See examples/node/drive-upload-download.ts, examples/node/drive-session-external.ts, and examples/node/drive-crontab.ts.

Messages Store API

Channel-agnostic message storage on top of Drive. Each workspace mailbox is one SQLite file under /Mailboxes/<mailboxId>.mailbox; attachment bytes live in Drive under /Mailboxes/<mailboxId>/<per-message-folder>/. The wire is the messages_store/* JSON-RPC namespace served by drive.

Use it to persist email (IMAP / JMAP), Telegram, WhatsApp, or any other channel where messages live in folders inside per-account mailboxes. Message payload is opaque JSON; the store never inspects it.

The client exposes four boundaries -- mailboxes, folders, messages, attachments -- via a functional scoping pattern: each level returns a client that captures its identifiers in a closure, so callers never repeat (mailboxId, folderId, externalId) on every call.

const messagesStore = diskd.os.messagesStore({ auth });
const mailbox = messagesStore.mailbox({ mailboxId: 'gmail-acme' });
const folder  = mailbox.folder({ folderId: 'INBOX' });
const message = folder.message({ externalId: 'imap-uid-1001' });

Mailboxes

// Allocate the mailbox SQLite file under /Mailboxes/.
// metadata is opaque JSON, stashed on the underlying drive_databases record.
const created = await messagesStore.createMailbox({
  mailboxId: 'gmail-acme',
  displayName: '[email protected]',
  metadata: { protocol: 'imap', host: 'imap.gmail.com' },
});
// → { mailboxId, dbInode, drivePath: '/Mailboxes/gmail-acme.mailbox' }

// Bind a mailbox-scoped client; subsequent calls don't repeat mailboxId.
const mailbox = messagesStore.mailbox({ mailboxId: 'gmail-acme' });

// Idempotent SQLite-schema bootstrap. Required before any folder/message ops.
await mailbox.init();

// Workspace-scoped enumeration.
const all = await messagesStore.listMailboxes();
//   → readonly { mailboxId, displayName, dbInode, recordCount,
//                sizeBytes, updatedAt }[]

// Cascade-delete (mailbox file + per-mailbox attachment subtree).
await mailbox.delete();

Folders

folderId is opaque (caller-chosen) -- IMAP folder name, JMAP id, Telegram chat_id, etc. Folder metadata is the natural place for protocol-specific sync state (UIDVALIDITY/UIDNEXT/HIGHESTMODSEQ for IMAP, JMAP state, Telegram pts, etc.). The store never reads it.

// Idempotent upsert. created=true on first call, false thereafter.
await mailbox.upsertFolder({
  folderId: 'INBOX',
  displayName: 'Inbox',
  metadata: { uidvalidity: 12345, uidnext: 1101 },
});

// List folders in this mailbox.
const folders = await mailbox.listFolders();

// Bind a folder-scoped client.
const folder = mailbox.folder({ folderId: 'INBOX' });

// Update display name / sync metadata via the scoped client (folderId implicit).
await folder.upsert({
  displayName: 'Inbox',
  metadata: { uidvalidity: 12345, uidnext: 1200 },
});

// Read one folder.
const summary = await folder.get();
//   → { folderId, displayName, metadata, messageCount, updatedAt }

// Cascade-delete folder + messages + per-message attachments.
const result = await folder.delete();
//   → { folderId, deleted, deletedMessageCount }

Messages

externalId is the caller's idempotency key within the folder (IMAP UID stringified, Telegram message_id, JMAP id, ...). payload is opaque JSON the store never inspects.

A successful upsertBatch response means the batch is durable in S3 -- the SQLite head is committed before the call returns.

// Bulk insert-or-update by externalId.
const ub = await folder.upsertBatch({
  items: [
    {
      externalId: 'imap-uid-1001',
      payload: {
        subject: 'Welcome to upgraide',
        from: '[email protected]',
        receivedAt: '2026-04-28T13:21:32Z',
        labels: ['inbox', 'unread'],
      },
    },
    { externalId: 'imap-uid-1002', payload: { subject: 'Your weekly digest' } },
  ],
});
//   → { inserted: 2, updated: 0 }

// Cursor-paginated read.
let cursor: string | null = null;
do {
  const page = await folder.listMessages({ limit: 100, cursor: cursor ?? undefined });
  for (const m of page.items) {
    // m.externalId, m.payload, m.createdAt, m.updatedAt
  }
  cursor = page.nextCursor;
} while (cursor);

// Single-message lookup.
const msg = await folder.getMessage({ externalId: 'imap-uid-1001' });

// Bulk delete; missing ids are silently skipped.
const del = await folder.deleteBatch({
  externalIds: ['imap-uid-1001', 'imap-uid-1002'],
});
//   → { deleted: 2 }

Attachments

Attachments follow Drive's upload-intent contract (start → PUT bytes → commit) but are scoped to a single message. The per-message Drive folder is created lazily on the first uploadStart call.

const message = folder.message({ externalId: 'imap-uid-1001' });

// 1. Begin upload -- get an intent + presigned URL.
const intent = await message.attachments.uploadStart({
  attachmentId: 'att-1',
  filename: 'invoice.pdf',
  contentType: 'application/pdf',
  sizeBytes: 12345,
});

// 2. PUT the bytes to intent.uploadUrl with header X-Upload-Intent-Id.
//    (See Drive upload examples for streaming/buffer modes.)
const putRes = await fetch(intent.uploadUrl, {
  method: 'PUT',
  headers: { 'X-Upload-Intent-Id': intent.intentId, 'Content-Type': 'application/pdf' },
  body: pdfBytes,
});
const { etag } = (await putRes.json()) as { etag: string };

// 3. Commit -- registers the attachment row.
await message.attachments.uploadCommit({
  attachmentId: 'att-1',
  intentId: intent.intentId,
  etag,
});

// Enumerate this message's attachments.
const list = await message.attachments.list();

// Presigned download URL (with explicit expiresAt).
const dl = await message.attachments.downloadUrl({ attachmentId: 'att-1' });

// Delete the attachment row + its Drive file.
await message.attachments.delete({ attachmentId: 'att-1' });

Auth modes

Same as the rest of the SDK -- both work:

// API key (internal services / Tilt / port-forward).
const auth = diskd.auth.apiKey({ workspaceId: 'ws-...' });

// OAuth2 client-credentials (external clients).
const auth = await diskd.auth.credentials({
  scopes: ['openid'],
  keyfilePath: '.agents/credentials.json',
});

const messagesStore = diskd.os.messagesStore({ auth });

Workspace identity is always auth-derived (X-Workspace-Id header from API key, ext.workspace_id claim from OAuth JWT) -- never sent on the wire as a parameter. See examples/node/messages-store-example.ts for a full end-to-end walk-through.

Routines API

REST client for managing routines (automated workflows) scoped to profile or project:

const routines = diskd.platform.routines({ auth });

// List routines in a scope
const all = await routines.list({ scope: 'workspace' });
const projectRoutines = await routines.list({ scope: 'project', projectName: 'my-project' });

// Get by slug
const routine = await routines.get({ slug: 'daily-summary', scope: 'workspace' });

// Create
const created = await routines.create({
  name: 'Daily Summary',
  scope: 'workspace',
  operativeSlug: 'research-agent',
  triggerType: 'rhythm',
  trigger: { cron: '0 9 * * *' },
  steps: [{ id: 'step-1', name: 'Summarize', action: 'summarize', order: 0 }],
});

// Update
const updated = await routines.update(
  'daily-summary',
  { status: 'paused' },
  { scopeType: 'workspace' },
);

// Delete
await routines.delete({ slug: 'daily-summary', scope: 'workspace' });

Operatives API

REST client for managing operatives (AI agents) with attached files, skills, and MCP tools:

const ops = diskd.platform.operatives({ auth });

// List operatives in a project
const list = await ops.list({ projectId: 'proj-1' });

// Get by id or slug
const operative = await ops.get('op-01');
const bySlug = await ops.getBySlug({ projectId: 'proj-1', slug: 'research-agent' });

// Create
const created = await ops.create({
  projectId: 'proj-1',
  name: 'Research Agent',
  engine: 'deep',
  engineProvider: 'anthropic',
  engineModel: 'claude-4',
});

// Update
await ops.update('op-01', {
  orders: 'You are a research assistant focused on academic papers.',
  fileAccess: 'selected',
});

// Delete
await ops.delete('op-01');

Operative files (Drive knowledge sources)

Attach Drive files from the operative's project chroot as knowledge sources:

// Attach files (paths relative to project chroot)
await ops.files.add('op-01', { paths: ['/docs/knowledge-base', '/docs/readme.md'] });

// List attached files
const files = await ops.files.list('op-01');

// Detach a file
await ops.files.remove('op-01', files[0].id);

Operative skills

// Attach skills
await ops.skills.add('op-01', { refIds: ['web-search', 'code-review'] });

// List attached skills
const skills = await ops.skills.list('op-01');

// Detach a skill
await ops.skills.remove('op-01', skills[0].id);

Operative MCP tools

// Attach MCP tools
await ops.tools.add('op-01', { selectors: ['github/search_repos', 'slack/send_message'] });

// List attached tools
const tools = await ops.tools.list('op-01');

// Detach a tool
await ops.tools.remove('op-01', tools[0].id);

Calendar API

REST client for workspace calendar management -- events, attendees, note links, attachments, and settings:

const calendar = diskd.platform.calendar({ auth });

// Accounts and events
const accounts = await calendar.listAccounts();
const events = await calendar.listEvents({
  startAfter: '2026-03-01T00:00:00Z',
  startBefore: '2026-03-31T23:59:59Z',
});

// Event CRUD
const event = await calendar.createEvent({
  calendarId: accounts[0].calendars[0].id,
  title: 'Sprint Planning',
  startAt: '2026-03-25T10:00:00Z',
  endAt: '2026-03-25T11:00:00Z',
});

await calendar.updateEvent(event.id, {
  title: 'Sprint Planning (updated)',
  metadata: { timeBlockCategory: 'meeting' },
});

await calendar.deleteEvent(event.id);

Attendees

const attendee = await calendar.attendees.add(event.id, {
  email: '[email protected]',
  role: 'required',
});

await calendar.attendees.updateRsvp(event.id, attendee.id, 'yes');
await calendar.attendees.remove(event.id, attendee.id);

Note links

const link = await calendar.noteLinks.add(event.id, {
  noteDiskPath: '/Projects/sprint/notes/planning.md',
  title: 'Planning Notes',
  linkType: 'context',
});

await calendar.noteLinks.remove(event.id, link.id);

Attachments

const attachment = await calendar.attachments.add(event.id, {
  type: 'url',
  title: 'Meeting Recording',
  url: 'https://meet.example.com/recording/123',
});

await calendar.attachments.remove(event.id, attachment.id);

Event metadata

Events support an extensible metadata JSONB field for cross-domain data:

await calendar.updateEvent(event.id, {
  metadata: {
    timeBlockCategory: 'focus',
    linkedNotes: [
      { noteDiskPath: '/docs/spec.md', title: 'Spec', linkType: 'context' },
    ],
  },
});

Settings

const settings = await calendar.getSettings();
await calendar.updateSettings({
  weekStartDay: 0,
  defaultView: 'month',
  timezone: 'America/New_York',
});

Drive Database API

JSON-RPC client for Drive's SQLite-backed database operations:

const drive = diskd.os.drive({ version: 'v1', auth });

// Create a database with schema
const db = await drive.db.create({
  name: 'myapp.workspace-123.main',
  schema: {
    users: {
      id:    { type: 'TEXT', primaryKey: true },
      name:  { type: 'TEXT', notNull: true },
      email: { type: 'TEXT', notNull: true },
    },
  },
});

// Insert rows
await drive.db.insert({
  name: 'myapp.workspace-123.main',
  table: 'users',
  rows: [{ id: '1', name: 'Alice', email: '[email protected]' }],
});

// Query with parameters
const result = await drive.db.query({
  name: 'myapp.workspace-123.main',
  sql: 'SELECT * FROM users WHERE id = ?',
  parameters: ['1'],
});

// Commit, metadata, drop, resolve
await drive.db.commit({ name: 'myapp.workspace-123.main' });
const meta = await drive.db.metadata({ name: 'myapp.workspace-123.main' });
const resolved = await drive.db.resolveByInode({ dbInode: db.dbInode });

Drive Repository (CRUD pattern)

Higher-level database + table-scoped repository pattern -- ideal for services that use Drive DB as their persistence layer:

// Create database with schema
const db = diskd.os.database({
  auth,
  dbName: 'shop.workspace-123.main',
  dbType: 'database',
  schema: {
    users:  { id: { type: 'TEXT', primaryKey: true }, name: { type: 'TEXT', notNull: true } },
    orders: { id: { type: 'TEXT', primaryKey: true }, user_id: { type: 'TEXT' }, total: { type: 'INTEGER' } },
  },
});

await db.ensureCreated();

// Get table-scoped repositories
const users = db.repository('users');
const orders = db.repository('orders');

// Insert
await users.insert([{ id: 'u1', name: 'Alice' }, { id: 'u2', name: 'Bob' }]);

// Find with where, orderBy, limit, offset
const results = await users.find({
  where: { name: 'Alice' },
  orderBy: { column: 'name', direction: 'ASC' },
  limit: 10,
});

// Find one (returns null if not found)
const alice = await users.findOne({ id: 'u1' });

// Count
const total = await orders.count();
const pending = await orders.count({ status: 'pending' });

// Update
await orders.update({ where: { id: 'o1' }, set: { status: 'shipped' } });

// Delete
await orders.deleteRows({ status: 'cancelled' });

// Raw SQL at database level for joins and complex queries
const summary = await db.query(`
  SELECT u.name, SUM(o.total) AS revenue
  FROM users u JOIN orders o ON o.user_id = u.id
  GROUP BY u.id ORDER BY revenue DESC
`);

// Commit and metadata (database-level operations)
await db.commit();
const meta = await db.metadata();

See examples/node/drive-db-repository-example.ts.

TypeORM Driver (diskd.os.datasource())

Use TypeORM entities, relations, and repositories against Drive DB. SQL is routed through Drive DB JSON-RPC, and TypeORM's transaction lifecycle maps to Drive DB's commit/rollback semantics. Requires typeorm as a peer dependency.

Installation

npm install @diskd-ai/sdk typeorm

Usage

import { diskd } from '@diskd-ai/sdk';
import { Entity, PrimaryColumn, Column } from 'typeorm';

// Define entities
@Entity({ name: 'users' })
class User {
  @PrimaryColumn({ type: 'varchar', length: 26 })
  id!: string;

  @Column({ type: 'varchar' })
  name!: string;

  @Column({ type: 'varchar' })
  email!: string;
}

// Create DataSource backed by Drive DB
process.env.APIS_BASE_URL ??= 'https://apis.diskd.local:8080';

const auth = diskd.auth.apiKey({ workspaceId: 'workspace-123' });

const ds = diskd.os.datasource({
  auth,
  url: `${process.env.APIS_BASE_URL}/v1/os/database/api/v1`,
  dbName: 'shop.workspace-123.main',
  entities: [User],
  synchronize: true,
});

await ds.initialize();

// Standard TypeORM repository operations
const userRepo = ds.getRepository(User);
await userRepo.save({ id: 'u1', name: 'Alice', email: '[email protected]' });

const alice = await userRepo.findOneBy({ id: 'u1' });
const users = await userRepo.find({ order: { name: 'ASC' } });

// Persist to S3 (flush WAL)
await ds.driver.commit();

// Rollback discards uncommitted changes (revert to last commit)
await ds.driver.driveRollback();

Transaction mapping

| TypeORM operation | Drive DB action | |------------------------|----------------------------------------| | BEGIN TRANSACTION | No-op (writes auto-accumulate in WAL) | | COMMIT | drive.db.commit() -- flush WAL to S3 | | ROLLBACK | drive.db.rollback() -- discard WAL |

Limitations (v1)

  • No nested transactions / savepoints (deferred to v2)
  • Affected row count returns 0 (each JSON-RPC call is a separate SQLite connection; works fine for ULID-based entities)
  • Schema introspection from live database is limited; synchronize: true generates DDL directly

See examples/node/typeorm-drive-example.ts and docs/typeorm-driver-design.md.

LLM Router API

JSON-RPC 2.0 + NDJSON streaming for multi-provider LLM completions:

const llm = diskd.os.llm({ auth });

// Non-streaming completion
const result = await llm.completions.create({
  provider: 'openai', model: 'gpt-4o-mini',
  messages: [{ role: 'user', content: 'Hello' }],
  maxTokens: 64,
});

// Streaming
for await (const chunk of llm.completions.stream(params)) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}

// Models, embeddings, OCR, audio transcription
const models = await llm.models.listAll();
const embeddings = await llm.embeddings.create({ provider: 'openai', model: 'text-embedding-3-small', input: ['text'] });

See examples/node/llm-router-example.ts.

Agent Hub API

SSE streaming with StreamProtocolHandler + StreamProtocolFetcher for agent invocation:

import { diskd, StreamProtocolHandler } from '@diskd-ai/sdk';

const agents = diskd.os.agents({ auth, workspaceId: '...' });

// List agents and models
const agentList = await agents.agents.list();
const models = await agents.agents.getSupportedModels('assistant');
const billing = await agents.billing.getAliases();

// Invoke with fluent stream handling
const handler = new StreamProtocolHandler()
  .on('response.output_text.delta', (e) => process.stdout.write(e.delta))
  .on('response.completed', (e) => console.log('done', e.response.usage))
  .on('response.failed', (e) => console.error(e.response.error.message))
  .on('error', (e) => console.error(e.message));

const stream = await agents.invoke({
  agentName: 'assistant',
  query: 'Hello',
  agentOptions: { maxTokens: 256 },
});

stream.map((event) => handler.handle(event))
  .stop(() => console.log('stream closed'))
  .catch((err) => console.error(err));

Stream protocol events include text deltas, function calls/results, content parts (images, files, audio), web/file search lifecycle, external sources, plan updates, notifications, and error/completion signals.

See examples/node/agent-hub-example.ts.

MCP Hub API

REST client for MCP server catalog, registry, and integrations:

const mcp = diskd.os.mcp({ auth, workspaceId: '...' });

// Catalog
const catalog = await mcp.catalog.list({ search: 'github' });
const details = await mcp.catalog.getServerDetails(serverId);

// Registry (installed servers)
const registry = await mcp.registry.list();
const added = await mcp.registry.addServer({ catalogServerId: '...' });
await mcp.registry.toggleTool(serverId, toolId, false);
const logs = await mcp.registry.getServerLogs(serverId, { limit: 10 });
await mcp.registry.deleteServer(serverId);

// Env vars, connection settings, remote servers
await mcp.registry.upsertEnvVar(serverId, { key: 'TOKEN', value: '...' });
await mcp.registry.addRemoteServer({ name: 'My MCP', url: '...', authType: 'pat' });

See examples/node/mcp-hub-example.ts.

Telegram Userbot API

REST client for Telegram channel resolution, importing, and message retrieval:

const tg = diskd.utils.tgUserBot({ auth, workspaceId: '...' });

// Resolve channel (public, no auth required)
const resolved = await tg.channels.resolve('durov');

// Channel operations
const channels = await tg.channels.list();
await tg.channels.add({ channelIdentifier: '@mychannel', limit: 1000 });
await tg.channels.sync({ telegramId: -1001234567890 });

// Messages and stats
const messages = await tg.channels.getMessages(channelId, { limit: 50, searchText: 'keyword' });
const stats = await tg.channels.getStats(channelId);
const status = await tg.channels.getStatus(channelId);

// Tasks
const tasks = await tg.tasks.list();
await tg.tasks.cancel(taskUuid);

See examples/node/tg-userbot-example.ts.

Web Navigator API

REST client for URL resolution and web scraping jobs:

const webNav = diskd.utils.webNavigator({ auth, workspaceId: '...' });

// Resolve URL metadata
const meta = await webNav.resolve({ url: 'https://example.com' });

// Submit scrape job
const job = await webNav.scrape.submit({ url: 'https://example.com', depth: 1, maxPages: 10 });
const status = await webNav.scrape.getStatus(job.jobId);
const result = await webNav.scrape.getResult(job.jobId);
await webNav.scrape.cancel(job.jobId);

See examples/node/web-navigator-example.ts.

Web quickstart (Vite + PKCE)

Use @diskd-ai/sdk/browser and a standard OAuth2 Authorization Code + PKCE redirect.

Runnable example: examples/web/ (see examples/README.md).

Publishing a new version

  1. Bump the version in package.json:

    npm version patch   # 0.3.0 -> 0.3.1
    npm version minor   # 0.3.0 -> 0.4.0
    npm version major   # 0.3.0 -> 1.0.0
  2. Push the commit and tag:

    git push gitlab main --tags
  3. The GitLab CI pipeline triggers on v*.*.* tags and automatically:

    • Builds the project
    • Runs unit tests and typecheck
    • Publishes to the GitLab Package Registry
  4. Verify at: https://gitlab.iosya.com/upgraide-v2/platform-api/-/packages

Docs and examples

  • Quickstart: docs/sdk-quickstart.md
  • Examples: examples/README.md
  • Design: docs/drive-session-sdk-design.md