Skip to content

Latest commit

 

History

History
891 lines (711 loc) · 20.3 KB

File metadata and controls

891 lines (711 loc) · 20.3 KB

SDK and API Reference

This is the full public API surface exported by @codex-plusplus/sdk.

Use it for types:

import {
  defineTweak,
  validateTweakManifest,
  type Tweak,
  type TweakApi,
  type TweakManifest,
} from "@codex-plusplus/sdk";

Runtime note: TypeScript and ESM must be bundled to runtime-loadable JavaScript before installing a tweak. See TypeScript and bundling.

Export Index

Functions:

Constants:

Types and interfaces:

defineTweak(tweak)

function defineTweak(tweak: Tweak): Tweak

defineTweak is an identity helper for type inference.

import { defineTweak } from "@codex-plusplus/sdk";

export default defineTweak({
  start(api) {
    api.log.info(api.manifest.id);
  },
});

validateTweakManifest(manifest)

function validateTweakManifest(manifest: unknown): TweakManifestValidationResult

Validates the manifest shape. It checks required string fields, id characters, GitHub repo format, scope, optional strings, author object, tags, permissions, and MCP shape.

It does not check that the entry file exists; codexplusplus validate-tweak adds that filesystem check.

const result = validateTweakManifest(manifest);
if (!result.ok) {
  for (const issue of result.errors) {
    console.error(`${issue.path}: ${issue.message}`);
  }
}

VALID_TWEAK_SCOPES

const VALID_TWEAK_SCOPES = ["renderer", "main", "both"] as const

Runtime-supported manifest scopes.

VALID_TWEAK_PERMISSIONS

const VALID_TWEAK_PERMISSIONS = [
  "ipc",
  "filesystem",
  "network",
  "settings",
  "codex-runtime",
  "codex-windows",
  "codex-views",
  "codex-cdp",
  "codex.windows",
  "codex.views",
  "native-module",
  "native-view",
  "native-helper",
] as const

Known permission declaration values.

Tweak

interface Tweak {
  start(api: TweakApi): void | Promise<void>;
  stop?(): void | Promise<void>;
}

The object exported by a tweak entry.

Runtime accepted shapes:

module.exports = { start() {} };
exports.start = function start() {};
module.exports.default = { start() {} };

Bundle TypeScript/ESM output before installing.

TweakApi

interface TweakApi {
  manifest: Readonly<TweakManifest>;
  storage: TweakStorage;
  log: TweakLogger;
  process: "renderer" | "main";
  settings?: SettingsApi;
  react?: ReactApi;
  ipc: TweakIpc;
  fs: TweakFs;
  codex?: CodexApi;
}

Available by process:

API Renderer Main
manifest yes yes
storage yes yes
log yes yes
process yes yes
settings yes no
react yes no
ipc yes yes
fs yes yes
codex yes yes

Check api.process or optional properties before using process-specific APIs in scope: "both" tweaks.

TweakManifest

interface TweakManifest {
  id: string;
  name: string;
  version: string;
  githubRepo: string;
  description?: string;
  author?: string | TweakAuthor;
  homepage?: string;
  iconUrl?: string;
  tags?: string[];
  minRuntime?: string;
  scope?: TweakScope;
  main?: string;
  mcp?: TweakMcpServer;
  permissions?: TweakPermission[];
}

See Manifest reference for runtime behavior and examples.

TweakAuthor

interface TweakAuthor {
  name: string;
  url?: string;
  email?: string;
}

Structured author metadata. author can also be a plain string.

TweakScope

type TweakScope = "renderer" | "main" | "both"

Controls which tweak host loads the entry. Missing scope currently behaves like both; set it explicitly.

TweakPermission

type TweakPermission =
  | "ipc"
  | "filesystem"
  | "network"
  | "settings"
  | "codex-runtime"
  | "codex-windows"
  | "codex-views"
  | "codex-cdp"
  | "codex.windows"
  | "codex.views"
  | "native-module"
  | "native-view"
  | "native-helper"

Declared capabilities for user visibility/review. Native permissions and Owl view permissions are enforced at runtime.

TweakMcpServer

interface TweakMcpServer {
  command: string;
  args?: string[];
  env?: Record<string, string>;
}

Declares an MCP server managed by Codex++. See MCP servers.

TweakManifestIssue

interface TweakManifestIssue {
  path: string;
  message: string;
}

Validation issue shape.

TweakManifestValidationResult

interface TweakManifestValidationResult {
  ok: boolean;
  errors: TweakManifestIssue[];
  warnings: TweakManifestIssue[];
}

Returned by validateTweakManifest.

