Skip to content

kitops-ml/kitops-ts

Repository files navigation

kitops-ts

npm version CI License

TypeScript/Node.js SDK for the KitOps CLI. Provides a type-safe functional API for packing, pushing, pulling, and inspecting ModelKits — without having to shell out manually.

Similar to pykitops but for TypeScript/JavaScript.

Requirements

  • Node.js >= 23
  • KitOps CLI installed and available in PATH (or set KITOPS_CLI_PATH to the full path of the binary)

Installation

npm install @kitops/kitops-ts
# or
pnpm add @kitops/kitops-ts

Quick start

import { login, pack, push } from '@kitops/kitops-ts';

await login('registry.example.com', process.env.REGISTRY_USER!, process.env.REGISTRY_PASS!);
await pack('.', { tag: 'registry.example.com/org/my-model:v1.0.0' });
await push('registry.example.com/org/my-model:v1.0.0');

API

Every function returns a CancellablePromise<T> — a standard Promise with an extra .cancel() method that kills the underlying kit process and rejects with a DOMException named 'AbortError'. Existing await code works without any changes; .cancel() is opt-in.

init(directory?, flags?)

Scans a directory for ML artifacts and generates a Kitfile. Automatically detects models, datasets, code, and docs by file extension.

const result = await init('./my-model', {
  name: 'my-model',
  desc: 'Sentiment analysis model',
  author: 'ML Team',
  force: true, // overwrite existing Kitfile
});

console.log(result.kitfilePath); // absolute path to the generated Kitfile

info(path, flags?)

Returns the parsed Kitfile for a ModelKit. The result also carries a non-enumerable _raw property with the original YAML string.

const kitfile = await info('registry.example.com/org/my-model:v1.0.0');
console.log(kitfile.package?.name);
console.log(kitfile.model?.path);

inspect(path, flags?)

Returns the full OCI manifest and parsed Kitfile. Use flags.remote to inspect directly from a registry without pulling first.

const result = await inspect('registry.example.com/org/my-model:v1.0.0', { remote: true });
console.log(result.digest);
console.log(result.manifest.layers);

pack(directory?, flags?)

Packages a directory containing a Kitfile into a ModelKit.

await pack('.', {
  tag: 'registry.example.com/org/my-model:v1.0.0',
  compression: 'zstd',
});

unpack(destination, flags?)

Extracts a ModelKit into a directory. Use flags.filter to pull out specific layers or paths.

// Extract everything
await unpack('./output');

// Model layer only
await unpack('./output', { filter: 'model' });

// A specific dataset and the full docs layer
await unpack('./output', { filter: 'datasets:validation,docs' });

// Don't fail if files already exist
await unpack('./output', { ignoreExisting: true });

list(repository?, flags?)

Lists ModelKits. Omit the argument to list local storage; pass a registry/repository to list remote tags.

const local = await list();
const remote = await list('registry.example.com/org/my-model');

for (const kit of local) {
  console.log(kit.repository, kit.tag, kit.size, kit.digest);
}

push(source, destination?, flags?)

Pushes a ModelKit to a registry. Supply destination to push under a different reference (e.g. promote from staging to production).

await push('registry.example.com/org/my-model:v1.0.0');

// Push to a different registry under a different tag
await push('staging.example.com/my-model:rc1', 'registry.example.com/org/my-model:v1.0.0');

pull(path, flags?)

Pulls a ModelKit from a registry into local storage.

await pull('registry.example.com/org/my-model:v1.0.0');

loginUnsafe(registry, username, password, flags?)

Authenticates with a registry. The password is passed as a CLI argument and will be visible in the process list — use login instead for production.

await loginUnsafe('registry.example.com', 'user', 'pass');

login(registry, username, password, flags?)

Same as loginUnsafe but passes the password via stdin, keeping it out of the process list. Preferred for CI and automated workflows.

await login(
  'registry.example.com',
  process.env.REGISTRY_USER!,
  process.env.REGISTRY_PASS!,
);

logout(registry)

await logout('registry.example.com');

tag(source, destination)

Assigns a new tag to an existing local ModelKit without re-packing. Use push afterward to publish it.

await tag('registry.example.com/org/my-model:rc1', 'registry.example.com/org/my-model:v1.0.0');

remove(path, flags?)

Removes a ModelKit from local storage or a remote registry.

// Remove a specific tag
await remove('registry.example.com/org/my-model:v0.9.0');

// Remove all locally cached ModelKits
await removeAll();

// Force-remove from the remote registry
await remove('registry.example.com/org/my-model:v0.9.0', { force: true, remote: true });

removeAll(flags?)

Removes all locally cached ModelKits.

await removeAll();

diff(reference1, reference2, flags?)

Compares two ModelKits and returns a structured diff of their layers.

const result = await diff(
  'registry.example.com/org/my-model:v1.0.0',
  'registry.example.com/org/my-model:v1.1.0',
);
console.log('Shared layers:', result.sharedLayers.length);
console.log('New layers:', result.uniqueToKit2);

version()

Returns version information for the installed kit binary.

const { version, commit, built, goVersion } = await version();
console.log(`kit ${version} (${commit})`);

kit(command, args, stdin?, options?)

Low-level escape hatch for running any kit subcommand directly, useful when the higher-level wrappers don't expose a flag you need.

const result = await kit('pack', ['.', '--tag', 'my-model:latest'], undefined, { cwd: '/path/to/project' });
console.log(result.stdout);

Cancellation

Every function returns a CancellablePromise. Call .cancel() at any time to kill the underlying kit process. The promise rejects with a DOMException named 'AbortError'.

import { push } from '@kitops/kitops-ts';

const op = push('registry.example.com/org/my-model:v1.0.0');

// Cancel after 30 seconds
const timeout = setTimeout(() => op.cancel(), 30_000);

try {
  await op;
} catch (err) {
  if (err instanceof DOMException && err.name === 'AbortError') {
    console.log('Push was cancelled');
  } else {
    throw err;
  }
} finally {
  clearTimeout(timeout);
}

Since CancellablePromise extends Promise, all existing await usage continues to work without any changes.

Error handling

Functions can reject in two ways:

  • CLI failure — rejects with a string message that includes the exit code and stderr output from the CLI.
  • Cancellation — rejects with a DOMException named 'AbortError' when .cancel() is called on a CancellablePromise.
try {
  await push('registry.example.com/org/my-model:v1.0.0');
} catch (err) {
  if (err instanceof DOMException && err.name === 'AbortError') {
    console.log('Push was cancelled');
  } else {
    console.error(err); // "Kit command failed with exit code 1: ..."
    process.exit(1);
  }
}

Environment variables

Variable Description
KITOPS_CLI_PATH Full path to the kit binary. Defaults to kit (resolved via PATH).

Examples

See the examples/ directory for runnable scripts covering common workflows:

Development

pnpm install
pnpm build       # compile TypeScript
pnpm dev         # watch mode
pnpm test        # run tests
pnpm typecheck   # type-check without emitting

Related

About

Typescript library for KitOps

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors