@finamx/repl
v0.2.1
Published
Browser REPL runtime package for FinamX host surfaces.
Readme
@finamx/repl
Browser REPL runtime package for FinamX host surfaces.
JSRepl
JSRepl executes synchronous JavaScript in a Web Worker. The worker uses ShadowRealm when available and falls back to a restricted Function evaluator when ShadowRealm is missing.
import { JSRepl } from '@finamx/repl';
const repl = new JSRepl();
const result = await repl.execute('mean(prices)', { prices: [10, 20, 30] });Available helpers:
sum(arr),mean(arr),std(arr),pctile(arr, p),cov(a, b),corr(a, b)returns(prices),sharpe(rets, rf?),maxDrawdown(prices),ema(arr, period)
The default timeout is 5000ms. On timeout the current worker is terminated and the next execution creates a fresh worker.
Persistent Realm State
The JS runtime keeps one realm per worker session. This is intentional for Phase 62 D-11 and means state can persist between calls:
await repl.execute('globalThis.__x = 42');
await repl.execute('globalThis.__x'); // 42This persistence is useful for notebook-style flows, but it can leak state between agent/user executions in the same host session. Use dispose() when switching trust boundaries, users, widgets, or agent runs. A timeout also terminates the worker, so the next call starts with a fresh realm.
Sandbox Boundary
User code is synchronous and must return JSON-serializable values. Network and DOM globals are not provided to the sandbox. Code that references fetch, window, or document fails with a reference error.
The Firefox fallback does not evaluate against the raw worker globalThis; it runs with a restricted global proxy containing only math/JSON primitives and FinamX finance helpers.
Third-Party Widget Registration
Reference path for REPL-capable widgets:
- Register an extension manifest with
capabilitiesRequested: ['repl.execute', 'market.read']. - Grant the install the same scopes so Launch includes
repl.executeingrantedScopes. - Subscribe to market context such as
fdc3.instrument, then callbridge.repl.js(code, { contextKeys: ['prices'] }). - Host local invoke resolves
contextKeysfrom PubSub/LVC before dispatching to the browser REPL runtime.
The concrete seeds are:
cms/src/seed/data/extensions/extension-template-repl-risk-calc.json→/widgets/repl-risk-calc(JS)cms/src/seed/data/extensions/extension-template-repl-notebook.json→/widgets/repl-notebook(Python)
Host-app board smoke recipe: hosts/conduit/docs/REPL-SHOWCASE.md.
Agent/RLM surfaces should use the shared dispatcher from Phase 62-05 (dispatchReplToolCall) so Demo REPL in the canvas agent panel and widget invocations exercise the same runtime contract.
PyRepl
PyRepl executes Python in a dedicated Pyodide Worker. It is intended for heavier notebook-style finance workloads where numpy/scipy are useful and JSRepl would be too limited.
import { PyRepl } from '@finamx/repl';
const repl = new PyRepl();
await repl.preload();
const result = await repl.execute(
'print("hello")\nfloat(np.mean(prices))',
{ prices: [10, 20, 30] },
{ packages: ['pandas'], timeout: 30000 },
);The default timeout is 30000ms. On timeout the current Pyodide worker is terminated, pending execution returns ExecutionTimeout, and the next call starts a fresh worker. Results always include stdout and stderr fields so agents can rely on printed output.
Pyodide CDN And CSP
Pyodide is pinned to jsDelivr 0.27.x:
- loader:
https://cdn.jsdelivr.net/pyodide/v0.27.0/full/pyodide.js - package base:
https://cdn.jsdelivr.net/pyodide/v0.27.0/full/
Host CSP must allow the worker script and Pyodide assets:
script-src 'self' https://cdn.jsdelivr.net;
worker-src 'self' blob:;
connect-src 'self' https://cdn.jsdelivr.net;If the host serves compiled worker files from a separate asset domain, include that domain in worker-src as well. A Service Worker cache can pre-cache the 0.27.0 Pyodide assets to reduce cold starts, but package loading should still be limited to Pyodide CDN assets requested through packages[].
Python No-Network DoD
The Python execution path does not expose host fetch, DOM APIs, or arbitrary URL loaders to user code. packages[] is the only supported lazy-load channel and is handled by pyodide.loadPackage() against the pinned Pyodide CDN.
D-14 acceptance: await repl.execute('import requests') must return a normal ReplResult with result: null and error containing ModuleNotFoundError or an equivalent network-denial message. It must not perform an arbitrary outbound request from user Python code.
