The Coline App SDK: apps, tools, file types and surfaces that run inside Coline workspaces, and public websites with Coline Sites. TypeScript, with an in-memory test workspace. MIT.
The Coline App SDK — define apps that live inside Coline workspaces: Kairo tools, file types with rich previews, home surfaces, app storage, scheduled triggers, and external API access. Coline hosts your logic; there are no servers to deploy unless you want them.
npx create-coline-app my-app
import { z } from "zod/v4";
import { defineApp, defineTool, ui } from "@colineapp/sdk/v2";
const createTodo = defineTool({
name: "todos.create_todo",
description: "Create a todo with a title.",
input: z.object({ title: z.string().min(1) }),
effect: "write", // enforced by the runtime as a hard capability ceiling
execute: async (input, ctx) => {
const file = await ctx.coline.files.create({
typeKey: "todos.todo",
name: input.title,
document: { title: input.title, status: "open" },
});
return {
output: { fileId: file.fileId },
card: ui.reference({ kind: "file", id: file.fileId }),
};
},
});
export default defineApp({
key: "todos",
name: "Todos",
permissions: ["files.read", "files.write", "ai.tools"],
hosting: { default: "coline" },
surfaces: { home: { tier: "tree" } },
tools: [createTodo],
handlers: {
renderHome: async (ctx) => {
const { files } = await ctx.coline.files.list({ typeKey: "todos.todo" });
return ui.stack([
ui.heading("Todos"),
...files.map((f) => ui.fileCard({ title: f.name, fileId: f.fileId })),
]);
},
},
});
One definition produces two artifacts: a serializable manifest (what the app may do — reviewed and consented to) and a runtime bundle (what it does). Every capability call is checked against the workspace's grants, the acting user's access, and the declared tool effect.
The same coline.* object everywhere your code runs — tools, render
handlers, schedules, hosted or external:
coline.files — create/read/update/trash workspace filescoline.storage.kv / coline.storage.collection(name) — app-private
key-value state and queryable recordscoline.members, coline.notifications, coline.searchcoline.net.fetch — external HTTP through Coline's allowlist, with
{{secret:KEY}} server-side credential injectionimport { createTestWorkspace } from "@colineapp/sdk/testing";
import app from "./app.config";
const ws = createTestWorkspace(app);
await ws.invokeTool("todos.create_todo", { title: "Ship it" });
expect(ws.files.byType("todos.todo")).toHaveLength(1);
@colineapp/sdk/testing runs your app against an in-memory workspace with
the same capability contract as production.
| Entry | Contents |
|---|---|
@colineapp/sdk/v2 | defineApp, defineTool, defineFileType, ui, actions, manifest schemas, capability types, createColineAppHandler for external hosting |
@colineapp/sdk/testing | createTestWorkspace — the in-memory test harness |
@colineapp/sdk | The legacy v1 surface (frozen; do not use for new apps) |
Full guides at coline.app/developers/docs.
MIT © Coline
Run coline-app check before pushing: it checks types, the manifest, hosted logic,
and the React bundle without uploading. dev --internal updates one development
version; install it in the workspace and reload after changes. The CLI bundles
from the app's installed dependencies and preserves imported CSS and inline assets.
CLI 2.5 compiles app-specific Tailwind v4 and ordinary CSS automatically.
Import UI styles followed by app.css. The scaffold includes experimental UI
skills, screen examples, and a credential-free coline-app preview command.
The starter includes a React home and editor, tree mobile fallback, and a tool.
Use coline.tools.invoke or actions.invokeTool to run the same declared tool
from UI that Kairo runs. Save with expectedVersion to detect concurrent edits.
Homes/editors can write within grants; previews and server render handlers cannot.
createTestWorkspace(app).client() tests the interactive contract and
grantedPermissions simulates revoked consent.
New tasks, calendar, messages, and references capabilities require their
explicit manifest permissions and retain the invoking user's workspace access.
Use files.list({ scope: "workspace" }) for shared files; document writes stay
with the owning app. Collections are install-private. Code integration and a
native mobile React runtime are separate work.
createLiveCollection(coline, name, query) exposes a bounded live query with
synchronous optimistic updates, queued version-checked writes, errors, and
reconnect recovery. Subscribe via subscribe, call start, read getSnapshot,
and finish with dispose. React authors use useLiveCollection from
@colineapp/ui instead. A view holds at most 200 records.
Collection envelopes include version and access. insert accepts
{ access: { workspace: "none" | "read" | "write", readers: [], writers: [] } };
the default is workspace read/write. Only the creator can setAccess. Use
update(id, patch, { expectedVersion }) to reject stale writes. The same options
apply to delete/setAccess. Missing or inaccessible get returns null. Permissions
are filtered before pagination and also apply to reference resolution.
sync(query, revision?) returns a durable revision with an authorized snapshot;
records: null means unchanged. Live views use WebSocket invalidations and a
two-second fallback poll. They are structured-data views, not offline replicas
or character-level collaborative editors. Mutation errors must be caught; a
network error alone does not establish whether the server committed the write.