TweakStorage

interface TweakStorage {
  get<T = unknown>(key: string, fallback?: T): T;
  set(key: string, value: unknown): void;
  delete(key: string): void;
  all(): Record<string, unknown>;
}

Per-tweak key/value storage.

Renderer backing store:

localStorage["codexpp:storage:<tweak-id>"]

Main backing store:

<userRoot>/storage/<tweak-id>.json

Example:

const count = api.storage.get("count", 0);
api.storage.set("count", count + 1);

Values must be JSON-serializable if you expect them to persist safely.

TweakLogger

interface TweakLogger {
  debug(...args: unknown[]): void;
  info(...args: unknown[]): void;
  warn(...args: unknown[]): void;
  error(...args: unknown[]): void;
}

Renderer logs go to DevTools and <userRoot>/log/preload.log. Main logs go to <userRoot>/log/main.log.

SettingsApi

interface SettingsApi {
  register(section: SettingsSection): SettingsHandle;
  registerPage(page: SettingsPage): SettingsHandle;
}

Renderer-only. settings is undefined in main.

Use register for compact controls nested under the tweak card. Use registerPage for a full sidebar page.

SettingsSection

interface SettingsSection {
  id: string;
  title: string;
  description?: string;
  render(root: HTMLElement): void | (() => void);
}

id is scoped by tweak id at runtime. A local id of quick becomes <tweak-id>:quick.

Runtime note: section cleanup return values are accepted by the type but current section rendering is simple. Clean up long-lived resources from tweak stop().

SettingsPage

interface SettingsPage {
  id: string;
  title: string;
  description?: string;
  iconSvg?: string;
  render(root: HTMLElement): void | (() => void);
}

Renderer-only dedicated settings page. Pages appear under a "Tweaks" sidebar group. iconSvg should be 20 by 20 SVG markup using currentColor.

Page cleanup functions are called before re-rendering and when pages unregister.

SettingsHandle

interface SettingsHandle {
  unregister(): void;
}

Returned by settings.register and settings.registerPage.

ReactApi

interface ReactApi {
  getFiber(node: Element): ReactFiberNode | null;
  findOwnerByName(node: Element, name: string): ReactFiberNode | null;
  waitForElement(selector: string, timeoutMs?: number): Promise<Element>;
}

Renderer-only. Use as an escape hatch for Codex internals.

Example:

const composer = await api.react.waitForElement("[data-testid='composer']", 8000);
const fiber = api.react.getFiber(composer);
const owner = api.react.findOwnerByName(composer, "Composer");

Component names and fiber shapes can change in upstream Codex builds.

ReactFiberNode

interface ReactFiberNode {
  type: unknown;
  stateNode: unknown;
  memoizedProps: Record<string, unknown> | null;
  memoizedState: unknown;
  return: ReactFiberNode | null;
  child: ReactFiberNode | null;
  sibling: ReactFiberNode | null;
}

Minimal exposed React fiber shape. Treat it as unstable.

TweakIpc

interface TweakIpc {
  on(channel: string, handler: (...args: unknown[]) => void): () => void;
  send(channel: string, ...args: unknown[]): void;
  invoke<T = unknown>(channel: string, ...args: unknown[]): Promise<T>;
  handle?(channel: string, handler: (...args: unknown[]) => unknown): void;
}

Channels are prefixed as:

codexpp:<tweak-id>:<channel>

Renderer supports on, send, and invoke.

Main supports on and handle. Main-side send and invoke intentionally throw because they are renderer-to-main operations in the current runtime.

TweakFs

interface TweakFs {
  dataDir: string;
  read(relPath: string): Promise<string>;
  write(relPath: string, contents: string): Promise<void>;
  exists(relPath: string): Promise<boolean>;
}

Sandboxed to:

<userRoot>/tweak-data/<tweak-id>/

Keep paths relative. Renderer calls are proxied through main IPC. Current main-side implementation joins paths under dataDir; keep to relative paths and avoid path traversal.

CodexApi

interface CodexApi {
  runtime: CodexRuntimeApi;
  windows: CodexWindowsApi;
  views: CodexViewsApi;
  cdp: CodexCdpApi;
  native: CodexNativeApi;
  createBrowserView(options: CodexCreateViewOptions): Promise<unknown>;
  createWindow(options: CodexCreateWindowOptions): Promise<CodexWindowRef>;
}

Available in renderer and main. Renderer calls are proxied to the main process where required.

These APIs depend on Codex window services exposed by the patch. They may need runtime updates when upstream Codex changes its Owl/Electron internals. See Owl runtime surface for the private upstream APIs Codex++ currently observes.

