Plugin system

Plugins are shared libraries (.dll on Windows, .so on Linux/macOS) that extend KonsolScript with new module-style classes callable with the same MyPlugin:Method(args) syntax as built-in modules. No recompile of minks is needed.


Loading plugins from a script

The recommended way — load plugins directly in the script that uses them:

#include "kse_mysql"     // finds kse_mysql.dll/.so in the plugin search path
#include "kse_audio"

See Var — Plugin includes for the full search-path order.


Managing installed plugins

minks install <name>          install plugin from current directory
minks remove  <name>          delete it from the user plugin dir
minks list                    list all installed plugins

minks install searches the current working directory for <name>.dll / .so / .dylib, then lib<name>.dll / .so / .dylib. Run it from inside the directory where you built the plugin.

Installed plugins live in ~/.minks/plugins/ (Linux/macOS) or %APPDATA%\minks\plugins\ (Windows) and are available to all scripts without any flags.

cd sample_plugin
minks install kse_sample
minks list
# kse_sample

Adding a search directory (CLI)

minks --plugin-path ./my_libs script.ks

--plugin-path can be repeated. Added dirs are checked before the defaults.


Low-level explicit load (legacy / debug)

minks --plugin ./myplugin.dll script.ks
minks --plugin lib1.dll --plugin lib2.dll script.ks
minks -p ./kse_mysql.so -p ./kse_audio.so game.ks

--plugin (or -p) can be repeated. Libraries are loaded in the order given before the script runs. Prefer #include "name" in scripts over this flag for portability.


Writing a plugin — the PluginClass builder

Include minks_plugin.h (ships with minks). The PluginClass RAII fluent builder handles all token-stream parsing internally; you only write lambdas for each method.

#include "minks_plugin.h"
#include <iostream>

extern "C" void minks_register(Engine& e) {
    PluginClass(e, "MyLib")

        // .byRefMethod — last script arg is the outVar; SDK writes result there.
        // args[] inside the lambda contains only the INPUT arguments.
        .byRefMethod("Greet", [](auto args) -> Value {
            return Value(std::string("Hello, ") + toString(args[0]) + "!");
        })

        // .voidMethod — side-effects only; no receiver variable needed
        .voidMethod("Log", [](auto args) {
            std::cout << toString(args[0]) << "\n";
        })

        .byRefMethod("Add", [](auto args) -> Value {
            return Value(toDouble(args[0]) + toDouble(args[1]));
        });

    // PluginClass destructor fires here, registering "MyLib" with the engine.
}

From KonsolScript:

Var:String greeting;
MyLib:Greet("world", greeting);
Konsol:Print(greeting);         // Hello, world!

MyLib:Log("debug info");        // void — no receiver needed

Var:Number sum;
MyLib:Add(3, 7, sum);
Konsol:Print(sum);              // 10

Builder API

Builder call Output convention Description
.byRefMethod("Name", fn) Last script arg is the outVar fn receives input args; its return Value is written to the caller's pre-declared variable
.voidMethod("Name", fn) None fn returns nothing; no variable is written

Plugins have no _ret path. Every non-void plugin method must use .byRefMethod.

Argument extraction

Inside your lambda, args is std::vector<Value>. Use these free helpers (from kse_types.hpp):

Helper Type extracted
toDouble(args[N]) Number (double)
toString(args[N]) String
toBool(args[N]) Boolean

Return values: Value(double), Value(std::string), Value(bool).


Build commands

# Windows
g++ -std=c++17 -shared -I<minks_dir> -o myplugin.dll myplugin.cpp

# Linux / macOS
g++ -std=c++17 -shared -fPIC -I<minks_dir> -o myplugin.so myplugin.cpp

Link additional libraries after myplugin.cpp:

g++ -std=c++17 -shared -fPIC -I../minks -o kse_mysql.so kse_mysql.cpp -lmariadb

<minks_dir> is the directory containing minks_plugin.h, kse.hpp, and kse_types.hpp.


Working example — sample plugin

sample_plugin/kse_sample.cpp exposes a Sample class with six byRefMethod entries and one voidMethod. Build, install, and use:

# from sample_plugin/
make
minks install kse_sample   # searches cwd for kse_sample.dll/.so

# In your .ks script:
#include "kse_sample"

Or for a quick test without installing (explicit load by path):

minks --plugin sample_plugin\kse_sample.dll tests\20_plugin.ks   # Windows
minks --plugin ./sample_plugin/kse_sample.so tests/20_plugin.ks  # Linux

Examples

Plugin usage

// 20_plugin.ks — Demo script for the sample plugin
//
// Install once, then run:
//   minks install kse_sample        (from inside sample_plugin/)
//   minks tests/20_plugin.ks
//
// Or load explicitly without installing:
//   minks --plugin sample_plugin\kse_sample.dll tests\20_plugin.ks   (Windows)
//   minks --plugin ./sample_plugin/kse_sample.so tests/20_plugin.ks  (Linux)

