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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@nexical/sdk

v0.4.2

Published

Official TypeScript SDK for the Nexical Orchestrator API

Readme

Nexical SDK Reference

The Nexical SDK (@nexical/sdk) is the official TypeScript library for interacting with the Nexical Orchestrator. It provides a typed, robust interface for managing users, teams, projects, and executing jobs.

This SDK is designed for four primary developer personas:

  1. Browser Extension Developers: Building tools to monitor and trigger jobs.
  2. Web Application Developers: Building management interfaces or dashboards.
  3. Factory Worker Developers: Building custom job execution runtimes (workers).
  4. Remote Agent Developers: Building AI agents running inside job containers.

Installation

npm install @nexical/sdk
# or
yarn add @nexical/sdk


1. Getting Started

Initialize the client with your access token.

import { NexicalClient } from '@nexical/sdk';

// Initialize with a static token (e.g., from environment or local storage)
const client = new NexicalClient({
    token: 'nx_abc123...', // API Token or JWT
});

1.1 Authentication Deep Dive

The SDK supports multiple authentication strategies depending on your runtime environment.

A. API Tokens (Long-Lived)

Best for: CI/CD pipelines, Backend Scripts, Web Servers.

  1. Log in to the Nexical Dashboard.
  2. Navigate to Settings > API Tokens.
  3. Click "Generate New Token" and select scope (e.g., read, write).
  4. Copy the token (starts with nx_...) and store it securely (e.g., .env).
const client = new NexicalClient({
    token: process.env.NEXICAL_API_TOKEN
});

B. Worker Enrollment (Automated)

Best for: Headless factory workers, GPU nodes.

Worker auth uses a "Golden Enrollment Token" to bootstrap trust. The SDK handles the exchange automatically.

  1. Generate an Enrollment Token in the dashboard (System Admin only).
  2. Set NEXICAL_ENROLLMENT_TOKEN in your worker's environment.
  3. The NexicalWorker class will:
    • Detect the env var.
    • Call /auth/worker/enroll to exchange it for a session JWT.
    • Cache the JWT in memory for subsequent requests.
// No manual token handling needed if using NexicalWorker + Env Var
const worker = new NexicalWorker(client, {
    workerId: 'my-node',
    enrollmentToken: process.env.NEXICAL_ENROLLMENT_TOKEN
});

C. Device Flow (Interactive)

Best for: CLI Tools, Desktop Apps, Developer-facing extensions.

Allows a user to authorize a headless device by visiting a URL on their phone/laptop.

// 1. Start Flow
// This tells the API "I want to authenticate user on this device"
const token = await client.auth.authenticateDevice('my-cli-app', (userCode, verificationUri) => {
    // 2. Prompt User
    console.log(`Please visit ${verificationUri} and enter code: ${userCode}`);
    // Ideally, open the browser automatically for them here.
});

// 3. Authenticated!
// The SDK polls until the user approves, then returns the token.
client.setToken(token);
console.log('Successfully logged in as', (await client.users.me()).fullName);

2. Developer Guides

🧩 Browser Extension Developer

Goal: Monitor build status, trigger deployments, and view logs from a browser popup.

Recommended Auth: API Token (generated by user) or Device Flow (if interactive).

// 1. List my teams
const teams = await client.teams.list();
const teamId = teams[0].id;

// 2. List projects in the team
const projects = await client.projects.list(teamId);
const project = projects.find(p => p.name === 'Frontend App');

if (project) {
    // 3. Find target branch (e.g., main)
    const branches = await client.branches.list(teamId, project.id);
    let branch = branches.find(b => b.name === 'main');
    
    // Create branch if missing (optional logic)
    if (!branch) {
         branch = await client.branches.create(teamId, project.id, { name: 'main' });
    }

    // 4. Trigger a new deployment on the branch
    const newJob = await client.jobs.create(teamId, project.id, branch.id, {
        type: 'deploy',
        inputs: { commit: 'latest' }
    });
    console.log(`Triggered Job ${newJob.id}`);

    // 5. Poll for status
    const interval = setInterval(async () => {
        const job = await client.jobs.get(teamId, project.id, branch.id, newJob.id);
        if (job.status !== 'pending' && job.status !== 'running') {
            clearInterval(interval);
            console.log(`Job finished: ${job.status}`);
        }
    }, 2000);
}

💻 Web Application Developer

Goal: Build a full management dashboard for creating teams, inviting users, and configuring projects.

// 1. Create a new Team
const team = await client.teams.create({
    name: 'Research Division',
    slug: 'research-div'
});

// 2. Invite a colleague
await client.teams.inviteMember(team.id, {
    email: '[email protected]',
    role: 'admin'
});

// 3. Configure a Project
const project = await client.projects.create(team.id, {
    name: 'AI Model Training',
    repoUrl: 'github.com/org/ai-model',
    productionUrl: 'https://model.ai'
});

// 4. Create a Default Branch
const mainBranch = await client.branches.create(team.id, project.id, {
    name: 'main',
    previewUrl: 'https://main.model.ai'
});

// 4. Update User Profile
await client.users.update({
    fullName: 'Bob Manager',
    avatarUrl: 'https://...'
});

🏭 Factory Worker Developer

Goal: Build a custom runtime (e.g., a GPU cluster node) that polls the Orchestrator for heavy compute jobs.

Recommended Auth: Enrollment Token (headless) or Device Flow (interactive CLI).

The SDK provides a NexicalWorker class that handles polling logic, backoff (jitter), and concurrency for you.

import { NexicalClient, NexicalWorker } from '@nexical/sdk';

const client = new NexicalClient();

// 1. Initialize Worker
// The worker will automatically exchange the ENROLLMENT_TOKEN for a session token.
const worker = new NexicalWorker(client, {
    workerId: 'gpu-node-01',
    concurrency: 2, // Process 2 jobs in parallel
    enrollmentToken: process.env.NEXICAL_ENROLLMENT_TOKEN
});

// 2. Define Job Processor
// This function is compatible with the "JobProcessor" type.
const processor = async (job) => {
    console.log(`[Job ${job.id}] Starting execution...`);
    
    // ... Perform heavy compute ...
    
    // Add logs back to the platform
    // Note: We need teamId/projectId/branchId from job metadata
    await client.jobs.addLog(job.teamId, job.projectId, job.branchId, job.id, {
        message: 'Compute complete. Uploading artifacts...',
        level: 'info'
    });
};

// 3. Start Polling
// This promise resolves only when worker.stop() is called.
await worker.start(processor);

🤖 Remote Agent Developer

Goal: Write code that runs inside the job container (e.g., an LLM Agent) and needs to interact with the platform securely.

Auth: The Agent uses a temporary token minted by the Worker.

// Inside the job container, these might be passed as ENV vars or args
const TEAM_ID = ...;
const PROJECT_ID = ...;
const BRANCH_ID = ...;
const JOB_ID = ...;

// 1. Get a GitHub Token to clone the repo
// (Only works if the worker specifically requests it or if it's part of the job context)
try {
    const gitToken = await client.jobs.getGitToken(TEAM_ID, PROJECT_ID, BRANCH_ID, JOB_ID);
    console.log(`Cloning with token expiring at ${gitToken.expires_at}`);
} catch (err) {
    console.error('Failed to get Git token:', err);
}

// 2. Mint a sub-token for a sub-agent
// If this process spawns another isolated process, it can vend a restricted token.
const agentToken = await client.jobs.getAgentToken(TEAM_ID, PROJECT_ID, BRANCH_ID, JOB_ID);

3. API Reference & Coverage

This table maps the Orchestrator API endpoints to the corresponding SDK methods.

✅ = Supported & Verified ⚠️ = Partially Supported / Different Signature ❌ = Not Implemented in SDK

Authentication (client.auth)

