@keycase/agent
v0.1.0
Published
TypeScript SDK for Keycase automation platform - keyword-driven test automation agent
Downloads
75
Maintainers
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/agentQuick 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
- Agent connects to WS URL from auth response
- Sends
agent_auth_requestwith access token - Receives
auth_success_response - Receives
agent_execute_plan_commandto start execution - Sends progress via
agent_execution_progress_notify - Sends results via HTTP POST to
/projects/{id}/runs/{id}/results - Sends
agent_execution_completed_notifywhen done
Development
npm install
npm run build # Build CJS + ESM
npm run test # Run tests
npm run lint # Lint code
npm run typecheck # Type checkArchitecture
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 examplesLicense
MIT