#include "kse_sample"

Konsol:Print("=== Sample Plugin ===");

// Version check
Var:String version;
Sample:Version(version);
Konsol:Print("plugin version: ${version}");

// Hello (void — prints directly, no receiver needed)
Sample:Hello("world");
Sample:Hello("MINKS");

// Arithmetic
Var:Number result;
Sample:Add(3, 4, result);
Konsol:Print("Add(3, 4)      = ${result}");

Sample:Multiply(6, 7, result);
Konsol:Print("Multiply(6, 7) = ${result}");

// Use result in expression
Sample:Add(10, 20, result);
Var:Number sum = result;
Sample:Multiply(sum, 2, result);
Konsol:Print("(10+20)*2      = ${result}");

// String ops
Var:String s;
Sample:Upper("hello from plugin", s);
Konsol:Print("Upper          = ${s}");

Sample:Repeat("ab", 5, s);
Konsol:Print("Repeat(ab, 5)  = ${s}");

// IsPrime
Konsol:Print("--- IsPrime ---");
Var:Number count = 0;
Var:Boolean isPrime;
for (Var:Number n = 2; n <= 30; n++) {
    Sample:IsPrime(n, isPrime);
    if (isPrime) {
        Konsol:Print("${n} is prime");
        count++;
    }
}
Konsol:Print("primes found: ${count}");

// Combine with built-ins
Var:String upper;
Sample:Upper("minks plugin system", upper);
Var:Number len;
String:Len(upper, len);
Konsol:Print("length of uppercased string: ${len}");

Plugin include

#include "kse_sample"

Var:String v;
Sample:Version(v);
Konsol:Print(v);

Var:Number n;
Sample:Add(10, 20, n);
Konsol:Print(n);

Var:String u;
Sample:Upper("hello from include", u);
Konsol:Print(u);


Plugin typed exceptions

Plugins can declare named exception classes that scripts catch by type — the same try / catch / finally syntax used for built-in exceptions.

A plugin registers its exception type once in minks_register:

registerPluginException(e, "MySQLException");

Then throws it from any method:

throwPluginException("MySQLException", mysql_error(conn), (double)mysql_errno(conn));

The caught instance exposes two fields (see Object — Exception):

Field Type Description
e.message String Human-readable error description
e.code Number Numeric error code (0 if unused)

catch (Exception e) is the generic catch-all. More specific types (MySQLException, CurlException, etc.) must be named exactly.


Plugin manifest (optional — enables minks install auto-setup)

Ship a kse_<name>.json file alongside your .dll / .so. When a user runs minks install <name>, minks reads the manifest and auto-runs the right package manager to install your native library dependency before copying your plugin to the user plugin directory.

{
  "name":        "myplugin",
  "version":     "1.0.0",
  "description": "One-line description of what the plugin does",
  "maintainer":  "Your Name <you@example.com>",
  "license":     "MIT",
  "library":     "kse_myplugin",
  "apt":         "libmything-dev",
  "dnf":         "libmything-devel",
  "brew":        "mything",
  "msys2":       "$MINGW_PACKAGE_PREFIX-mything",
  "windows":     "Download from https://example.com/mything"
}
Field Required Description
name yes Short plugin name (what users type in minks install <name>)
version yes Semantic version string
description yes One-line description
maintainer yes Author contact in "Name <email>" format
license yes SPDX license identifier (e.g. "MIT", "Apache-2.0")
library yes Base name of the plugin file without extension (e.g. "kse_myplugin")
apt no Debian/Ubuntu package name for apt install
dnf no Fedora/RHEL package name for dnf install
brew no Homebrew formula name for brew install
msys2 no MSYS2 package name for pacman -S; $MINGW_PACKAGE_PREFIX is resolved from the environment
windows no Hint string printed to the user on native Windows (download URL or instructions)

Omit install fields (apt, dnf, brew, msys2, windows) for plugins that have no external library dependency. If no manifest is present, minks install copies the plugin file only, without running any package manager.


IDE false-positive on minks_plugin.h

Editors using clangd may flag 'kse.hpp' file not found or 'minks_plugin.h' file not found inside sample_app/ or sample_plugin/ source files. This is a false positive — clangd does not read those directories' Makefile and therefore does not pick up the -I.. flag. The make build compiles correctly. Add a compile_commands.json or a .clangd config with CompileFlags: Add: [-I..] to suppress it.


Available plugins

Plugin Description
curl HTTP client — GET, POST, PUT, DELETE
sqlite SQLite database (no external dependency)
mysql MySQL / MariaDB database
pg PostgreSQL database
redis Redis client — strings, hashes, lists, sets
zip Zip archive read/write
crypto AES-256, SHA-512, HMAC-SHA256, PBKDF2, CSPRNG
ws WebSocket client (non-blocking)
jwt JWT sign, verify, decode (HS256)
net LAN TCP networking