@ebruni/kronosjs
v0.1.4
Published
Manage, monitor, and control scheduled cron jobs with terminal integration and a simple REST API
Readme
kronosjs - Cron Job Manager for Node.js
Manage, monitor, and control scheduled cron jobs with TypeScript support, hot-reloading, terminal integration and a REST API
✨ Features
General
- 🧱 Built on cron: Uses the battle-tested cron package
- 📁 Directory-based Jobs: Load jobs automatically from a directory
- 🔄 Hot Reload: Auto-reload job definitions when files change
- 📝 Crontab Support: Persist job schedules in a crontab file
- 🖥️ REST API: Built-in Fastify HTTP server for job management
- 🛠️ CLI/Terminal UI: Manual runs and live status monitoring
- 🪵 Structured Logging: Powered by Pino with per-job context
- ⚡ TypeScript-first: Full TypeScript support with comprehensive types
- 🎯 Timezone Support: Schedule jobs in any timezone
🖥️ Web UI
- Dashboard: Status, next run, last run, duration, failures
- Job Detail: Cron expression, timezone, recent runs, logs
- Actions: Create, start, stop, delete, enable/disable
- Logs: Stream and filter by time, text, status
🔄 Hot Reloading & 🧩 Dynamic Jobs
- Load/unload job definitions at runtime without restarting the server.
- Watch your job definition files and automatically apply changes.
- Trigger on-demand runs via API or CLI while the scheduler is active.
⌨️ CLI & Terminal Integration
Open the interactive terminal UI by running your project’s CLI entrypoint. Then use:
h: List all available commandsl: List all jobs with their statusq: Quit the programr #num: Run the job in the list with the specified number key
Great for quick manual runs, smoke tests, and monitoring during development.
💡 Use Cases
- Operational task scheduling and visibility
- Admin-friendly controls for background workers
- CI-triggered job runs and monitoring
- Rapid iteration with hot-reloaded job definitions
🔐 Security
- API key or JWT auth for UI and API (recommended in production)
- CORS and rate limiting toggles
📦 Installation
npm install @ebruni/kronosjs
# or
pnpm add @ebruni/kronosjs
# or
yarn add @ebruni/kronosjs🚀 Quick Start
- Install package and register your handlers
- Launch the server and open the UI at
/(API under/api) - Use the CLI to run jobs and monitor status from the terminal
- Configure storage and auth via ENV or config file
Basic Example
import Kronos from '@ebruni/kronosjs';
const cm = await Kronos.create({
logger: true,
name: 'My Cron Manager'
});
cm.add({
name: 'hello_world',
cronTime: '* * * * * *', // every second
onTick: function () {
if (this.log) this.log.info('Hello, World!');
},
start: true
});Load Jobs from Directory
Create a job file jobs/hello_world.job.ts:
import type { KCronConfig, KJob } from '@ebruni/kronosjs';
export default function run(this: KJob) {
this.log?.info('Hello, World!');
}
export const config: KCronConfig = {
name: 'hello_world_job',
timezone: 'UTC',
schedule: '* * * * * *', // every second
start: true,
waitForCompletion: true
};Then load it:
import Kronos from '@ebruni/kronosjs';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const cm = await Kronos.create({
logger: true,
jobsDir: { base: `${__dirname}/jobs` }
});
cm.start();With HTTP Server
import Kronos from '@ebruni/kronosjs';
const cm = await Kronos.create({
logger: true,
name: 'My Cron Manager',
httpServer: { port: 3000, host: '0.0.0.0' }
});
cm.add({
name: 'hello_world',
cronTime: '*/2 * * * * *', // every 2 seconds
onTick: function () {
if (this.log) this.log.info('Hello, World!');
},
start: true
});
// Server automatically started on http://0.0.0.0:3000Custom Logger Output
import Kronos from '@ebruni/kronosjs';
const cm = await Kronos.create({
logger: {
stream: {
write: msg => {
const msgObj = JSON.parse(msg);
const msgString = msgObj.jobId
? `[${msgObj.jobId}] ${msgObj.msg}`
: msgObj.msg;
process.stdout.write(msgString + '\n');
}
}
},
name: 'Custom Logger Example'
});Complete configuration
const devMode = process.env.NODE_ENV !== 'production';
const cm = await Kronos.create({
// enable logger with different level based on environment
logger: { level: devMode ? 'debug' : 'info' },
// specify jobs directory
jobsDir: { base: `${__dirname}/jobs` },
// enable cron tab and set path in production
cronTabPath: devMode ? undefined : `${__dirname}/crontab`,
// enable terminal integration
terminal: true,
// enable HTTP for API access (default port 3000 binding to 0.0.0.0)
httpServer: true
});📖 API Reference
Kronos.create(config: KConfig): Promise<Kronos>
Creates a new Kronos instance with the given configuration.
Configuration Options
interface KConfig {
// Optional crontab file path for persistence
cronTabPath?: string;
// Directory to load job files from
jobsDir?: {
base: string;
writeable?: boolean; // if false, enables hot-reloading
};
// Instance name
name?: string;
// Logger configuration
logger?: boolean | KLogOptions;
// Custom logger instance
loggerInstance?: KLog;
// HTTP server configuration
httpServer?: {
port: number;
host?: string; // defaults to '0.0.0.0'
};
}Instance Methods
add(params: KNamedParams | KJob | CronJob): KJob
Add a new cron job.
const job = cm.add({
name: 'my-job',
cronTime: '0 */5 * * * *', // every 5 minutes
timezone: 'America/New_York',
onTick: function () {
// Job logic here
if (this.log) this.log.info('Running job');
},
start: true
});job(nameOrIndex: string | number): KJob | undefined
Get a job by name or index.
const job = cm.job('my-job');
const firstJob = cm.job(0);remove(nameOrJob: string | KJob, deleteFromCrontab?: boolean): Promise<void>
Remove a job.
await cm.remove('my-job', true);start(): void
Start all jobs.
cm.start();stop(): void
Stop all jobs.
cm.stop();count(): number
Get the number of registered jobs.
const total = cm.count();close(): Promise<void>
Stop all jobs and close the Kronos instance (including HTTP server if enabled).
await cm.close();reloadAll(): Promise<void>
Reload all jobs from the directory and crontab.
await cm.reloadAll();Job Object (KJob)
Each job has the following properties and methods:
interface KJob extends CronJob {
name: string;
log?: KLog; // Pino logger instance
isActive: boolean;
isCallbackRunning: boolean;
cronTime: CronTime;
lastDate: Date | null;
nextDate: Date | null;
start(): void;
stop(): void;
}🔄 Hot Reloading
When you set jobsDir.writeable: false (or omit it), Kronos watches the directory for changes:
const cm = await Kronos.create({
logger: true,
jobsDir: {
base: './jobs',
writeable: false // enables hot-reloading
}
});Any changes to .ts or .js files in the jobs directory will automatically reload all jobs.
📝 Crontab Persistence
Save job schedules to a crontab file:
const cm = await Kronos.create({
cronTabPath: './my-crontab.txt',
jobsDir: { base: './jobs' }
});The crontab file format:
* * * * * * job_name_1
0 */5 * * * * job_name_2Changes to the crontab file are automatically detected and applied. Add files to jobDir, change configuration via API or via terminal are applied to crontab file.
📊 Logging
Kronos uses Pino for structured logging. Each job gets its own logger context with the jobId field.
function run(this: KJob) {
this.log?.info('Starting task');
this.log?.debug('Debug information');
this.log?.error('An error occurred');
}🌐 HTTP Server
The built-in HTTP server uses Fastify and can be customized:
const cm = await Kronos.create({
httpServer: { port: 3000, host: 'localhost' }
});
const fastify = cm.httpServer?.getFastifyInstance();
if (fastify) {
// Add custom routes
fastify.get('/custom', async () => {
return { message: 'Custom endpoint' };
});
}🔌 REST API Endpoints
When you enable the HTTP server, the following endpoints are available:
GET /api/jobs— List all jobsPOST /api/jobs— Create a jobGET /api/jobs/:id— Get job detailsPOST /api/jobs/:id/start— Start a jobPOST /api/jobs/:id/stop— Stop a jobDELETE /api/jobs/:id— Delete a jobGET /api/jobs/:id/logs?status=&q=&from=&to=— Paginated logs
Health Check
GET /health— Server health status{ "status": "ok" }
Job Management
GET /api/jobs— List all jobs{ "total": 1, "items": ["hello_world"] }GET /api/jobs/:jobName— Get job details{ "name": "hello_world", "cronTime": "*/2 * * * * *", "isActive": true, "isRunning": false, "lastDate": "2025-01-15T10:30:00.000Z", "nextDate": "2025-01-15T10:30:02.000Z" }POST /api/jobs/:jobName/start— Start a job{ "result": true, "status": "Job started" }POST /api/jobs/:jobName/stop— Stop a job{ "result": true, "status": "Job stopped" }DELETE /api/jobs/:jobName— Delete a job{ "result": true, "status": "Job removed" }
📚 Examples
Check the examples directory for more use cases:
hello_world.ts- Basic cron jobhello_world_console_log.ts- Custom logger outputhello_world_from_folder.ts- Load jobs from directoryhello_world_http.ts- HTTP server integration
🧪 Testing
# Run tests
pnpm test
# Run with coverage
pnpm test:coverage
# Lint code
pnpm lint
# Build
pnpm build🔗 Links
- Report Bugs: GitHub Issues
- Feature Requests: GitHub Issues
- Repository: GitHub
Contributing
We welcome contributions!
License
Copyright 2024-2025 | Emiliano Bruni
