Download runtime/workflow.mjs from candypunk/NanoJev-Web: direct link, hf CLI and curl.
- Browser
- Download file 3.71 kB
-
https://huggingface.co/candypunk/NanoJev-Web/resolve/main/runtime/workflow.mjs
- Command line
-
hf download hf://candypunk/NanoJev-Web/runtime/workflow.mjs
-
curl -L -o workflow.mjs https://huggingface.co/candypunk/NanoJev-Web/resolve/main/runtime/workflow.mjs
3.71 kB
| // Compile a small, explicit workflow. Natural-language planning stays with the host. | |
| const FIELD_OPS = new Set(['fill', 'select', 'check']); | |
| const target = value => typeof value === 'string' ? {name: value} : value; | |
| export function validateTarget(value) { | |
| const t = target(value); | |
| if (!t || typeof t !== 'object' || Array.isArray(t) || | |
| !(typeof t.name === 'string' && t.name.trim() || typeof t.selector === 'string' && t.selector.trim())) | |
| throw Error('A target needs an exact accessible name or selector'); | |
| for (const key of ['role', 'within']) if (t[key] !== undefined && typeof t[key] !== 'string') throw Error('Invalid target ' + key); | |
| return t; | |
| } | |
| export class Workflow { | |
| constructor(task) { | |
| if (!Array.isArray(task.steps) || !task.steps.length || task.steps.length > 100) throw Error('Expected 1–100 workflow steps'); | |
| this.steps = task.steps.map((step, index) => { | |
| const ops = Object.keys(step).filter(k => ['click', ...FIELD_OPS].includes(k)); | |
| if (ops.length !== 1 || Object.keys(step).some(k => ![ops[0], 'purpose', 'expect'].includes(k))) throw Error('Each step needs exactly one supported operation'); | |
| const op = ops[0], source = step[op]; | |
| if (op === 'click') { | |
| const t = validateTarget(source); | |
| return [{id: `step-${index}-click`, op, target: t, label: t.name ?? t.selector, | |
| purpose: step.purpose ?? (index === task.steps.length - 1 ? 'submit' : 'interaction'), expect: step.expect}]; | |
| } | |
| if (!source || typeof source !== 'object' || !Object.keys(source).length) throw Error('Field step needs a nonempty mapping or target/value list'); | |
| const entries = Array.isArray(source) ? source : Object.entries(source).map(([name, value]) => ({target: {name}, value})); | |
| return entries.map(({target: suppliedTarget, value}, i) => { | |
| const t = validateTarget(suppliedTarget); | |
| if (op === 'check' ? typeof value !== 'boolean' : !(typeof value === 'string' || typeof value === 'number' || op === 'select' && Array.isArray(value) && value.every(x => typeof x === 'string'))) throw Error('Invalid assigned field value'); | |
| return {id: `step-${index}-field-${i}`, op: op === 'check' ? value ? 'check' : 'uncheck' : op, | |
| target: t, label: t.name ?? t.selector, value, purpose: 'input', selectByLabel: op === 'select'}; | |
| }); | |
| }); | |
| this.index = 0; | |
| this.completed = []; | |
| } | |
| get finished() { return this.index >= this.steps.length; } | |
| controls() { return this.finished ? [] : this.steps[this.index]; } | |
| advance(observation) { | |
| if (this.finished || this.controls()[0].op === 'click') return false; | |
| const controls = observation.dom.controls; | |
| if (controls.length !== this.controls().length || !controls.every(c => c.count === 1 && c.current === 'match')) return false; | |
| this.completed.push({step: this.index + 1, operation: 'fields', fields: controls.map(c => ({name: c.label, matched: true}))}); | |
| this.index++; | |
| return true; | |
| } | |
| acted(action) { | |
| if (!this.finished && action.op === 'click' && this.controls()[0].op === 'click') { | |
| this.completed.push({step: this.index + 1, operation: 'click', name: action.label, executed: true}); | |
| this.index++; | |
| } | |
| } | |
| } | |
| export function workflowSuccess(expect) { | |
| if (!expect || typeof expect !== 'object') throw Error('An explicit final expectation is required'); | |
| if (typeof expect.visibleText === 'string' && expect.visibleText.trim()) return {kind: 'visibleText', value: expect.visibleText}; | |
| if (expect.target) return {kind: 'visible', target: validateTarget(expect.target)}; | |
| if (expect.kind) return expect; | |
| throw Error('Use expect.visibleText, expect.target or a documented condition'); | |
| } | |