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

kronos-sdk

v0.6.0

Published

TypeScript SDK for the Kronos automation protocol on TON — register recurring on-chain jobs, run automatons, decode events.

Readme

kronos-sdk

TypeScript SDK for the Kronos automation protocol on TON — Chainlink Automation for the TON ecosystem.

Register recurring on-chain jobs, decode events, maintain a local automaton mirror. Works in browsers, Node servers, and TON sandbox tests.

npm install kronos-sdk @ton/core
npx kronos-sdk init   # installs .claude/skills/ so Claude/Cursor know Kronos

@ton/core is a peer dependency — bring your own version (>= 0.63.0). @ton/sandbox and forgeton-sdk are optional peers — only needed when you use kronos-sdk/testing or run automaton flows.

💡 Running an automaton? Install forgeton-sdk alongside this one. Pool-side operations (stake, slash, pool events) live there. This SDK covers the Kronos registry only.

🤖 Using Claude Code / Cursor? Run npx kronos-sdk init once — it installs the Kronos skills (/kronos-register-job, /kronos-target-receiver, /kronos-scaffold, …) into your project's .claude/skills/. Every future AI session in this project knows how to use Kronos without needing prompt engineering.

Three layers, pick what you need

┌──────────────────────────────────────────────────────┐
│  KronosClient   registerJobOpts    decodeEvent       │  high-level
├──────────────────────────────────────────────────────┤
│  executionEconomics  assignedAutomatonIndex  isDue   │  pure helpers
├──────────────────────────────────────────────────────┤
│  KronosRegistry                                      │  contract wrapper (1:1 ABI)
└──────────────────────────────────────────────────────┘

You can drop down any layer — they're all part of the public surface.

Quickstart

Register a recurring job

import { TonClient, WalletContractV4, internal } from '@ton/ton';
import { mnemonicToPrivateKey } from '@ton/crypto';
import { Address, beginCell, toNano } from '@ton/core';

import { registerJobOpts, KronosClient, KronosRegistry } from 'kronos-sdk';

const REGISTRY_ADDRESS = Address.parse('EQ...');
const TARGET = Address.parse('EQ...');

const tonClient = new TonClient({ endpoint: 'https://testnet.toncenter.com/api/v2/jsonRPC' });
const registry = tonClient.open(KronosRegistry.createFromAddress(REGISTRY_ADDRESS));
const client = new KronosClient({ registry });

const cfg = await client.jobs.config();

const opts = registerJobOpts(
    {
        target: TARGET,
        message: beginCell().storeUint(0, 32).endCell(),
        interval: 3600,                                // once per hour
        reward: toNano('0.05'),                        // automaton reward
        gasLimit: toNano('0.02'),                      // forwarded gas
        maxExecutions: 0,                              // unlimited (default)
        windowBefore: 30,                              // tolerate 30s early
        windowAfter: 600,                              // and 10min late
    },
    cfg,                                               // auto-funds 100 runs
);

await client.jobs.register(walletSender, opts);

Decode registry events from your indexer

import { decodeEvent } from 'kronos-sdk';

// Each `Cell` is the body of an external-out message emitted by the registry.
// For pool-side events (AutomatonRegistered, AutomatonSlashed, …) use
// `decodeEvent` from forgeton-sdk on the pool's external bodies.
for (const body of externalBodies) {
    const ev = decodeEvent(body);
    if (ev === null) continue;  // not a Kronos event — try forgeton-sdk next
    switch (ev.kind) {
        case 'JobExecuted':
            console.log(`job ${ev.jobId} run ${ev.executionCount} by ${ev.automaton}`);
            break;
        case 'AssignedAutomatonMissed':
            console.warn(`automaton ${ev.assigned} missed window for job ${ev.jobId}`);
            break;
    }
}

Run an automaton (staking via forgeton-sdk)

Pool operations live in forgeton-sdk. Use both SDKs together:

import { KronosClient, KronosRegistry } from 'kronos-sdk';
import { ForgeTON } from 'forgeton-sdk';

const registry = tonClient.open(KronosRegistry.createFromAddress(REGISTRY_ADDRESS));
const pool = tonClient.open(ForgeTON.createFromAddress(FORGETON_ADDRESS));
const client = new KronosClient({ registry });

// Stake to become an automaton (pool-side; forgeton-sdk).
const poolCfg = await pool.getConfig();
const consumerCount = await pool.getConsumerCount();
await pool.sendRegisterAutomaton(walletSender, {
    value: poolCfg.minStake
         + poolCfg.minGasForRegister
         + BigInt(consumerCount) * poolCfg.syncGasCost,
});

// Find the next due job and execute it (registry-side; kronos-sdk).
const jobCount = await client.jobs.count();
for (let i = 0n; i < jobCount; i++) {
    if (!(await client.jobs.exists(i))) continue;
    if (!(await client.jobs.isDue(i))) continue;

    // Check if you're the assigned automaton for the primary window.
    const assigned = await client.assignedAutomatonFor(i);
    if (assigned && !assigned.equals(myAddress)) continue;

    await client.jobs.execute(walletSender, { value: toNano('0.5'), jobId: i });
}

