@x12i/runx
v1.3.0
Published
Runx — Catalox-backed dynamic capability library (storage, cache, run, artifacts, MCP, condition evaluation).
Maintainers
Readme
@x12i/runx
Runx is a Catalox-backed dynamic capability library: it stores capability records in Catalox, loads them into an in-memory cache, executes active runtime capabilities, and returns code, artifacts, and bundles on demand.
Use Runx when you want a durable, versioned catalog of reusable capabilities (functions, adapters, UI deliverables, scripts, compositions) that both humans and AI agents can discover, run, and export—not a pile of one-off prompts or ad hoc scripts.
What Runx is good for
| Goal | How Runx helps |
|------|----------------|
| Shared capability catalog | One source of truth in Catalox; reload into cache when the catalog changes. |
| Runtime execution | Call runx.run(capabilityId, input, params) for active JavaScript capabilities (v1). |
| Transparent artifacts | Fetch source, tests, docs, and dependency manifests via getCapabilityArtifact / getCapabilityBundle—Runx does not hide generated code. |
| AI / agent integration | Expose the stored capability catalog over MCP (@x12i/runx/mcp, CLI runx-mcp) so clients discover and call tools dynamically. |
| Reuse before regeneration | Capabilities can compose, wrap, or parameterize others; metadata tracks reuse and dependencies. |
| Runtime condition checks | Evaluate whether content meets a JavaScript or JSON rule via checkContentCondition / runx.condition.checkContentCondition (Rendrix templates, filter predicates, JSONLogic, JSONata, optional model). |
| Ops / UI deliverables | Script and UI capabilities are artifact-first (runbooks, React/HTML panels)—export and run outside Runx when needed. |
How the pieces fit together
Runx is the execution and artifact layer, not the authoring brain:
Authoring (LLM orchestration) Storage (source of truth) Run + expose
───────────────────────────── ───────────────────────── ────────────
FuncX runx.* authoring sets → Catalox catalogs → Runx SDK
(client.funcx / runSkill) (capabilities, packages) run / artifacts / MCP- FuncX (
@x12i/funcx) — generates and reviews capabilities via prerequisite sets such asrunx.generateJavascriptCapability,runx.api.*,runx.ui.*,runx.script.*. Wire it withfuncxorfuncxCreateOptionsoncreateRunx; useclient.funcxwith@x12i/funcx/functions(e.g.runSkill) for authoring flows. - Catalox — persists capability documents and runtime package allowlists.
- Runx — loads the catalog, runs supported kinds, exports bundles, and (optionally) serves MCP tools/resources from what is actually stored—not a hardcoded tool list.
Important: Runx MCP exposes the Runx capability catalog only. It does not mirror the full FuncX function catalog; that would be a separate FuncX MCP server (see docs/mcp.md).
Capability kinds
| Kind | Typical use | Runtime in v1 core |
|------|-------------|---------------------|
| javascript | Transforms, scoring, helpers | Executed via runx.run |
| api-adapter | External API connectors | Stored; execution path evolving (MCP gated) |
| composition / wrapper / parameterized | Reuse and specialization | Stored; JS execution for leaf nodes |
| ui | React / OpenUI / HTML panels | Artifacts (not executed in-process by default) |
| script | PowerShell, bash, runbooks | Artifacts (controlled runner not enabled by default) |
Every capability supports two consumption modes:
- Runtime —
await runx.run(id, input, params) - Artifact —
await runx.getCapabilityBundle(id, { includeDependencies: true, ... })
Install
npm install @x12i/runxRequires Node.js ≥ 20.
Configuration
Copy .env.example to .env for local development (do not commit secrets).
| Variable | Purpose |
|----------|---------|
| FIREBASE_PROJECT_ID, GOOGLE_SERVICE_ACCOUNT_BASE64, FIRESTORE_DATABASE_ID | Catalox / Firestore backend |
| MONGO_URI or CATALOX_MONGO_URI | Optional Mongo for mapped catalogs |
| OPEN_ROUTER_KEY | FuncX LLM backend when using funcxCreateOptions |
| RUNX_CAPABILITIES_CATALOG_ID | Override default runx-capabilities |
| RUNX_RUNTIME_PACKAGES_CATALOG_ID | Override default runx-runtime-packages |
You can also pass a custom catalox instance into createRunx({ catalox }) for tests or alternate backends.
Quick start (SDK)
import { createRunx } from "@x12i/runx";
import type { RunxCapability } from "@x12i/runx";
const runx = await createRunx({
// Optional: FuncX for authoring
// funcxCreateOptions: { backend: "openrouter", ... },
});
await runx.bootstrap(); // ensure Runx catalogs exist in Catalox
await runx.reload(); // load capabilities into the in-memory cache
// Persist a capability (often produced by FuncX authoring, then normalized)
const capability: RunxCapability = {
capabilityId: "normalize-finding",
displayName: "Normalize finding",
description: "Maps raw finding input to a canonical shape",
kind: "javascript",
status: "active",
revision: 1,
runtime: {
runtimeLanguage: "javascript",
executionRuntime: "node",
moduleFormat: "esm",
entrypoint: "run",
executionKind: "node-js",
},
contract: {
inputSchema: { type: "object" },
outputSchema: { type: "object" },
},
artifacts: {
javascript: {
language: "javascript",
moduleFormat: "esm",
source: `export async function run(input, params = {}, _context = {}) {
return { normalized: input, params };
}`,
filename: "normalize-finding.mjs",
entrypoint: "run",
exports: ["run"],
sourceHash: "",
sizeBytes: 0,
},
},
dependencies: { internalCapabilities: [], runtimePackages: [] },
reuse: { searchKeys: [], reusedFrom: [] },
exposure: { publicName: "normalize-finding", toolExposureMode: "input-only" },
tags: ["example"],
audit: { createdAt: new Date().toISOString(), createdBy: "app" },
};
await runx.putCapability(capability);
await runx.reloadCapability("normalize-finding");
// Run
const result = await runx.run("normalize-finding", { title: "Issue" }, {});
if (result.ok) console.log(result.output);
// Export full bundle (code + dependency manifest)
const bundle = await runx.getCapabilityBundle("normalize-finding", {
includeDependencies: true,
includeTests: true,
});Condition evaluation (checkContentCondition)
Use the built-in runtime evaluator to answer: does this content meet this condition?
import {
checkContentCondition,
seedCheckContentConditionCapability,
} from "@x12i/runx";
// Or import only the library: import { checkContentCondition } from "@x12i/runx/condition";
// Direct (no Catalox)
const result = await checkContentCondition({
content: { status: "active", score: 91 },
condition: "content.status === 'active' && content.score >= parameters.minScore",
parameters: { minScore: 80 },
conditionType: "javascript",
});
// { ok: true, meetsCondition: true, reasoning: "..." }
// Via stored capability (after seeding once per catalog)
await seedCheckContentConditionCapability(runx);
await runx.reloadCapability("runx.condition.checkContentCondition");
const runResult = await runx.run("runx.condition.checkContentCondition", {
content: { department: "Finance" },
conditionType: "json",
condition: {
all: [{ path: "content.department", op: "eq", value: "Finance" }],
},
}, {});Supported condition types:
| conditionType | Formats | Notes |
|-----------------|---------|--------|
| javascript | expression, return body, function | Sandboxed node:vm; Rendrix {{tokens}} from parameters |
| json | native filter predicate (all / any / not), JSONLogic, JSONata wrapper | options.jsonConditionFormat: auto (default) or explicit |
| either | custom semantic rules | mode: "hybrid" or mode: "model" + client / funcx on createRunx |
Authoring flows (FuncX runx.condition.create*) can smoke-test generated rules with verifyConditionWithRunx(runx, input).
Typical application flow
createRunx()— connect to Catalox (from env or injected client).bootstrap()— create Runx catalog entries if missing.reload()— refresh the in-memory cache from Catalox.- Author — use FuncX
runx.*skills, thenputCapability(or upsert via your pipeline). run/getCapabilityBundle/getCapabilityArtifact— execute or export.reloadCapability(id)after external catalog edits.
createCapability() on the client is reserved for a future end-to-end path; today, persist with putCapability after FuncX authoring (see error message in createRunx.ts).
MCP: expose capabilities to AI clients
Install globally or use npx:
npx runx-mcp --transport stdioEach active executable capability becomes a tool named runx.<capabilityId>. UI/script capabilities can expose artifact/bundle tools when configured (--expose-artifacts, --expose-bundles).
Cursor / Claude Desktop (stdio)
{
"mcpServers": {
"runx": {
"command": "npx",
"args": ["-y", "@x12i/runx", "runx-mcp", "--transport", "stdio", "--active-only"],
"env": {
"FIREBASE_PROJECT_ID": "...",
"GOOGLE_SERVICE_ACCOUNT_BASE64": "..."
}
}
}
}Programmatic server
import { createRunx } from "@x12i/runx";
import { createRunxMcpStdioServer } from "@x12i/runx/mcp";
const runx = await createRunx();
await runx.bootstrap();
await runx.reload();
const mcp = await createRunxMcpStdioServer({
runx,
exposure: { activeOnly: true, exposeArtifactTools: true },
});
await mcp.start();HTTP transport: createRunxMcpHttpServer or runx-mcp --transport http --port 3334.
Read-only catalog context is available as MCP resources (e.g. runx://catalog/capabilities, runx://capabilities/{id}/artifacts/javascript). See docs/mcp.md for tool naming, security defaults, and exposure policy.
Main API surface
| Method | Description |
|--------|-------------|
| bootstrap() | Ensure Runx Catalox catalogs exist |
| load() / reload() | Refresh in-memory cache from Catalox |
| putCapability / getCapability / listCapabilities | CRUD against the capability catalog |
| run(capabilityId, input?, params?, options?) | Execute (JavaScript in v1 core) |
| getCapabilityArtifact / getCapabilityBundle / exportCapability | Artifact and export paths |
| activateCapability / archiveCapability / deleteCapability | Lifecycle |
| getToolSpec / listToolSpecs | Tool metadata for integrators |
| listRuntimePackages / putRuntimePackage | Allowed npm packages for sandboxed runs |
| checkContentCondition | Evaluate content against a JS/JSON condition (also @x12i/runx/condition) |
| seedCheckContentConditionCapability | Idempotent putCapability for runx.condition.checkContentCondition |
| verifyConditionWithRunx | Authoring smoke-test helper wrapping runx.run for the condition capability |
Types are exported from @x12i/runx. Subpath exports: @x12i/runx/condition, @x12i/runx/mcp.
Development
npm install
npm run build
npm test
# Live tests (Firestore + condition evaluator; requires .env Catalox credentials):
RUNX_LIVE_TESTS=1 npm run test:liveLive suite covers catalog bootstrap, runx.condition.checkContentCondition seed/run/bundle, and direct checkContentCondition against Firestore when RUNX_LIVE_TESTS=1 or FIRESTORE_LIVE_TESTS=1.
Documentation
| Doc | Contents | |-----|----------| | docs/specs.md | Full product and package specification | | docs/mcp.md | MCP tools, resources, prompts, security | | docs/appendix.md | Supplementary notes |
V1 scope (read before production)
- JavaScript capabilities are fully executed in the core library; other executable kinds are stored and exposed but may return
unsupported_kinduntil implemented. - Condition evaluation (
runx.condition.checkContentCondition) sandboxes condition JavaScript innode:vm; the outer capability module still runs in the host Node process. Seed withseedCheckContentConditionCapabilitybefore calling viarunx.run. - Scripts are not executed through Runx or MCP by default—use artifacts and an external runner.
- Authoring goes through FuncX, not
runx.createCapability(). - MCP reflects the Runx catalog dynamically; tighten exposure with
--active-only, kind/id allowlists, and auth policies for HTTP deployments.
License
MIT
