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

@keycase/agent

v0.1.0

Published

TypeScript SDK for Keycase automation platform - keyword-driven test automation agent

Downloads

75

Readme

@keycase/agent

TypeScript SDK for the Keycase automation platform — a keyword-driven test automation agent.

Converted from the Python SDK with identical WebSocket protocol and API surface.

Installation

npm install @keycase/agent

Quick Start

WebSocket Mode (Server-Connected)

import { KeycaseAgent, loadConfig, loadKeywords } from '@keycase/agent';

loadKeywords('my_keywords');

const config = loadConfig();
const agent = new KeycaseAgent(config);
await agent.start();

Local Mode (Standalone)

import { ExecutionManager, loadKeywords } from '@keycase/agent';

loadKeywords('my_keywords');

const manager = new ExecutionManager(undefined, undefined, 'local');
const results = await manager.executeLocal(executionPlan);

Configuration

Set these environment variables (or create a .env file):

| Variable | Required | Description | |----------|----------|-------------| | HTTP_URL | Yes | Base HTTP API URL | | AGENT_TOKEN | Yes | AgentToken (must start with agt_) | | AGENT_NAME | Yes | Unique agent name within organisation | | AGENT_VERSION | No | Agent version (default: 1.0.0) | | AGENT_CAPABILITIES | No | Comma-separated list of capabilities | | AGENT_TAGS | No | Comma-separated list of tags |

loadConfig() reads these from environment variables, validates them (invalid URL or missing agt_ prefix throws immediately), and returns a typed config object. In local development, place a .env file in your project root — it is loaded automatically. In production, set the variables directly on the server.

Defining Keywords

Use @Keyword, @Input, and @Output decorators on class methods. Keywords register automatically the moment the class is defined — just import the file.

import { Keyword, Input, Output } from '@keycase/agent';

class CalculatorKeywords {
  @Keyword('Calculator Add')
  @Input('num1', { required: true, description: 'First number' })
  @Input('num2', { required: true, description: 'Second number' })
  @Output('result', { description: 'The sum' })
  add(kwargs: { num1: string; num2: string }) {
    return { result: parseFloat(kwargs.num1) + parseFloat(kwargs.num2) };
  }

  @Keyword('Calculator Divide')
  @Input('dividend', { required: true })
  @Input('divisor', { required: true })
  @Output('result')
  divide(kwargs: { dividend: string; divisor: string }) {
    if (parseFloat(kwargs.divisor) === 0) throw new Error('Cannot divide by zero');
    return { result: parseFloat(kwargs.dividend) / parseFloat(kwargs.divisor) };
  }
}
import './myKeywords'; // decorators run → keywords live in registry immediately

@Input Options

@Input('name', {
  type: 'string',       // default, can omit
  required: false,      // default, can omit
  default: 'World',
  description: 'Recipient name',
  choices: ['Alice', 'Bob'],   // renders as dropdown in UI
  binding: 'recipient_name',   // see Binding below
})

Binding — Decouple Server Name from Variable Name

When the server sends a different param name than what you want inside your method:

class UserKeywords {
  @Keyword('Greet User')
  @Input('name', { binding: 'recipient_name', required: true })
  @Output('greeting')
  greet(kwargs: { name: string }) {
    // server sends 'recipient_name' → arrives here as kwargs.name
    return { greeting: `Hello, ${kwargs.name}!` };
  }
}

Without binding, the @Input name is used for both — existing keywords need no changes.

Multiple Outputs

class UserKeywords {
  @Keyword('Get User Info')
  @Input('user_id', { required: true })
  @Output('username')
  @Output('email')
  getUserInfo(kwargs: { user_id: string }) {
    return {
      username: `user_${kwargs.user_id}`,
      email: `user_${kwargs.user_id}@example.com`,
    };
  }
}

Parameter with Choices

class PriorityKeywords {
  @Keyword('Set Priority')
  @Input('priority', {
    required: true,
    choices: ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'],
  })
  @Output('message')
  setPriority(kwargs: { priority: string }) {
    return { message: `Priority set to: ${kwargs.priority}` };
  }
}

Parameter Types

import { ParamType } from '@keycase/agent';

ParamType.STRING   // 'string'  ← default
ParamType.NUMBER   // 'number'
ParamType.INTEGER  // 'integer'
ParamType.BOOLEAN  // 'boolean'
ParamType.OBJECT   // 'object'
ParamType.ARRAY    // 'array'
ParamType.ANY      // 'any'

Loading Keywords

// Single folder
loadKeywords('my_keywords');

// Multiple folders
loadKeywords(['keywords/ui', 'keywords/api', 'keywords/db']);