Inspect a job's window state

import { jobWindowState, isDue, nextExecutionAt } from 'kronos-sdk';

const job = await client.jobs.get(jobId);
const cfg = await client.jobs.config();
const state = jobWindowState({
    lastExecutedAt: job!.lastExecutedAt,
    interval: job!.interval,
    windowBefore: job!.windowBefore,
    windowAfter: job!.windowAfter,
    primaryWindowSeconds: cfg.primaryWindowSeconds,
    expireAfter: job!.expireAfter,
});
// state.status: 'never-executed' | 'too-early' | 'primary' | 'fallback' | 'too-late' | 'expired'
// state.secondsToNext: countdown to next status change

Reference

KronosClient

High-level façade over the registry. Construct with an already-opened contract handle:

const client = new KronosClient({
    registry: tonClient.open(KronosRegistry.createFromAddress(REGISTRY_ADDRESS)),
});

| Namespace | Methods | |-----------|---------| | client.jobs | register, execute, fund, ensureFunded, cancel, pause, resume, withdraw, update, updateFull, cleanupExpired, sweepDust, performHousekeeping, get, exists, isDue, nextDue, balance, balanceHealth, economics, count, config | | client.mirror | snapshot, assignedFor, activeCount — registry-side reads against the dense automaton mirror maintained by inbound AutomatonSync | | client.events | decode, decodeAll |

Plus convenience methods on the client itself: assignedAutomatonFor(jobId), windowFor(jobId).

For pool-side operations (stake, unstake, slash, pool events) use forgeton-sdk's ForgeTON class directly.

registerJobOpts(input, cfg?)

Helper that fills in sensible defaults (30s before / 10min after window, no expiry, unlimited runs) and, if cfg is supplied, auto-computes funding for 100 runs.

registerJobOpts(
    {
        target,
        message,
        interval: 3600,
        reward: toNano('0.05'),
        gasLimit: toNano('0.02'),
        // Optional overrides — omit for defaults:
        maxExecutions: 0,      // unlimited
        windowBefore: 30,
        windowAfter: 600,
        expireAfter: 0,        // never
        funding: toNano('1'),  // or omit + pass cfg to auto-compute
    },
    cfg,
);

decodeEvent(body: Cell): KronosEvent | null

