Skip to content
KeelOps
Contents
  1. 00 The KeelOps plugin SDK
  2. 01 Getting started
  3. 02 The manifest
  4. 03 The context and create()
  5. 04 Data: reading and owning tables
  6. 05 Pages in the application
  7. 06 Local AI: reports and release notes
  8. 07 Working with the core
  9. 08 Security and rules
  10. 09 Testing and shipping
  11. 10 SDK reference

SDK reference

Every module of keelops-sdk/, every export. The modules are plain ES modules with no dependencies; load them from ctx.sdkDir:

const sdk = (file) => import(pathToFileURL(join(ctx.sdkDir, file)).href);
const { json, readJson } = await sdk("http.mjs");

database.mjs

The driver (ctx.db)

memberreturnsnotes
dialect"sqlite" | "mariadb"
nickstring | nullthe owner of the writable tables
all(sql, ...params)Promise<object[]>every row; reads only
get(sql, ...params)Promise<object | undefined>the first row
run(sql, ...params)Promise<{ changes }>one write, on plugin_<nick>_… only
close()Promise<void>a borrowed connection is not closed
sqlobjectthe dialect helpers below

Dialect helpers (db.sql)

helperreturns SQL for
now()the current instant, UTC
today()today, YYYY-MM-DD
dayOf(expr)the day of a timestamp, YYYY-MM-DD
monthOf(expr)the month of a timestamp, YYYY-MM
daysBetween(a, b)days from a to b (fractional)
todayPlusDays(n)the date n days from today; n must be an integer
concat(...parts)string concatenation
ident(name)a quoted identifier
upsert(key, columns)the insert-or-update clause after INSERT … VALUES (…)
createTableSuffix()what follows CREATE TABLE (…) (charset on MariaDB)
longText()a text type with no practical bound

Functions

functionreturnswhat
applyMigrations(db, nick, steps, from = 0)Promise<number>runs steps ([{ version, up: async (db) => … }]) above from, in order, writing schema_version after each; returns the version reached
pluginConfig(db, nick)configthe key/value table plugin_<nick>_config: table, ensure(), get(name) → string | null, set(name, value), all() → { name: value }
prepareOwnedTables(db, { nick, schemaVersion, migrate, version })Promise<number>what the core does before mounting; for the standalone mode and tests
openWritableSqlite(path, nick)drivera writable SQLite driver with the same gate; for a copy of the database
borrowedDriver({ dialect, query, execute?, close?, nick? })drivera driver over someone else's connection (the core uses it)
readOnlyQuery(path)(sql, params) => rowsa read-only query function over a SQLite file
openDatabase(target)drivera read-only SQLite driver from a path or sqlite: URL

http.mjs

functionwhat
json(res, status, body, headers?)answers JSON
html(res, status, body, headers?)answers HTML
readBody(req, limit = 1_000_000)Promise<string>: the body, refused above limit bytes
readJson(req)Promise<object>: the body parsed, {} when empty
createDispatcher({ routes, staticDir?, name })(req, res, pathWithQuery) => Promise: the one routing logic, used by the core and by the standalone mode
startServer({ port, routes, staticDir?, name })a standalone HTTP server on the given port, listening on the loopback interface only

A route is [method, regexp, handler(req, res, match, url)]. No match: the static folder, then 404 { errore: "non trovato" }. An exception: 500 { errore: "errore interno" } and one log line.

ui.mjs

exportwhat
BASE_CSSthe application's look: tokens (--carta, --inchiostro, --tenue, --filo, --rilievo, --accento, --accento-fondo, --pericolo, --raggio, --font), imported from /api/tema.css with fallbacks
CARD_CSSa centred single-card page
ICONSinline SVG glyphs: copia, conferma, nega, revoca, indietro, entra, grip, comprimi, esterno
TEMA_SCRIPTthe theme follower, as text — served to every plugin as sdk/tema.js
FRAME_RESIZE_SCRIPTthe frame height reporter, as text — served as sdk/riquadro.js
TRADUZIONI_SCRIPTthe translation mechanism, as text — served as sdk/traduzioni.js
avviaRiquadro()the height reporter as a function, for bundled pages that cannot load scripts
avviaTraduzioni()the translation mechanism as a function
FRAME_EMPTY_MESSAGE"keelops:vuoto": the message a panel sends when it has nothing to show

