Browser integration

Browser adapters connect the fx WebAssembly SDK to storage, authentication, command execution, and network requests. Add only the adapters your app needs; adapter methods may be synchronous or asynchronous. createFxTerminal() supports every option on this page, while createFxAgent() supports configStore, sessionStore, and fetch.

Choose an adapter

OptionUsed byPurpose
configStoreBothRestore accepted model and mode settings.
promptHistoryStoreTerminalPreserve terminal input history.
sessionStoreBothPersist conversations.
openUrl and oauthSessionStoreTerminalComplete browser device login.
workspaceTerminalAdd a foreground run_command tool.
fetchBothControl network requests.

Config store

Use configStore to restore settings such as the active model and mode:

const configStore = {
  get(id) {
    return localStorage.getItem(`fx.core.config.${id}`)
  },
  set(id, value) {
    localStorage.setItem(`fx.core.config.${id}`, value)
  },
}

get(id) returns a string or null. fx calls set(id, value) only after it accepts the new value.

Prompt history store

Use promptHistoryStore to preserve terminal input history across page loads:

type PromptHistoryStore = {
  load(
    workspaceRoot: string,
    limit: number,
  ): string[] | Promise<string[]>
  append(
    workspaceRoot: string,
    value: string,
    timestampMs: number,
  ):
    | void
    | 'duplicate'
    | 'record_too_large'
    | Promise<void | 'duplicate' | 'record_too_large'>
  clear(workspaceRoot: string): void | Promise<void>
}

load() returns prompts from oldest to newest. Return duplicate when the latest stored prompt already matches, or record_too_large when the app declines to store it.

Session store

Use sessionStore to persist conversations. Pass args: ['--resume', 'last'] to createFxTerminal() to resume the conversation with the latest updatedAtMs value.

type SessionStore = {
  load(id: string):
    | { bytes: Uint8Array; revision: string }
    | null
    | Promise<{ bytes: Uint8Array; revision: string } | null>
  commit(
    id: string,
    bytes: Uint8Array,
    expectedRevision: string | undefined,
  ): { revision: string } | Promise<{ revision: string }>
  list():
    | Array<{ id: string; updatedAtMs: number }>
    | Promise<Array<{ id: string; updatedAtMs: number }>>
  remove(id: string): void | Promise<void>
}

Treat bytes as opaque data. commit() receives undefined for the first revision and must return a new revision after every write.

If expectedRevision is stale, throw an error whose code is FX_SESSION_REVISION_CONFLICT. The conflict prevents the stale snapshot from overwriting the current record. createFxAgent() uses an in-memory session store when you do not provide one.

Device login

createFxTerminal() supports browser device login through openUrl and oauthSessionStore:

const terminal = await createFxTerminal({
  wasm: './fx-term.wasm',
  terminal: terminalAdapter,
  openUrl(url) {
    return window.open(url, '_blank', 'noopener,noreferrer') !== null
  },
  oauthSessionStore,
})

Return false from openUrl() when the app cannot open the verification page. fx still prints the URL and verification code in the terminal.

The OAuth store uses the following contract:

type OAuthSessionStore = {
  load():
    | { bytes: Uint8Array; revision: string }
    | null
    | Promise<{ bytes: Uint8Array; revision: string } | null>
  commit(
    bytes: Uint8Array,
    expectedRevision: string | undefined,
  ): { revision: string } | Promise<{ revision: string }>
  remove(expectedRevision: string | undefined):
    | void
    | boolean
    | 'missing'
    | Promise<void | boolean | 'missing'>
}

Store the OAuth bytes without inspecting or logging them. Return false or missing when there is no record to remove. Reject a stale commit or removal with FX_OAUTH_SESSION_REVISION_CONFLICT.

Browser workspace

The optional workspace adapter adds a foreground run_command tool implemented by your app:

type WorkspaceAdapter = {
  info: {
    version: 1
    root: string
    cwd: string
    home: string
    gitAvailable: false
    ephemeral: true
  }
  permission: 'allow-sandboxed' | 'prompt'
  exec(input: {
    command: string
    cwd: string
    signal: AbortSignal
    timeoutMs: number
    outputLimitBytes: number
  }):
    | { stdout: string; stderr: string; exitCode: number }
    | Promise<{ stdout: string; stderr: string; exitCode: number }>
}

root, cwd, and home must be normalized absolute paths without NUL bytes. Version 1 also requires:

  • cwd equals root.
  • gitAvailable is false.
  • ephemeral is true.
  • permission is allow-sandboxed or prompt.

exec() returns { stdout, stderr, exitCode }. Commands are limited to 64 KiB of UTF-8, combined output previews to 64 KiB, and execution to 30 seconds. The adapter must stop work when its AbortSignal is cancelled. Without a workspace adapter, the WebAssembly terminal does not expose run_command.

The try page implements this adapter with an in-memory bash environment.

Network requests

The SDK uses globalThis.fetch by default. Pass a fetch function to createFxTerminal() or createFxAgent() when your app needs to proxy requests, add authentication, or apply its own network policy. Browser Cross-Origin Resource Sharing (CORS) and Content Security Policy (CSP) rules still apply.