RuimteDocs

@ruimte/extension/server

What server/ imports. It runs as one process on the machine, started on the first call.

Generated from the type declarations your editor reads, so a name here is a name you can import.

ExtensionErrorCode

Why a call came back without its answer. permission-denied: the manifest does not ask for the permission. no-project: no project with a folder is open. no-canvas: no canvas is in front. not-found: what it names is not there. outside-project: a path leaves the project folder. refused: the machine or the person said no. limit: too many calls in a while. timeout: no answer in time. unavailable: the machine, or server/, cannot answer now. failed: anything else, with its message.

export type ExtensionErrorCode =
    | 'permission-denied'
    | 'no-project'
    | 'no-canvas'
    | 'not-found'
    | 'outside-project'
    | 'refused'
    | 'limit'
    | 'timeout'
    | 'unavailable'
    | 'failed';

Result

What a call that can fail answers: its value, or why not. Only a wrong argument throws. A call with nothing to answer, a Result<void>, answers { ok: true, value: undefined } with the key value there.

export type Result<Value, Code extends string = ExtensionErrorCode> = { ok: true; value: Value } | { ok: false; error: { code: Code; message: string } };

Json

A value as it crosses to the machine and back.

export type Json = string | number | boolean | null | Json[] | { [key: string]: Json | undefined };

NodeRef

export interface NodeRef {
    id: string;
}

ViewRef

export interface ViewRef {
    id: string;
}

ProjectRef

export interface ProjectRef {
    id: string;
}

AgentStatus

Where an agent is, the same word everywhere it shows.

export type AgentStatus = 'idle' | 'running' | 'needsYou' | 'stopped';

ChatSummary

A chat on the machine, as chats.list answers it. Its id is the id of its node.

export interface ChatSummary {
    id: string;
    provider: string;
    status: AgentStatus;
    model: string | null;
    cwd: string;
}

defineServer

What the client can call. The default export of server/index.ts. A method that throws reaches the client as failed, with its message.

export function defineServer<Api extends Record<string, (...args: never[]) => Promise<unknown>>>(api: Api): Api;

ServerEvents

The machine's events server/ can follow, with what each says.

export interface ServerEvents {
    /** A chat changed what it is doing. Needs `chats.read`. */
    'chat.status': { chat: ChatSummary };
    /** Something new in a chat's thread; `chats.read` reads it. Needs `chats.read`. */
    'chat.event': { chatId: string };
    /** The agent in a terminal changed what it is doing. Needs `terminals.read`. */
    'session.status': { nodeId: string; status: AgentStatus };
}

onEvent

Follows a machine event, behind its permission; any other name is refused. Set "autostart": true to follow them with no client open.

export function onEvent<Name extends keyof ServerEvents>(name: Name, listener: (event: ServerEvents[Name]) => void): () => void;

emit

Says something to every client of this machine, where useServerEvent(name) hears payload, or undefined without one.

export function emit<Payload>(name: string, payload?: Payload): void;

defineNodeContext

What an agent reads of a node along a line, also with no client open.

export function defineNodeContext<Data>(nodeType: string, read: (node: { data: Data }) => Promise<{ text: string }[]> | { text: string }[]): void;

defineAgentTool

Needs agents.tools. Any agent Ruimte starts on this machine can then find it with tool_list and call it with tool_run. Having it starts server/ with the machine, since an agent can only find what is running. It answers in text. Type Input after parameters, a JSON Schema.

export function defineAgentTool<Input extends object = { [key: string]: Json | undefined }>(tool: {
    id: string;
    description: string;
    parameters?: object;
    run(input: Input, context: { caller: string }): string | Promise<string>;
}): void;

schedule

Needs the permission clock. Runs on this process's own clock: every is like 30s, 5m or 1h, of at least 10 seconds and at most 596 hours.

export function schedule(task: { id: string; every: string; run(): void | Promise<void> }): void;

agents

export const agents: {
    /** Needs `agents.run`. One hidden prompt on a CLI of the person, with the answer whole. */
    run(options: { prompt: string; context?: string[]; provider?: string }): Promise<Result<{ text: string }>>;
    /** Needs `agents.start`. A chat a person sees, supervised at most: on the canvas `view`, beside `near`, or a view of its own in the sidebar without one. */
    start(options: { project: ProjectRef; view?: ViewRef; near?: NodeRef[]; prompt?: string; provider?: string; title?: string }): Promise<Result<NodeRef>>;
};

chats

list and read need chats.read, send needs chats.send.

export const chats: {
    list(): Promise<Result<ChatSummary[]>>;
    /** The thread as text, as an agent reads it. */
    read(chatId: string): Promise<Result<string>>;
    /** Wakes the chat. At most 20 messages per chat per hour, past which it answers `limit`. */
    send(chatId: string, text: string): Promise<Result<void>>;
};

storage

export const storage: {
    get<Value>(key: string): Promise<Value | undefined>;
    set<Value>(key: string, value: Value): Promise<void>;
    delete(key: string): Promise<void>;
};

secrets

Kept readable only by the person who runs the machine.

export const secrets: {
    get(key: string): Promise<string | undefined>;
    set(key: string, value: string): Promise<void>;
};

machine

export const machine: {
    platform: string;
    dataFolder: string;
    /** An action of the machine in a project, such as `launch.start`, within what your manifest asks for. */
    run<Name extends MachineActionName>(project: ProjectRef, action: Name, input: MachineInput<Name>): Promise<Result<MachineOutput<Name>>>;
};

defineLanguageServer

Needs languages.servers. Adds a server, listed in Settings like one a person added, and started without asking: installing the extension was the approval. Calling it again with the same name changes it, and removing the extension removes it unless a person edited it.

export function defineLanguageServer(server: {
    name: string;
    command: string;
    args?: string[];
    env?: Record<string, string>;
    languages: string[];
    patterns: string[];
}): Promise<Result<void>>;

log

export const log: {
    info(message: string): void;
    /** `error` is what a `catch` caught, of any shape. */
    error(message: string, error?: unknown): void;
};