Module: LittleGhost::CodeMode::Javascript::Host
- Defined in:
- lib/little_ghost/code_mode/javascript/host.rb
Overview
:nodoc: all
Defined Under Namespace
Constant Summary collapse
- MAX_ACTIVE_PROGRAMS =
8- MAX_SOURCE_BYTES =
1024 * 1024
- CONTEXT_MEMORY_BYTES =
64 * 1024 * 1024
- JAVASCRIPT_TIMEOUT_MS =
10_000- MAX_PENDING_TOOL_CALLS =
1_024- Terminated =
Class.new(StandardError)
- BOOTSTRAP =
<<~'JAVASCRIPT' const __LITTLE_GHOST_CONTROL_IDENTIFIER__ = (() => { "use strict"; const definitions = __LITTLE_GHOST_TOOL_DEFINITIONS__; const definitionIndex = Object.fromEntries(definitions.map((definition) => [definition.name, definition])); const calls = []; const outputs = []; const pending = new Map(); const exitSignal = Object.freeze({exit: true}); let nextCallId = 0; let done = false; let failure = null; const stringify = (value) => { if (typeof value === "string") return value; if (value === undefined) return "undefined"; if (typeof value === "bigint") return value.toString(); const encoded = JSON.stringify(value); return encoded === undefined ? String(value) : encoded; }; const enqueue = (name, args) => new Promise((resolve, reject) => { if (!Object.prototype.hasOwnProperty.call(definitionIndex, name)) { reject(new Error(`Unknown tool: ${name}`)); return; } const id = String(++nextCallId); pending.set(id, {resolve, reject}); calls.push({call_id: id, name, arguments: args === undefined ? {} : args}); }); const tools = new Proxy(Object.freeze(Object.create(null)), { get(_target, property) { if (property === "then") return undefined; if (typeof property !== "string") return undefined; return (args = {}) => enqueue(property, args); } }); const catalog = definitions.map(({name, description}) => Object.freeze({name, description})); const text = (value) => { outputs.push(stringify(value)); }; const exit = () => { throw exitSignal; }; const errorText = (error) => { if (error && typeof error.stack === "string") return error.stack; if (error && typeof error.message === "string") return error.message; return stringify(error); }; Object.defineProperties(globalThis, { tools: {value: tools, writable: false, configurable: false}, ALL_TOOLS: {value: Object.freeze(catalog), writable: false, configurable: false}, text: {value: text, writable: false, configurable: false}, exit: {value: exit, writable: false, configurable: false}, console: {value: undefined, writable: false, configurable: false}, process: {value: undefined, writable: false, configurable: false}, require: {value: undefined, writable: false, configurable: false}, fetch: {value: undefined, writable: false, configurable: false}, WebAssembly: {value: undefined, writable: false, configurable: false}, ArrayBuffer: {value: undefined, writable: false, configurable: false}, SharedArrayBuffer: {value: undefined, writable: false, configurable: false}, DataView: {value: undefined, writable: false, configurable: false}, Atomics: {value: undefined, writable: false, configurable: false}, Int8Array: {value: undefined, writable: false, configurable: false}, Uint8Array: {value: undefined, writable: false, configurable: false}, Uint8ClampedArray: {value: undefined, writable: false, configurable: false}, Int16Array: {value: undefined, writable: false, configurable: false}, Uint16Array: {value: undefined, writable: false, configurable: false}, Int32Array: {value: undefined, writable: false, configurable: false}, Uint32Array: {value: undefined, writable: false, configurable: false}, BigInt64Array: {value: undefined, writable: false, configurable: false}, BigUint64Array: {value: undefined, writable: false, configurable: false}, Float32Array: {value: undefined, writable: false, configurable: false}, Float64Array: {value: undefined, writable: false, configurable: false} }); return Object.freeze({ drain: () => ({ calls: calls.splice(0), outputs: outputs.splice(0), done, failure }), resolve: (id, ok, value) => { const continuation = pending.get(String(id)); if (!continuation) return false; pending.delete(String(id)); if (ok) continuation.resolve(value); else continuation.reject(new Error(String(value))); return true; }, run: (source) => { let execution; try { execution = (0, eval)(`(async () => {\n${source}\n})()`); } catch (error) { done = true; failure = errorText(error); return; } Promise.resolve(execution).then( () => { done = true; }, (error) => { done = true; failure = error === exitSignal ? null : errorText(error); } ); }, }); })(); JAVASCRIPT