Parses an external-log message body into a typed registry event. The discriminant is event.kind. Returns null when the opcode is not a Kronos event (try forgeton-sdk's decodeEvent next).

All registry event kinds:

JobRegistered  JobExecuted    JobFunded       JobCancelled    JobUpdated
JobPaused      JobResumed     JobWithdrawn    JobExpired      JobDustSwept
JobHousekeepingExecuted  ForgetonSet  AutomatonMirrorUpdated   AssignedAutomatonMissed
ConfigUpdated  TreasuryUpdated  HousekeepingJobSet  FeesWithdrawn
UpgradeProposed  UpgradeCancelled  CodeUpdated  SlashRetried

Pool-side events (AutomatonRegistered, AutomatonSlashed, …) are decoded by forgeton-sdk's own decodeEvent. Chain both decoders if you're watching both contracts.

Pure helpers

| Function | Purpose | |----------|---------| | executionEconomics({ reward, gasLimit, protocolFeeBps }) | Returns { totalCost, protocolFee, automatonReward, gasLimit }. Mirrors on-chain JobConfig.executionEconomics. | | recommendedRegisterValue(...) | max(executions * totalCost, minFunding) + minGasReserve. The number you attach to RegisterJob. | | previewJobCost({ reward, gasLimit, interval, executions? }, cfg) | One-call UI-friendly preview — { perExecutionCost, minFunding, recommended, protocolFee, burnRatePerDay, runsAtRecommended, … }. | | estimateJobGas({ initCode, initData?, body }) or estimateJobGas({ blockchain, target, body }) | Sandbox-based gas estimator — { gasUsed, recommended } with 20% buffer. initCode mode deploys a fresh account; target mode needs the contract pre-deployed on the supplied blockchain. Requires @ton/sandbox (optional peer). | | assignedAutomatonIndex({ jobId, executionCount, activeAutomatonCount }) | The on-chain assignment formula (jobId + execCount) % activeCount. | | resolveAssignedAutomaton({ ..., automatonAddresses }) | Same, but resolves to an Address using a mirror snapshot. | | jobWindowState({ lastExecutedAt, interval, ... }) | Full window inspection: status, deadlines, secondsToNext. | | isDue(...), nextExecutionAt(...) | Convenience over jobWindowState — time/expiry only. | | isExecutable({ ..., balance, perExecutionCost, isActive }) | Faithful preview of Execute success — also checks active + funded. Use this in automaton daemons. | | validateRegisterOpts(opts, cfg) | Pre-flight foot-gun check: throws on contract-reject values (bad interval / reward / gasLimit / expireAfter), returns warnings[] for likely-buggy-but-legal inputs. Wired into registerJobOpts automatically when cfg is supplied — opt out with { validate: false }. |

JobPresets

Three spread-in profiles that collapse the windowBefore/windowAfter choice to a name:

import { registerJobOpts, JobPresets } from 'kronos-sdk';

// Every minute, strict window.
registerJobOpts({ ..., interval: 60, ...JobPresets.tight }, cfg);

// Hourly (current defaults).
registerJobOpts({ ..., interval: 3600, ...JobPresets.default }, cfg);

// Daily / weekly, tolerant of late runs.
registerJobOpts({ ..., interval: 86_400, ...JobPresets.loose }, cfg);

JobWatcher

Long-running subscription for a single job's events — polls the registry tx stream, filters by jobId, emits typed callbacks. Plug in any transaction source (TonClient, custom indexer, sandbox for tests):

import { JobWatcher } from 'kronos-sdk';

const watcher = new JobWatcher(client, jobId, { source: myEventSource });
watcher
    .on('JobExecuted', (ev) => console.log(`run ${ev.executionCount} by ${ev.automaton}`))
    .on('JobFunded',   (ev) => console.log(`topped up by ${ev.amount}`))
    .on('LowBalance',  (ev) => alertOps(ev.jobId, ev.runsRemaining));

const stop = watcher.start();
// ... later ...
await stop();

Test harness — kronos-sdk/testing

Shrinks "deploy registry + register job + fast-forward + execute" to a one-liner. Requires @ton/sandbox installed:

import { SandboxKronos } from 'kronos-sdk/testing';

const kronos = await SandboxKronos.deploy(blockchain, { owner, treasury, via: owner.getSender() });
const jobId = await kronos.register({ target, message, interval, reward, gasLimit, via });
kronos.fastForward(301);
const res = await kronos.executeNext(jobId, automaton.getSender());

See /kronos-integration-test for the full API + patterns.

Error introspection

import { explainError, formatErrorExplanation, ERR } from 'kronos-sdk';

const e = explainError(132);
// { code: 132, origin: 'kronos', name: 'NotJobOwner',
//   message: 'Operation requires the job owner.',
//   relatedSkill: '/kronos-job-lifecycle' }

// Single-line for logs or thrown Error messages.
throw new Error(formatErrorExplanation(e));
// → Error: [NotJobOwner] (132) Operation requires the job owner.  See /kronos-job-lifecycle

Covers every E_* from contracts/errors.tolk plus common TVM exit codes. Pool-side codes (160-199) route to origin: 'forgeton' with a pointer — install forgeton-sdk and call its explainError for the prose.

Bundled compiled contract

import { loadRegistryCode, KronosRegistry } from 'kronos-sdk';

const code = loadRegistryCode();
const registry = KronosRegistry.createFromConfig({ owner, treasury }, code);
// → registry.init.code (BoC) + registry.address (deterministic from init)

Ships with the compiled KronosRegistry.compiled.json under artifacts/. For the pool's BoC (ForgeTON.compiled.json), install forgeton-sdk and call its loadForgetonCode().

Examples

See examples/ for full programs:

  • 01-register-job.ts — register a recurring counter-bumper job.
  • 02-automaton-register.ts — run an automaton (uses both kronos-sdk + forgeton-sdk).
  • 03-decode-events.ts — minimal indexer that prints every registry event.

Working with AI tools

This package ships AI-friendly assets alongside the runtime:

  • AGENTS.md — terse map of the SDK auto-discovered by Claude Code, Cursor, and other AI editors that follow the agents.md convention. Drop the SDK into your project and your assistant has context.

  • skills/ — drop-in .claude/skills/*.md files for Claude Code:

    Job owner workflow

    • /kronos-register-job — guided job builder
    • /kronos-job-lifecycle — update / pause / withdraw / cancel
    • /kronos-monitor-events — indexer / alerting recipes

    Contract dev workflow (writing the Kronos target)

    • /kronos-target-receiver — idiomatic Tolk receiver pattern
    • /kronos-gas-sizing — empirically size gasLimit with a 20% buffer
    • /kronos-integration-testSandboxKronos harness patterns
    • /kronos-scaffold — one-shot "bolt Kronos onto my contract"

    Operator workflow

    • /kronos-automaton-setup — run an automaton (uses both SDKs)
    • /kronos-deploy — self-host deployment + housekeeping setup

    Debugging / admin

    • /kronos-debug-exit-code — exit-code triage
    • /kronos-owner-ops — admin operations + timelocked upgrades

    (Pool-side skills — stake an automaton, slash, admit a consumer — ship with forgeton-sdk.)

    Install all of them in one step:

    npx kronos-sdk init

    This also prints a CLAUDE.md fragment you can paste into your project's instructions so the AI has day-one context.

  • JSDoc @example blocks on every public method — your IDE's hover popups show runnable snippets.

Versioning

This SDK ships its own compiled BoC under artifacts/ (regenerated on each prepublishOnly). When the on-chain ABI changes, bump the SDK's major version. While we're pre-mainnet (v0.x), expect breaking changes alongside contract upgrades.

License

MIT.