Duplicate keyword names throw a KeywordDefinitionError immediately at startup so misconfiguration is never silent.

Lifecycle Hooks

import { BeforeRun, AfterRun } from '@keycase/agent';

BeforeRun(() => {
  console.log('Setting up before execution...');
});

AfterRun(() => {
  console.log('Cleaning up after execution...');
});

Schema Introspection

import { getAllKeywordSchemas } from '@keycase/agent';

const all = getAllKeywordSchemas();
console.log(all);

API Reference

Core Classes

| Class | Description | |-------|-------------| | KeycaseAgent | Main orchestrator: auth, WebSocket, execution | | ExecutionManager | Flow execution engine with parameter passing | | AuthService | Token-based auth with background refresh | | WebSocketClient | Robust WS with reconnection, heartbeat, queuing | | AgentStateTracker | Busy/available state management |

Decorators

| Decorator | Description | |-----------|-------------| | @Keyword(name, opts?) | Marks a method as a keyword and registers it automatically | | @Input(name, opts?) | Defines an input parameter | | @Output(name, opts?) | Defines an output parameter |

Functions

| Function | Description | |----------|-------------| | loadConfig() | Load config from environment variables | | loadKeywords(folder) | Load keyword modules from one or more directories | | BeforeRun(fn) | Register a pre-execution hook | | AfterRun(fn) | Register a post-execution hook | | getAllKeywordSchemas() | Get all registered keyword metadata | | getKeywordSchema(fn) | Get metadata for a single keyword | | discoverKeywords() | List all registered keywords |

Exceptions

| Error | Description | |-------|-------------| | KeycaseError | Base error for all Keycase errors | | KeywordDefinitionError | Duplicate keyword name or incorrect definition | | ParameterValidationError | Parameter mismatch between plan and definition | | ExecutionError | Keyword execution failure | | AuthenticationError | Authentication failure | | ConnectionError | WebSocket connection failure | | LoginError | HTTP login/auth failure | | AuthTokenMissing | Missing authentication token |

Enums

| Enum | Values | |------|--------| | StatusEnum | PENDING, RUNNING, COMPLETED, FAILED, PASSED, SKIPPED, ABORTED, ERROR | | ConnectionState | DISCONNECTED, CONNECTING, CONNECTED, RECONNECTING, FAILED | | ParamType | STRING, NUMBER, INTEGER, BOOLEAN, OBJECT, ARRAY, ANY | | ParamDirection | INPUT, OUTPUT | | ExecutionPlanRunMode | AbortOnFailure, Skip, Default | | WebSocketEventType | All WebSocket event types (auth, execute, notify, etc.) |

WebSocket Protocol

The SDK communicates over WebSocket using JSON messages:

// Single event
{"event": "event_type", "payload": {...}}

// Batched events (flushed periodically)
{"events": [{"event": "...", "payload": {...}}, ...]}

Event Flow

  1. Agent connects to WS URL from auth response
  2. Sends agent_auth_request with access token
  3. Receives auth_success_response
  4. Receives agent_execute_plan_command to start execution
  5. Sends progress via agent_execution_progress_notify
  6. Sends results via HTTP POST to /projects/{id}/runs/{id}/results
  7. Sends agent_execution_completed_notify when done

Development

npm install
npm run build      # Build CJS + ESM
npm run test       # Run tests
npm run lint       # Lint code
npm run typecheck  # Type check

Architecture

src/
  index.ts              # Package exports
  agent.ts              # KeycaseAgent orchestrator
  auth.ts               # AuthService with token refresh
  classDecorators.ts    # @Keyword/@Input/@Output decorator API
  config.ts             # Environment config loading
  decorators.ts         # keyword/inputParam/outputParam (advanced use)
  eventHandler.ts       # WebSocket message routing
  executionContext.ts   # Run/project/step context tracking
  executionManager.ts   # Flow execution engine
  loader.ts             # Dynamic keyword module loading
  logger.ts             # Console logger
  registry.ts           # Keyword name→function registry
  stateTracker.ts       # Busy/available state management
  types.ts              # All TypeScript interfaces
  websocketClient.ts    # Robust WebSocket client
  exceptions/
    index.ts            # Custom error classes
  models/
    executePlan.ts      # Execution plan parsing
    executionResult.ts  # Execution result tracking
    executionRunModeTypes.ts
    websocketEventTypes.ts
  utils/
    authHelper.ts       # HTTP auth helpers
    eventSender.ts      # WebSocket event batching
examples/
  calculatorClass.ts        # Calculator keywords
  stringOperations.ts       # String operation keywords
  advancedExamplesClass.ts  # Advanced usage examples

License

MIT