In the browser: sdk/traduzioni.js

globalwhat
KeelOpsI18n.crea(catalogues, { titolo? })installs the translations; catalogues is { en, fr, de, es }, Italian is the key; returns i18n
i18n.imposta(locale)sets the language (the person's choice in KeelOps) and re-translates the page
i18n.lingua()the current language
i18n.applica()re-translates data-i18n* elements (after adding some)
t(key, values?)a translated phrase; {name} replaced from values

Messages between a frame and the host

messagefrommeaning
{ tipo: "keelops:altezza", altezza }the pageits height (sent by sdk/riquadro.js)
{ tipo: "keelops:vuoto", vuoto }the pageit has nothing to show (true) or has again (false)

ollama.mjs

exportreturnswhat
embed({ url, input, model = "bge-m3", batch = 64, timeoutMs = 120000, normalized = true, onProgress? })Promise<number[][] | null>vectors for an array of strings, in batches
chat({ url, model, system?, user, schema?, think = false, numPredict = 512, numCtx?, keepAlive?, timeoutMs = 60000 })Promise<string | null>one answer at temperature zero; schema is a JSON Schema for the answer
normalize(vector)number[]L2 normalisation
dot(a, b)numberdot product (the cosine, for normalised vectors)
EMBEDDING_MODEL"bge-m3"the default embedding model

Every failure is null, never an exception.

extension.mjs

openExtensionStore(dataDir, { fileName = "extension.duckdb", from }) → Promise<store> — a DuckDB file of the plugin's own. from: import.meta.url tells the SDK where the plugin's node_modules are (npm i @duckdb/node-api in the plugin folder). fileName: ":memory:" for tests.

memberwhat
dialect"duckdb"
exec(sql)DDL, statements without parameters
all(sql, ...params), get(sql, ...params)reads
run(sql, ...params){ changes }
migrate(steps, from?)versioned schema, the version kept in keelops_schema; returns the version reached
version()the schema version in the file (0 = none)
close()

perimeter.mjs

functionreturnswhat
taskPerimeter(user){ where, params }an AND filter over Task t: what a person may see, conservatively
projectPerimeter(user){ where, params }a filter over Project p: the projects a person is in

text.mjs

functionwhat
stripHtml(text)rich text (HTML) to plain text
escapeHtml(text)escapes & < > " ' for HTML

session.mjs

sessionUser(req, db, cookieName) → Promise<User | null>: the KeelOps session behind a request, for the standalone mode only. Inside the core use ctx.sessionUser.

sql-targets.mjs

functionreturnswhat
writeTargets(sql){ tables, indexes, statements }the tables and indexes a statement writes; throws on views, triggers and what it cannot classify
assertWritesOnlyOwnTables(sql, nick){ tables, indexes }the gate: throws unless every written name carries plugin_<nick>_ and the text holds one statement
stripCommentsAndStrings(sql)stringthe statement without comments and string literals

Italian names in the contract

nameEnglishwhere
nomenamemanifest; tool
titolotitlemanifest; bar state
versioneversionmanifest
descrizionedescriptionmanifest; tool
sommariosummarymanifest
licenza (libero, commerciale)licence (free, commercial)manifest
permessipermissionsmanifest
montaComemount asmanifest
pubblicipublic pathsmanifest
vocemenu labelui
iconaiconui, ui.barra
sezione (aree, amministrazione)section (areas, administration)ui
dopoafterui
ruolirolesui
soloManager, soloGruppomanagers only, group onlyui
barra (stato, pannello)bar button (state, panel)ui
tono (acceso, spento, bloccato, nessuno), lampeggiatone (on, off, locked, none), blinkbar state
strumenti (parametri, esegui)tools (parameters, run)create(), ctx.plugins
segnali.inviasignals.sendctx
timesheet.aggiungi, timesheet.meseChiuso, ore, notatimesheet.add, month closed, hours, notectx
mail.invia, mail.attiva, inviata, motivomail.send, mail active, sent, reasonctx
riepilogoMattutino (oggi, righe)morning digest (today, lines)create()
chiave, originekey, origingroups
saltato, sostituitoskipped, replacedattachments
imposta, lingua, applica, creaset, language, apply, createtranslations
ancora, progetto, offerta, allegato, gruppoanchor, project, deal, attachment, groupquery strings