@getcodex/sdk
v1.4.1
Published
Codex Editor SDK
Readme
@getcodex/sdk
Typed TypeScript client for the Codex API. Works in Node, Bun, and the browser.
Not to be confused with the other Codex SDK. This is for a document editor, not for a coding agent.
Installation
bun add @getcodex/sdk
# or
npm install @getcodex/sdkREST Client
Setup
import { CodexClient } from "@getcodex/sdk";
const client = new CodexClient({
baseUrl: "https://codex.cane1712.dev",
apiKey: "cdx_yourkey", // API key
// OR
accessToken: "cdx_at_...", // OAuth access token (takes precedence)
});Workspaces
const workspaces = await client.workspaces.list();
const ws = await client.workspaces.get("my-workspace");
await client.workspaces.create("My Workspace", "my-workspace");
await client.workspaces.update("my-workspace", { visibility: "public" });
const members = await client.workspaces.members("my-workspace");
const { token } = await client.workspaces.createInvite("my-workspace", { maxUses: 10 });Documents
const docs = await client.docs.list("my-workspace");
const doc = await client.docs.get("my-workspace", docId);
const { id } = await client.docs.create("my-workspace", "New Document");
await client.docs.update("my-workspace", docId, { title: "Renamed", visibility: "internal" });
await client.docs.delete("my-workspace", docId);
// Folders
const folders = await client.docs.listFolders("my-workspace");
await client.docs.createFolder("my-workspace", "Engineering", parentId);Comments
const comments = await client.comments.list(docId);
await client.comments.create(docId, "Looks good!", { quote: "selected text" });
await client.comments.update(docId, commentId, { resolved: true });Versions
const versions = await client.versions.list(docId);
const { id } = await client.versions.create(docId, "Before refactor");
const snapshot = await client.versions.get(docId, versionId);API Keys
const keys = await client.apiKeys.list("my-workspace");
const { key } = await client.apiKeys.create("my-workspace", "CI bot", { scopes: ["docs:read"] });
await client.apiKeys.delete("my-workspace", keyId);Webhooks
const hooks = await client.webhooks.list("my-workspace");
await client.webhooks.create("my-workspace", {
url: "https://example.com/hook",
events: ["doc.created", "doc.updated", "doc.deleted", "comment.created", "member.joined"],
});
await client.webhooks.ping("my-workspace", webhookId);
const deliveries = await client.webhooks.deliveries("my-workspace", webhookId);Plugins
// Instance admin
const registries = await client.plugins.registries.list();
await client.plugins.registries.add({
name: "Official",
url: "https://plugins.codex.cane1712.dev",
confirmed: true,
});
// registryId accepts either the registry's UUID or its slug (e.g. "official").
// Calling install() again for an already-installed plugin re-fetches and
// overwrites the stored bundle — this doubles as the update path.
await client.plugins.install({ registryId: "official", pluginId: "word-count" });
// Workspace policy
await client.plugins.setPolicy("my-workspace", pluginId, "required"); // required | available | blocked
const wsPlugins = await client.plugins.listForWorkspace("my-workspace");
// Capability token (for plugin iframes)
const { token } = await client.plugins.token("my-workspace", pluginId);Uploads
// Browser: upload a File from <input>
const uploadId = await client.uploads.create(docId, inputEl.files[0]);
const rawUrl = client.uploads.rawUrl(docId, uploadId);OAuth Server Integration
For third-party apps that authenticate with a Codex instance via OAuth.
import { CodexOAuth } from "@getcodex/sdk";
const oauth = new CodexOAuth("https://codex.cane1712.dev");
// 1. Redirect user to authorization URL
const url = oauth.getAuthorizationUrl({
clientId: "your-client-id",
redirectUri: "https://yourapp.com/callback",
scopes: ["docs:read", "profile"],
state: crypto.randomUUID(),
codeChallenge: pkceChallenge,
codeChallengeMethod: "S256",
});
// 2. Exchange code for token
const { accessToken } = await oauth.exchangeCode({
code: req.query.code,
clientId: "your-client-id",
redirectUri: "https://yourapp.com/callback",
codeVerifier: pkceVerifier,
});
// 3. Use token with CodexClient
const client = new CodexClient({ baseUrl: "https://codex.cane1712.dev", accessToken });Plugin Development
Worker plugin (@getcodex/sdk/plugin)
Worker plugins run in a sandboxed Web Worker and respond to document events.
// src/worker.ts
import { defineWorker } from "@getcodex/sdk/plugin";
export default defineWorker({
events: ["doc.updated"],
async on(event, ctx) {
if (event.type === "doc.updated") {
const doc = await ctx.doc.get(event.docId);
console.log(`[${ctx.pluginId}] updated: ${doc.title} (${doc.text.length} chars)`);
}
},
});Build:
bun build src/worker.ts --outfile dist/worker.js --target browserUI plugin (@getcodex/sdk/plugin/ui)
UI plugins render inside a sandboxed iframe. They receive live document state and can register toolbar buttons and sidebar panels.
// src/ui.tsx
import { defineUI, useDoc, Toolbar, Sidebar } from "@getcodex/sdk/plugin/ui";
defineUI({
toolbar: (
<Toolbar.Button id="word-count" icon="hash" label="Word Count">
<WordCountPanel />
</Toolbar.Button>
),
sidebar: (
<Sidebar.Panel id="info" title="Doc Info">
<DocInfoPanel />
</Sidebar.Panel>
),
});
function WordCountPanel() {
const doc = useDoc();
const words = doc.text.trim().split(/\s+/).filter(Boolean).length;
return <div>{words} words</div>;
}Build (IIFE format — required so the bundle auto-executes inside the iframe):
bun build src/ui.tsx --outfile dist/ui.js --target browser --format=iifePlugin manifest (manifest.codex.json)
{
"id": "dev.yourname.word-count",
"version": "1.0.0",
"name": "Word Count",
"description": "Shows word and character count in the toolbar.",
"permissions": ["doc.read"],
"ui": { "entry": "dist/ui.js" },
"worker": {
"entry": "dist/worker.js",
"events": ["doc.updated"]
}
}Scaffold a new plugin with the CLI:
cdx plugins init dev.yourname.word-countSubpath Exports
| Import | Contents |
|--------|----------|
| @getcodex/sdk | CodexClient, CodexOAuth, shared types |
| @getcodex/sdk/plugin | defineWorker, WorkerContext, WorkerDefinition |
| @getcodex/sdk/plugin/ui | defineUI, useDoc, usePlugin, Toolbar, Sidebar |
| @getcodex/sdk/plugin/bridge | HostToPlugin, PluginToHost postMessage protocol types |