createWindow() and createBrowserView() are kept for backwards compatibility. Prefer the namespaced APIs for new tweaks.

CodexRuntimeType

type CodexRuntimeType = "owl" | "electron" | "unknown";

CodexRuntimeInfo

interface CodexRuntimeInfo {
  type: CodexRuntimeType;
  codexVersion: string | null;
  channel: string | null;
  buildFlavor: string | null;
  usesOwlAppShell: boolean | null;
  appPath: string | null;
  resourcesPath: string | null;
}

CodexRuntimeCapabilities

interface CodexRuntimeCapabilities {
  windows: {
    create: boolean;
    focus: boolean;
    primary: boolean;
    browserView: boolean;
  };
  views: {
    create: boolean;
    privateViewTree: boolean;
    webContentsView: boolean;
    browserViewFallback: boolean;
  };
  cdp: {
    supported: boolean;
    enabled: boolean;
    port: number | null;
  };
  native: {
    inProcessModules: boolean;
    swiftModules: boolean;
    appKitEmbedding: boolean;
    childWindowOverlay: boolean;
    directViewAttach: boolean;
    metalViews: boolean;
    nativeHost: boolean;
    helpers: boolean;
  };
}

CodexRuntimeApi

interface CodexRuntimeApi {
  getInfo(): Promise<CodexRuntimeInfo>;
  getCapabilities(): Promise<CodexRuntimeCapabilities>;
}

Use this before calling Owl-sensitive APIs.

CodexWindowsApi

interface CodexWindowsApi {
  create(options: CodexCreateWindowOptions): Promise<CodexWindowRef>;
  getPrimary(): Promise<CodexWindowRef | null>;
  focus(windowId: number): Promise<boolean>;
  show(windowId: number): Promise<boolean>;
}

create() is the namespaced replacement for api.codex.createWindow().

CodexViewCreateOptions

interface CodexViewCreateOptions {
  id?: string;
  parentWindowId?: number;
  route?: string;
  url?: string;
  hostId?: string;
  appearance?: string;
  bounds?: { x: number; y: number; width: number; height: number };
  visible?: boolean;
  backgroundColor?: string;
  registerWithCodex?: boolean;
}

Creates an Owl BrowserView / WebContentsView child inside a Codex window. When route is provided, Codex++ loads the Codex app route through app://-/index.html and registers the view with Codex's host context unless registerWithCodex is false. When url is provided, it loads that URL.

The tweak manifest must declare codex-views or the legacy codex.views permission.

CodexViewRef

interface CodexViewRef {
  id: string;
  webContentsId: number;
  parentWindowId: number | null;
  setBounds(bounds: { x: number; y: number; width: number; height: number }): Promise<void>;
  setVisible(visible: boolean): Promise<void>;
  bringToFront(): Promise<void>;
  loadRoute(route: string, hostId?: string): Promise<void>;
  loadUrl(url: string): Promise<void>;
  dispose(): Promise<void>;
}

The ref is serializable and works from renderer or main tweaks. Internally, Codex++ prefers Owl's private contentView.addChildView(view.webContentsView) path and falls back to BrowserWindow.addBrowserView(view).

CodexViewsApi

interface CodexViewsApi {
  create(options: CodexViewCreateOptions): Promise<CodexViewRef>;
}

CodexCdpStatus

interface CodexCdpStatus {
  supported: boolean;
  enabled: boolean;
  port: number | null;
  url: string | null;
}

CDP is opt-in. Codex++ reports status but does not silently enable it.

CodexCdpTarget

interface CodexCdpTarget {
  id: string;
  type: string;
  title?: string;
  url: string;
  webSocketDebuggerUrl?: string;
}

CodexCdpApi

interface CodexCdpApi {
  getStatus(): Promise<CodexCdpStatus>;
  listTargets(): Promise<CodexCdpTarget[]>;
}

listTargets() returns an empty array unless CDP is already enabled.

NativeModuleKind

type NativeModuleKind = "node-addon" | "dylib" | "framework";

1.0.0 loads .node addons directly. Swift dylibs/frameworks should be reached through an Objective-C++ .node shim.

NativeModuleLoadOptions

interface NativeModuleLoadOptions {
  id: string;
  path: string;
  kind?: NativeModuleKind;
  entrypoint?: string;
}

path is resolved inside the tweak directory. The file must exist, and Codex++ checks the real path so traversal and symlink escapes outside that directory are rejected.

NativeModuleRef

interface NativeModuleRef {
  id: string;
  kind: NativeModuleKind;
  request(method: string, payload?: unknown, timeoutMs?: number): Promise<unknown>;
  dispose(): Promise<void>;
}

