# Caged Code – Vanilla Claude Code Binary in a Webpage

> Source: <https://tomlarkworthy.github.io/lopebooks/notebooks/Caged_Code.html>
> Published: 2026-08-13 20:54:42+00:00

The vanilla Claude Code binary hosted in a webpage, hardwired to a notebook environment. This is not endorsed by Anthropic in any way. Use your existing Anthropic account through /login with token copying, or use OpenRouter API endpoints. Defaults to our OpenRouter demo endpoint, restricted to MiMo models only.

Live values of variables being observed by Claude Code.

Bridges a lopecode notebook to Claude Code via WebSocket.

```
Claude Code CLI  ←MCP→  Channel Server  ←WS→  This Module (browser)
```

**Cell groups** (in dependency order):

`cc_chat`

(main interface), `cc_watch_table`

(variable inspector)`cc_config`

, `cc_notebook_id`

, `cc_activity`

, `cc_status`

, `cc_messages`

, `cc_watches`

, `cc_module`

, `voiceEnabled`

`cc_find_module`

resolves module names via `runtime.mains`

`cc_serialize_value`

(safe value→string for transport)`cc_watchers`

observes variables, debounces & sends updates`cc_handle_*`

(one per MCP tool)`cc_command_handlers`

routes action→handler`cc_ws`

manages connect/pair/auto-reconnect and message dispatch`cc_change_forwarder`

streams cell edits, `cc_voice`

optional speech I/O`currentModules`

, `exportToHTML`

, `compile`

, `history`

, runtime-sdk helpersReactive `Inputs.input()`

cells holding connection state. These are the shared mutable atoms that UI and WebSocket cells read/write.

`cc_find_module`

resolves a module name (e.g. `"@tomlarkworthy/exporter-3"`

) to a runtime Module object by polling `viewof currentModules.value`

at call time (not reactively). `cc_serialize_value`

safely converts any runtime value to a truncated string for transport.

Subscribes to Observable variables via `observe()`

, debounces updates (1s), and forwards changes to Claude Code over the WebSocket. Also maintains the `cc_watches`

table UI.

One handler per MCP tool. Each receives a `cmd`

object and the Observable `runtime`

, returns `{ok, result}`

or `{ok:false, error}`

. Handlers: get-variable, define-variable, define-cell, delete-variable, list-variables, list-cells, run-tests, create-module, delete-module, eval, fork, watch, unwatch.

`cc_command_handlers`

maps action strings to handler functions. `cc_ws`

manages the WebSocket lifecycle: connect, pair with token, dispatch incoming messages (commands, replies, tool-activity), and auto-reconnect from the URL hash `cc=`

param or sessionStorage.

`cc_change_forwarder`

polls the local-change-history and streams new cell edits to Claude Code. `cc_voice`

provides optional speech recognition input and TTS output for hands-free pairing.

Serialize literate computational notebooks with their dependancies into single [downloadable](#) files. Double click to open locally. No server required, works in a `file://`

context for simplicity.

`Inputs`

, `htl`

, `highlight`

, `_`

(lodash) and `markdown`

are bundled for offline operation.Exporter improves upon [exporter 2](https://observablehq.com/@tomlarkworthy/exporter-2) by refactoring out Observable Javascript concepts, it works on the low level reprentation directly.

To put the exporter in one of your notebooks, first import the UI builder.

``` js
import {exporter, forkAnchor, exportAnchor } from '@tomlarkworthy/exporter'
```

Then call the builder to make the UI. You don't need to pass any options, but the options is where you can customise the output format.

``` js
exporter({
  handler: (action, state) => {}, // Optional UI click handler
  style: undefined,// customer reference to a style DOM node or a string to insert as a style block
  output: (out) => {}, // hook to get result of exporting
  notebook_url: undefined,// hardcode the default notebook_url
})
```

If you want to export without a UI, use the function `exportToHTML`

, see the [example](https://observablehq.com/@tomlarkworthy/export-to-html-example)

``` js
import {exportToHTML } from '@tomlarkworthy/exporter'
php
async function exportToHTML({
  mains = new Map(), // (name -> module) Map of main modules
  runtime = _runtime,
  options = {} // Object, export options, e.g. head, title
} = {})
```

You can also just use inline anchor tags: [forkAnchor()](#) or [downloadAnchor()](#)

The HTML file contains `<script>`

blocks that hold content to serve internal network requests locally.

```
<script id="d/c2dae147641e012a@46" 
        type="text/plain"
        data-encoding="base64+gzip"
        data-mime="application/javascript"
>
...inline text or base64 string
</script>
```

Requests to the URL matching the `id`

are served locally, this includes `import`

, `fetch`

, `XMLHttpRequest`

and `<script>`

src attribute.

The main script loads the Observable runtime with no standard library and loads a bootloader module. The bootloader is responsible for setting up the standard library and loading the first real modules, which it discovers by reading the `bootconf.json`

. `@tomlarkworthy/bootloader`

is the default which comes with d3, Plot, md, htl and lodash local.

To help carry state across an export, the URL hash parameter is remembered in the `bootconf.json`

and set automatically when opening the file if one is not present. URLs are limited in size, ff you need to move large amount of data across an export, use a [local FileAttachment](https://observablehq.com/@tomlarkworthy/fileattachments) instead.

Themes are sourced from NotebookKit. A theme is fetched *once* from Github once when switching themes, but integrated into the export, and reused locally on subsequent exports. This keeps the bundle small, and you only need a network connection if switching to obtain the new CSS source files.

Serialize a single module from the live runtime to a `.js`

module source string. Unlike `module_specs`

(which serializes all modules as part of the full HTML export pipeline), `exportModuleJS`

works on-demand against the live runtime with no `task`

dependency.

``` js
import {exportModuleJS} from "@tomlarkworthy/exporter-3"
const {source, fileAttachments} = await exportModuleJS(moduleId, {runtime, moduleNamesFn})
```

**Parameters**

`moduleId`

— module name string, e.g. `"@tomlarkworthy/flow-queue"`

`options.runtime`

— Observable runtime instance (default: `_runtime`

)`options.moduleNamesFn`

— function to build module name map (default: `buildModuleNames`

)**Returns** `{source, fileAttachments}`

`source`

— full `.js`

module source with `export default function define(runtime, observer)`

`fileAttachments`

— `Map<name, {url, mimeType}>`

of the module's file attachments`()`

Figures out the import structure of a runtime, just pass a runtime to the function `moduleMap`

to get a summary of the modules. Returns a map indexed by a Module object to a record.

``` php
module -> {
  type: "notebook import" | "module variable",
  name: <module name>,
  module: <module object ref>
  dependsOn: [<module name>*],
  dependedBy: [<module name>*],
}
```

`currentModules`

`()`

We resolve what we can using variables named with prefix `module`

that hold module values. We `forcePeek`

the variables to make them resolve, which forces loading of the modules.

modules imported via notebook imports do not have module variables, so they are trickier to figure out. We can sniff the page DOM to find the import expressions, and try to map them to the modules we could to resolve earlier