| API Endpoint | SDK Method | Status | | :--- | :--- | :--- | | POST /auth/users | createSystemUser(data) | ✅ | | POST /auth/tokens | generateToken(data) | ✅ | | GET /auth/tokens | listTokens() | ✅ | | DELETE /auth/tokens/:id | revokeToken(id) | ✅ | | POST /auth/worker/enroll | enrollWorker(data) | ✅ | | POST /device/* | authenticateDevice(clientId, cb) | ✅ (Orchestrated) |

Users (client.users)

| API Endpoint | SDK Method | Status | | :--- | :--- | :--- | | GET /users/me | me() | ✅ | | PUT /users/me | update(data) | ✅ |

Teams (client.teams)

| API Endpoint | SDK Method | Status | | :--- | :--- | :--- | | GET /teams | list() | ✅ | | POST /teams | create(data) | ✅ | | GET /teams/:id | get(id) | ✅ | | PUT /teams/:id | update(id, data) | ✅ | | POST /teams/:id/invites | inviteMember(id, data) | ✅ | | DELETE /teams/:id/members/:uid | removeMember(id, uid) | ✅ | | DELETE /teams/:id | delete(id) | ✅ |

Projects (client.projects)

| API Endpoint | SDK Method | Status | | :--- | :--- | :--- | | GET /teams/:tid/projects | list(teamId) | ✅ | | POST /teams/:tid/projects | create(teamId, data) | ✅ | | GET /teams/:tid/projects/:pid | get(teamId, projId) | ✅ | | PUT /teams/:tid/projects/:pid | update(teamId, projId, data) | ✅ | | DELETE /teams/:tid/projects/:pid | delete(teamId, projId) | ✅ |

Branches (client.branches)

| API Endpoint | SDK Method | Status | | :--- | :--- | :--- | | GET /teams/:tid/projects/:pid/branches | list(teamId, projId) | ✅ | | POST /teams/:tid/projects/:pid/branches | create(teamId, projId, data) | ✅ | | GET /teams/:tid/projects/:pid/branches/:bid | get(teamId, projId, branchId) | ✅ | | DELETE /teams/:tid/projects/:pid/branches/:bid | delete(teamId, projId, branchId) | ✅ |

Jobs (client.jobs)

| API Endpoint | SDK Method | Status | | :--- | :--- | :--- | | GET /:branchId/jobs | list(teamId, projId, branchId) * | ⚠️ (Requires teamId scope) | | POST /:branchId/jobs | create(teamId, projId, branchId, data) * | ⚠️ (Requires teamId scope) | | GET /:jobId | get(teamId, projId, branchId, jobId) * | ⚠️ (Requires full scope) | | GET /:jobId/logs | getLogs(teamId, projId, branchId, jobId) | ✅ | | POST /:jobId/logs | addLog(teamId, projId, branchId, jobId, data) | ✅ | | GET /jobs/:id/git-token | getGitToken(teamId, projId, branchId, jobId) | ✅ | | POST /jobs/:id/agent-token | getAgentToken(teamId, projId, branchId, jobId) | ✅ |

* Note: The SDK enforces strict hierarchical scoping (/teams/:id/projects/:id/branches/:id/...) to ensure correct resource addressing and team-based authorization checks.

Workers (client.workers)

| API Endpoint | SDK Method | Status | | :--- | :--- | :--- | | POST /workers/acquire | acquireJob() | ✅ |

Error Handling

The SDK throws typed error classes for easier handling:

  • NexicalNetworkError: Connection failures (DNS, Timeout).
  • NexicalAuthError: 401 Unauthorized / 403 Forbidden.
  • NexicalRateLimitError: 429 Too Many Requests.
  • NexicalContractError: Response did not match expected Zod schema.
  • NexicalAPIError: Generic 4xx/5xx errors (includes code and message).
try {
  await client.teams.create({ ... });
} catch (error) {
  if (error instanceof NexicalContractError) {
    console.error('Server returned invalid data format', error.validationErrors);
  } else if (error instanceof NexicalAPIError) {
    console.error(`API Error ${error.statusCode}: ${error.message}`);
  }
}

4. Data Models

The SDK exports TypeScript interfaces for all API resources. These match the Zod schemas used for runtime validation.

Core Resources

export interface User {
  id: string; // UUID
  email?: string;
  fullName: string | null;
  avatarUrl: string | null;
  role: 'user' | 'system';
  createdAt: string;
  updatedAt: string;
}

export interface Team {
  id: number;
  name: string;
  slug: string;
  billingPlan: string;
  role?: 'owner' | 'admin' | 'member'; // Current user's role
  createdAt: string;
  updatedAt: string;
}

export interface Project {
  id: number;
  teamId: number;
  name: string;
  repoUrl: string | null;
  productionUrl: string | null;
  mode: 'managed' | 'self_hosted';
  contextHash: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface Branch {
  id: number;
  projectId: number;
  name: string;
  previewUrl: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface Job {
  id: number;
  branchId: number;
  type: string; // e.g. "deploy", "build"
  status: 'pending' | 'running' | 'completed' | 'failed';
  queue: string; // e.g. "public", "private-123"
  inputs: Record<string, any> | null;
  outputs: Record<string, any> | null;
  startedAt: string | null;
  completedAt: string | null;
  createdAt: string;
}

export interface JobLog {
  id: number;
  jobId: number;
  level: 'info' | 'warn' | 'error';
  message: string;
  metadata: Record<string, any> | null;
  timestamp: string;
}

Context Interfaces

Workers

export interface Worker {
  id: string; // UUID
  name: string;
  teamId: number | null; // Null for managed/public workers
  lastSeenAt: string | null;
}

Tokens

export interface ApiToken {
  id: number;
  name: string;
  tokenPrefix: string;
  scopes: string[] | null;
  lastUsedAt: string | null;
  expiresAt: string | null;
}

export interface GitTokenResponse {
  token: string;
  expires_at: string; // ISO Date
}

Request Objects

Useful payload types for create and update methods.

export interface CreateBranchRequest {
  name: string;
  previewUrl?: string;
}

export interface CreateJobRequest {
  type: string;
  inputs?: Record<string, any>;
}

export interface EnrollWorkerRequest {
  token: string; // The Golden Enrollment Token
  metadata?: Record<string, any>;
}