For .node modules, Codex++ calls module.request(method, payload) when present, otherwise it calls an exported function matching method. The tweak manifest must declare native-module.

NativePanelCreateOptions

interface NativePanelCreateOptions {
  moduleId?: string;
  factory?: string;
  parentWindowId?: number;
  bounds?: { x: number; y: number; width: number; height: number };
  transparent?: boolean;
  passthroughMouse?: boolean;
}

Calls a factory exported by a loaded native module. When moduleId is omitted, Codex++ uses its built-in native host and defaults factory to createPanel. The runtime supplies the parent native window handle to the module when available. After creation, Codex++ forwards parent window bounds, focus, visibility, fullscreen, minimize, and close events to optional native methods such as syncParent(state), parentChanged(state), setParentBounds(bounds, state), and parentBoundsChanged(bounds, state). The tweak manifest must declare native-view.

NativePanelRef

interface NativePanelRef {
  id: string;
  windowId: number | null;
  setBounds(bounds: { x: number; y: number; width: number; height: number }): Promise<void>;
  show(): Promise<void>;
  hide(): Promise<void>;
  dispose(): Promise<void>;
}

NativeViewAttachOptions

interface NativeViewAttachOptions {
  moduleId?: string;
  factory?: string;
  parentWindowId: number;
  bounds: { x: number; y: number; width: number; height: number };
  zIndex?: number;
}

When moduleId is omitted, Codex++ uses its built-in native host and defaults factory to attachView. The 1.0.0 built-in host uses a child-window overlay with an MTKView; direct child NSView insertion is reported separately by capabilities.native.directViewAttach.

NativeViewRef

interface NativeViewRef {
  id: string;
  setBounds(bounds: { x: number; y: number; width: number; height: number }): Promise<void>;
  setVisible(visible: boolean): Promise<void>;
  dispose(): Promise<void>;
}

NativeHelperLaunchOptions

interface NativeHelperLaunchOptions {
  id: string;
  executable: string;
  args?: string[];
  env?: Record<string, string>;
  transport?: "stdio" | "unix-socket";
  restart?: "never" | "on-crash";
}

Helpers are a fallback/native-service path. In-process native modules are the primary Swift/Metal bridge for 1.0.0. The tweak manifest must declare native-helper. In 1.0.0, helper transport must be stdio and restart policy must be never; unsupported values fail explicitly.

NativeHelperRef

interface NativeHelperRef {
  id: string;
  pid: number;
  send(message: unknown): Promise<void>;
  request(message: unknown, timeoutMs?: number): Promise<unknown>;
  stop(): Promise<void>;
}

CodexNativeApi

interface CodexNativeApi {
  loadModule(options: NativeModuleLoadOptions): Promise<NativeModuleRef>;
  createPanel(options: NativePanelCreateOptions): Promise<NativePanelRef>;
  attachView(options: NativeViewAttachOptions): Promise<NativeViewRef>;
  launchHelper(options: NativeHelperLaunchOptions): Promise<NativeHelperRef>;
}

Native APIs are main-process backed. Renderer tweaks call them through Codex++ IPC. Local/dev tweaks may load unsigned native code from their own directory; store-distributed native tweaks require stricter review. Native module, view, and helper permissions are enforced at runtime.

CodexCreateWindowOptions

interface CodexCreateWindowOptions {
  route: string;
  hostId?: string;
  show?: boolean;
  appearance?: string;
  parentWindowId?: number;
  bounds?: { x: number; y: number; width: number; height: number };
}

Fields:

Field Notes
route Required absolute Codex app route, for example / or /local/<conversation-id>. Must start with / and must not include a protocol/control characters.
hostId Defaults to local.
show Defaults to true.
appearance Defaults to secondary. Passed through to Codex window services.
parentWindowId Optional Electron BrowserWindow id. Defaults to focused window when available.
bounds Optional screen coordinates.

Example:

const ref = await api.codex.createWindow({
  route: "/",
  hostId: "local",
  show: true,
  bounds: { x: 100, y: 100, width: 900, height: 700 },
});
api.log.info(ref.windowId, ref.webContentsId);

CodexCreateViewOptions

interface CodexCreateViewOptions {
  route: string;
  hostId?: string;
  appearance?: string;
}

Creates an Electron BrowserView registered with Codex's host context. The return type is unknown so renderer-only bundles do not need Electron types. Use only from main-process code that understands Electron.

CodexWindowRef

interface CodexWindowRef {
  windowId: number;
  webContentsId: number;
}

Returned by createWindow.