An engineering-focused contribution implementing a WakaTime provider plugin for Corsair. The plugin enables secure retrieval of authenticated user details using HTTP Basic Authentication over API keys, featuring strict Zod runtime verification and rate-limiting retry safety.
This contribution integrates WakaTime code statistics tracking context into Corsair's modular provider-plugin architecture. By implementing a lightweight HTTP client and strict response constraints, the plugin establishes safe data-transfer boundaries between Corsair clients (such as AI agents) and the WakaTime REST backend.
Registers provider details in core constants, exposing WakaTime identifiers natively to Corsair's registry.
Secures API requests using Base64 HTTP Basic Authentication with client API keys, avoiding heavy OAuth configurations.
Detects 429 rate limit statuses and preserves transport retry delays, driving robust recovery inside Corsair.
Implements explicit Jest suites asserting HTTP request boundaries, schemas parsing, and error definitions.
This contribution directly resolves Corsair Issue #1088, which requested the addition of WakaTime support.
Prior to this integration, Corsair users and agent frameworks could not query developers' coding logs or profile metadata stored in WakaTime. By creating a dedicated provider plugin, this change adds WakaTime to the platform's extensible list of integrations, enabling seamless profile metadata retrieval through a standardized framework contract.
The WakaTime integration fits into Corsair's provider structure, isolating all API requests, validation logic, and authorization mappings inside a modular packages layer.
Dispatches tool calls to retrieve the authenticated user profile.
Resolves runtime context, credentials, and error configs.
Maps endpoint parameters and handles route execution.
Validates response structures at runtime using Zod.
GET /users/current verifies auth and returns payload.
Constructs requests with Base64 API Basic Auth.
Returns type-safe profile details structure to the calling Corsair agent application.
The WakaTime integration uses API-Key Authentication only via HTTP Basic Authentication. There is no OAuth flow, no webhooks, and no subscription structures required by this plugin, ensuring a lightweight and secure integration.
The API key is base64-encoded and transmitted inside the HTTP Authorization header of every outbound request, as implemented in the client wrapper:
const config: OpenAPIConfig = {
BASE: 'https://api.wakatime.com/api/v1',
VERSION: '1.0.0',
WITH_CREDENTIALS: false,
CREDENTIALS: 'omit',
HEADERS: {
'Content-Type': 'application/json',
'Authorization': `Basic ${Buffer.from(apiKey).toString('base64')}`,
},
};
The plugin registers one main profile retrieval action, mapping Corsair framework tools to the downstream WakaTime endpoint.
| Action Name | HTTP Method | Endpoint Path | Purpose |
|---|---|---|---|
| users.current | GET | /api/v1/users/current | Retrieves details of the currently authenticated WakaTime user profile. |
/** Fetches and validates the authenticated WakaTime user response. */
export const getCurrentUser: WakaTimeEndpoints['getCurrentUser'] = async (
ctx,
) => {
const response = await makeWakaTimeRequest<
WakaTimeEndpointOutputs['getCurrentUser']
>('users/current', ctx.key);
const validatedResponse =
WakaTimeEndpointOutputSchemas.getCurrentUser.parse(response);
await logEventFromContext(ctx, 'wakatime.users.current', {}, 'completed');
return validatedResponse;
};
To shield client systems from malformed data payloads or undocumented upstream shifts, all request parameters and response outputs are validated using Zod at the plugin boundary.
import { z } from 'zod';
const GetCurrentUserInputSchema = z.object({});
const GetCurrentUserDataSchema = z.object({
id: z.string(),
username: z.string().optional(),
display_name: z.string().optional(),
email: z.string().optional(),
});
const GetCurrentUserResponseSchema = z.object({
data: GetCurrentUserDataSchema,
});
Validation Constraints & Characteristics:
data object containing a string id.username, display_name, and email are marked optional).
The WakaTime client maps network exceptions to a custom WakaTimeAPIError type. Crucially, the error handler preserves the original status codes and HTTP metadata from Corsair's core ApiError.
By retaining fields like status === 429 and the retryAfter parameter, the plugin integrates seamlessly with Corsair's framework-level retry loops, enabling automated recovery when rate limits are hit.
export const errorHandlers = {
RATE_LIMIT_ERROR: {
match: (error: Error) => {
if (
(error instanceof ApiError || error instanceof WakaTimeAPIError) &&
error.status === 429
)
return true;
const msg = error.message.toLowerCase();
return msg.includes('rate_limited') || msg.includes('429');
},
handler: async (error: Error) => {
let retryAfterMs: number | undefined;
if (
(error instanceof ApiError || error instanceof WakaTimeAPIError) &&
error.retryAfter !== undefined
) {
retryAfterMs = error.retryAfter;
}
return { maxRetries: 5, headersRetryAfterMs: retryAfterMs };
},
},
AUTH_ERROR: {
match: (error: Error) => {
if (
(error instanceof ApiError || error instanceof WakaTimeAPIError) &&
error.status === 401
)
return true;
const msg = error.message.toLowerCase();
return msg.includes('unauthorized') || msg.includes('invalid_auth');
},
handler: async () => ({ maxRetries: 0 }),
},
} satisfies CorsairErrorHandler;
The directory footprint adheres strictly to Corsair's codebase convention for integrations, locating the plugin source files inside a dedicated packages directory.
packages/wakatime/
├── client.ts // HTTP requester, sets Base URL and Basic Auth headers
├── client.test.ts // Mocks requests, verifies client auth, query mapping, and errors
├── error-handlers.ts // Defines rate-limiting and auth match/recovery logic
├── error-handlers.test.ts // Tests RATE_LIMIT_ERROR checks and delay extraction
├── index.ts // Registers metadata, endpoints nested mapping, and options schema
├── schema.test.ts // Asserts semver schema conventions and Zod payload parsers
├── endpoints/
│ ├── index.ts // Exports users current operation entrypoint
│ ├── types.ts // Zod models representing inputs/outputs structures
│ ├── users.ts // Implements getCurrentUser caller logic
│ └── users.test.ts // Asserts users endpoints validation and completion events
├── schema/
│ └── index.ts // Exports empty WakaTimeSchema (data is non-persistent)
└── webhooks/ // Empty directory (plugin has no webhook dependencies)
WakaTimeAPIError mapping logic and uses the client request executor with WakaTime API credentials.WakaTimeSchema with entities: {}, asserting the plugin's non-persistent data design.BaseProviders and ProviderDisplayNames mappings.The integration is fully verified locally via Jest test suites, providing coverage for client requests, schema validation, endpoints, and error handlers.
Verifies request authorization encoding, query-string mapping, and ApiError translation.
Verifies rate-limit identification matches HTTP 429 status and parses header retry intervals.
Asserts proper SemVer formatting of the plugin version, empty entities structure, and response parse rules.
Validates response schema mapping on completion, context events emission, and invalid schema detection.
Several intentional engineering choices were made during implementation to guarantee alignment with Corsair's codebase architecture and maintainability guidelines:
Leveraged Corsair's existing modular plugin registration framework, avoiding custom transport layers or configuration boilerplate.
Chose a straightforward Basic Authentication header scheme instead of adding complex OAuth setups or token refresh states.
Enforced Zod validation at the boundary, ensuring API contract drift is immediately caught before downstream usage.
Kept the integration stateless by defining an empty entities block, as storing code session metrics is outside the plugin's scope.
Decided to forward original HTTP exception properties within WakaTimeAPIError, allowing Corsair to use native retry timing.
The pull request passed all repository validation checks and CI testing before being reviewed and merged by the repository maintainer.
Pull Request: #1097 — Feat/wakatime | Repository: corsairdev/corsair
"Basic Auth looks correct, tests match conventions. Happy to merge this, thanks Shebha M!"
PR #1097 was successfully merged into the main branch of corsairdev/corsair.
This interactive demo console simulates the WakaTime current-user request lifecycle, showing the Basic Auth setup and response validation against the Zod schema.
The merged integration provides substantial functional utility to the framework's capability set:
Building and merging this plugin highlighted several key lessons in engineering for complex agent platforms:
Shebha M's contribution successfully integrated WakaTime profile lookup capabilities into the Corsair platform via PR #1097, establishing type safety, rate-limit retry support, and comprehensive test coverage.
MERGED INTO MAIN