A local-first AI automation studio that lives entirely in a browser tab. No backend, no API keys, no accounts, no bill.
Most AI tools rely on cloud APIs, resulting in latency, cost, and privacy risks. ORANGE executes 100% client-side. It loads Qwen2.5-0.5B directly into the browser using WebGPU (with WASM fallback via Transformers.js).
A dedicated Web Worker (`engine.html`) isolates the heavy inference compute from the main UI thread, communicating via `postMessage` to keep the interface buttery smooth.
The full ORANGE UI: brutalist canvas, live WebGPU metrics, and deterministic DAG rendering.
The main thread sends user prompts to the engine frame. The frame streams back progress updates, metrics, and the final compiled JSON DAG.
// main.js - Sending prompt to Engine Frame
const engineFrame = document.createElement('iframe');
engineFrame.src = './engine.html';
engineFrame.contentWindow.postMessage({
type: 'COMPILE',
payload: prompt
}, '*');
// Listening for the generated DAG
window.addEventListener('message', (e) => {
const { type, payload } = e.data;
if (type === 'SUCCESS') {
updateRailMetrics(payload.metrics, 'success');
renderFlow(payload.flow); // Render DAG
}
});
The LLM generating the JSON DAG token-by-token.
ORANGE is an interpreter, not a code executor. The LLM is strictly forced to output a typed JSON DAG (Flow). If the model hallucinates invalid nodes or breaks the schema, a deterministic Validator catches the error and triggers a self-repair loop.
export function validateFlow(flow) {
const errors = [];
if (!flow.nodes || flow.nodes.length === 0)
errors.push('Flow must have at least one node');
if (!flow.nodes[0] || flow.nodes[0].type !== 'trigger')
errors.push('Flow must start with a trigger node');
return errors;
}
export function repairFlow(flow, errors) {
if (errors.some(e => e.includes('trigger'))) {
flow.nodes.unshift({ id: 'node_trigger', type: 'trigger' });
}
return flow;
}
The engine NEVER executes model-written code. The runtime iterates through the DAG and only executes a strict, closed set of node types. Untrusted API data is rendered safely.
// Closed node set - interprets data, never executes code
const SUPPORTED_NODES = [
'trigger', 'http-get', 'transform',
'filter', 'output'
];
export async function runFlow(flow) {
for (let i = 0; i < flow.nodes.length; i++) {
const node = flow.nodes[i];
if (!SUPPORTED_NODES.includes(node.type)) {
results.errors.push(`Node ${node.type} is not implemented`);
continue;
}
// ... deterministic switch statement ...
}
}
To prevent the AI from making malicious requests, the `http-get` node is strictly restricted to a deterministic allowlist of CORS-enabled, keyless public APIs.
const API_ENDPOINTS = {
'open-meteo': 'https://api.open-meteo.com/v1/forecast',
'hacker-news': 'https://hacker-news.firebaseio.com/v0',
'frankfurter': 'https://api.frankfurter.app/latest',
'wikipedia': 'https://en.wikipedia.org/w/api.php'
};
async function executeHttpGet(params) {
const isAllowed = Object.values(API_ENDPOINTS).some(endpoint =>
params.url.startsWith(endpoint)
);
if (!isAllowed) {
return { status: 'error', error: 'URL not in allowed keyless API list.' };
}
// ... safe fetch ...
}
ORANGE features a `Ctrl+K` command palette for lightning-fast navigation, mimicking a native OS experience directly in the browser.
Workflows can be shared instantly without a database. The current DAG is stringified, base64-encoded, and appended to the URL hash (`#flow=...`). On load, the app decodes it and re-renders the canvas.
function updateShareURL() {
if (!currentFlow) return;
try {
const jsonStr = JSON.stringify(currentFlow);
const encoded = btoa(encodeURIComponent(jsonStr));
window.history.replaceState(
{}, '',
`${window.location.origin}${window.location.pathname}#flow=${encoded}`
);
} catch (e) {
console.warn('Share URL encode failed', e);
}
}
The clean initial state showing the default Trigger -> Fetch -> Output DAG.
The post-compile state showing the generated weather flow and execution metrics.