This document defines the public surface of @aro/core. Core is the headless engine for workspace management, job execution, persistence, and validation. It runs in Node.js only; Desktop (or another host) creates a Core instance and forwards operations.
import { createCore } from '@aro/core';
const core = createCore({ workspaceRoot: '/path/to/workspace' });
// ... use core
core.shutdown();function createCore(options: AroCoreOptions): AroCoreCreates a Core instance for the given workspace. Initializes .aro/ directory and SQLite database. Call core.shutdown() before releasing the instance.
Options:
| Property | Type | Required | Description |
|---|---|---|---|
workspaceRoot |
string | Yes | Absolute path to the workspace directory |
dbPath |
string | No | Override SQLite path (default: workspaceRoot/.aro/aro.sqlite) |
tokensPath |
string | No | Override tokens file path (default: tokens/tokens.json) |
The returned object provides the following services.
Scoped file access within the workspace. Paths are relative; ../ traversal is blocked.
| Method | Description |
|---|---|
initWorkspace() |
Ensures .aro/ exists |
resolve(path) |
Resolves a relative path; throws on ../ |
readText(path) |
Reads file content as string |
writeText(path, content) |
Writes file content |
exists(path) |
Returns true if file exists |
mkdirp(dir) |
Creates directory recursively |
Run lifecycle: start, finish, query.
| Method | Description |
|---|---|
startRun(params?) |
Starts a run; returns { runId } |
finishRun({ runId, status }) |
Finishes run with success, error, or cancelled |
getRun(runId) |
Returns Run or null |
listRuns(filter?) |
Returns runs ordered by time (newest first) |
Log entries for runs. Persisted in SQLite.
| Method | Description |
|---|---|
appendLog({ runId, level, message }) |
Appends a log entry |
listLogs(runId) |
Returns all entries for a run |
subscribe(runId, handler) |
Subscribes to new entries; returns unsubscribe function |
Artifacts are files written under .aro/artifacts/<runId>/. Indexed in SQLite.
| Method | Description |
|---|---|
writeArtifact({ runId, path, content }) |
Writes artifact; returns Artifact |
listArtifacts(runId) |
Returns artifacts for a run |
Job registration and execution. Jobs receive a JobContext with logger, workspace facet, artifact writer, and abort signal.
| Method | Description |
|---|---|
register(jobDef) |
Registers a job definition |
run(jobKey, input) |
Runs a job; returns { runId } |
cancel(runId) |
Cancels a running job via AbortSignal |
Load/save tokens from workspace (e.g. tokens/tokens.json).
| Method | Description |
|---|---|
loadTokens() |
Loads tokens from disk; returns parsed JSON or empty object |
saveTokens(tokens) |
Saves tokens to disk |
diffTokens(a, b) |
Returns TokenDiff (added, removed, changed keys) |
Zod-based validation at boundaries.
| Method | Description |
|---|---|
validateTokens(tokens) |
Returns ValidationResult with ok and optional issues |
core.shutdown(): voidCloses the database and clears subscriptions. Call before discarding the Core instance.
interface Run {
id: string;
status: string;
startedAt: number;
finishedAt: number | null;
createdAt: number;
}interface LogEntry {
id: string;
runId: string;
level: string;
message: string;
createdAt: number;
}interface Artifact {
id: string;
runId: string;
path: string;
createdAt: number;
}interface JobDefinition {
key: string;
run: (ctx: JobContext, input: unknown) => void | Promise<void>;
}Provided to job run functions.
interface JobContext {
readonly logger: RunLogger; // (level, message) => void
readonly workspace: WorkspaceFacet; // resolve, readText, writeText, exists, mkdirp
readonly artifactWriter: ArtifactWriter; // ({ path, content }) => Artifact
readonly abort: AbortSignal;
progress?: (value: number | { current: number; total: number }) => void;
}interface ValidationResult {
ok: boolean;
issues?: ValidationIssue[];
}
interface ValidationIssue {
path: string;
message: string;
}interface TokenDiff {
added: string[];
removed: string[];
changed: string[];
}Shared server-side registry for module initialization. Both hosts register their modules with these functions; all registry logic lives in @aro/core.
import { registerModule, resolveConfig, loadModules } from '@aro/core';
import { init as inspectInit } from '@aro/module-inspect';
// 1. Register modules
registerModule('inspect', inspectInit);
// 2. Resolve tenant config
resolveConfig(configDir);
// 3. Load modules into a Core instance
loadModules(core, setRegisteredJobKeys);| Function | Description |
|---|---|
registerModule(key, init) |
Registers a ModuleInit function under the given key |
getInit(key) |
Returns the ModuleInit for a key, or undefined |
getRegisteredModuleKeys() |
Returns all registered module keys |
resolveConfig(configDir) |
Loads tenant config via @aro/config; caches result |
getResolvedConfig() |
Returns the cached config (throws if not yet resolved) |
getUIModel() |
Returns the current UI model from resolved config |
getEnabledModuleKeys() |
Returns enabled module keys (with unknown-key warnings) |
| Function | Description |
|---|---|
loadModules(core, setRegisteredJobKeys) |
Initializes enabled modules on a Core instance. Standalone mode loads only the first module. Calls setRegisteredJobKeys with accumulated job keys. |
Reference adapter that wraps AroCore to produce an AroPreloadAPI-compatible object. Both Desktop (IPC) and Web (HTTP/WS) delegate their Core operations through this adapter — the transport layer remains host-specific, but operation logic is defined once here. Also usable directly for contract tests.
import { createCoreAdapter } from '@aro/core';
// Contract test usage (minimal options)
const api = createCoreAdapter(core, {
uiModel: 'standalone',
enabledModules: ['inspect'],
workspaceRoot: '/path/to/workspace',
});
// Host usage (full options)
const api = createCoreAdapter(core, {
uiModel: config.uiModel,
enabledModules: config.enabledModules,
workspaceRoot: getCurrentWorkspacePath()!,
tenantConfig: config,
getRegisteredJobKeys,
});Options (CoreAdapterOptions):
| Property | Type | Required | Description |
|---|---|---|---|
uiModel |
UIModel | Yes | Which UI model the adapter reports |
enabledModules |
string[] | Yes | Module keys the adapter reports as enabled |
workspaceRoot |
string | Yes | Absolute path to the workspace |
tenantConfig |
TenantConfig | No | Full config; when set, getTenantConfig() returns it verbatim |
getRegisteredJobKeys |
() => string[] | No | Job key accessor; when set, job.listRegistered() uses it instead of enabledModules |
AroCore_v1 is a type alias for the current AroCore interface (exported from @aro/types). When a breaking change lands, a new alias (AroCore_v2) will be introduced while the old version remains available during migration. Additive, non-breaking changes (new optional methods) do not bump the version.
From @aro/core:
createCorecreateCoreAdapterregisterModule,getInit,getRegisteredModuleKeysresolveConfig,getResolvedConfig,getUIModel,getEnabledModuleKeysloadModules- Types:
AroCore,AroCore_v1,AroCoreOptions,CoreAdapterOptions,ModuleInit,Run,LogEntry,Artifact,ValidationIssue,ValidationResult,TokenDiff,JobDefinition,JobContext