yaml-flow 8.0.0 → 8.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/asset-integrity.json +2 -2
- package/browser/board-livecards-localstorage.js +5 -5
- package/cli/browser-api/board-live-cards-browser-adapter.d.ts +2 -2
- package/cli/{execution-interface-C_A6WCiK.d.ts → execution-interface-ftO1W7Po.d.ts} +4 -2
- package/cli/node/board-live-cards-cli.js +7 -7
- package/cli/node/card-store-cli.js +1 -1
- package/cli/node/execution-adapter.d.ts +47 -2
- package/cli/node/execution-adapter.js +2 -2
- package/cli/node/fs-board-adapter.d.ts +4 -4
- package/cli/node/fs-board-adapter.js +7 -7
- package/cli/node/step-machine-cli.js +4 -4
- package/cli/{types-CziUxkiv.d.ts → types-C2YQXFwo.d.ts} +1 -1
- package/examples/ARCHITECTURE.md +188 -0
- package/examples/board/demo-shell-with-server.html +2 -2
- package/examples/board-local/demo-shell-localstorage.html +3 -3
- package/examples/step-machine-cli/json/double-handler.js +20 -0
- package/examples/step-machine-cli/json/mixed-handlers.flow.json +118 -0
- package/examples/step-machine-cli/json/mixed-handlers.input.json +5 -0
- package/examples/step-machine-cli/json/scale-handler.js +13 -0
- package/examples/step-machine-cli/yaml/double-handler.js +20 -0
- package/examples/step-machine-cli/yaml/mixed-handlers.flow.yaml +94 -0
- package/examples/step-machine-cli/yaml/mixed-handlers.input.json +5 -0
- package/examples/step-machine-cli/yaml/scale-handler.js +13 -0
- package/lib/board-live-cards-node.cjs +7 -7
- package/lib/board-live-cards-node.d.cts +46 -2
- package/lib/board-live-cards-node.d.ts +46 -2
- package/lib/board-live-cards-node.js +7 -7
- package/lib/board-live-cards-public.cjs +1 -1
- package/lib/board-live-cards-public.js +1 -1
- package/lib/board-live-cards-server-runtime.cjs +2 -2
- package/lib/board-live-cards-server-runtime.js +2 -2
- package/lib/execution-refs.cjs +1 -1
- package/lib/execution-refs.d.cts +4 -2
- package/lib/execution-refs.d.ts +4 -2
- package/lib/execution-refs.js +1 -1
- package/lib/server-runtime/index.cjs +2 -2
- package/lib/server-runtime/index.js +2 -2
- package/package.json +4 -4
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/gandalf-runtime/.config/card-store-ref.json +0 -1
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/gandalf-runtime/.config/chat-handler.json +0 -1
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/gandalf-runtime/.config/outputs-store-ref.json +0 -1
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/gandalf-runtime/.config/task-executor.json +0 -1
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/gandalf-runtime/.state-snapshot/board/graph.json +0 -29
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/gandalf-runtime/.state-snapshot/board/lastJournalProcessedId.json +0 -1
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/gandalf-runtime-out/.outputs/status.json +0 -25
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/runtime-out/.outputs/cards/card-market-prices/computed_values.json +0 -67
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/runtime-out/.outputs/cards/card-portfolio/computed_values.json +0 -1
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/runtime-out/.outputs/cards/card-portfolio-value/computed_values.json +0 -52
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/runtime-out/.outputs/data-objects/holdings.json +0 -22
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/runtime-out/.outputs/data-objects/positions.json +0 -46
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/runtime-out/.outputs/data-objects/quotes.json +0 -35
- package/examples/board/.demo-setup/run-1778643703151-3360-dopnpv/board-default/runtime-out/.outputs/status.json +0 -113
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import*as m from'fs';import {existsSync}from'fs';import*as R from'path';import {dirname,resolve}from'path';import'ajv-formats';import {createRequire}from'module';import {fileURLToPath}from'url';import {spawn,execFile,execFileSync}from'child_process';import*as St from'os';import'net';import {randomUUID}from'crypto';import'proper-lockfile';function I(e,t,r,n){let s=e.steps[r];if(!s)throw new Error(`Step "${r}" not found in flow configuration`);if(n.result==="failure"&&s.retry){let i=t.retryCounts[r]??0;if(i<s.retry.max_attempts)return {newState:{...t,retryCounts:{...t.retryCounts,[r]:i+1},updatedAt:Date.now()},nextStep:r,isTerminal:false,isCircuitBroken:false,shouldRetry:true}}let o=s.failure_transitions?.[n.result]??s.transitions[n.result];if(!o)throw new Error(`No transition defined for result "${n.result}" in step "${r}"`);let a=!!e.terminal_states[o];return {newState:{...t,currentStep:o,stepHistory:[...t.stepHistory,r],retryCounts:{...t.retryCounts,[r]:0},updatedAt:Date.now()},nextStep:o,isTerminal:a,isCircuitBroken:false,shouldRetry:false}}function N(e,t,r){let n=e.steps[r];if(!n?.circuit_breaker)return {broken:false,newState:{...t,iterationCounts:{...t.iterationCounts,[r]:(t.iterationCounts[r]??0)+1},updatedAt:Date.now()}};let s=t.iterationCounts[r]??0;return s>=n.circuit_breaker.max_iterations?{broken:true,redirectStep:n.circuit_breaker.on_open,newState:{...t,currentStep:n.circuit_breaker.on_open,updatedAt:Date.now()}}:{broken:false,newState:{...t,iterationCounts:{...t.iterationCounts,[r]:s+1},updatedAt:Date.now()}}}function L(e,t,r){let n=e.steps[t];if(!n)throw new Error(`Step "${t}" not found`);if(n.expects_data){let s={};for(let o of n.expects_data)s[o]=r[o];return s}return {...r}}function K(e,t){if(e===false||e===void 0)return {};if(typeof e=="string")return {[e]:t[e]};if(Array.isArray(e)){let r={};for(let n of e)r[n]=t[n];return r}return {}}function q(e,t){let r=Date.now();return {runId:t,flowId:e.id??"unnamed",currentStep:e.settings.start_step,status:"running",stepHistory:[],iterationCounts:{},retryCounts:{},startedAt:r,updatedAt:r}}var k=class{runs=new Map;data=new Map;async saveRunState(t,r){this.runs.set(t,{...r});}async loadRunState(t){let r=this.runs.get(t);return r?{...r}:null}async deleteRunState(t){this.runs.delete(t),this.data.delete(t);}async setData(t,r,n){this.data.has(t)||this.data.set(t,{});let s=this.data.get(t);s[r]=n;}async getData(t,r){return this.data.get(t)?.[r]}async getAllData(t){return {...this.data.get(t)??{}}}async clearData(t){this.data.delete(t);}async listRuns(){return Array.from(this.runs.keys())}clear(){this.runs.clear(),this.data.clear();}};function Ft(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return (e==="x"?t:t&3|8).toString(16)})}var O=class{flow;handlers;store;components;options;listeners=new Map;aborted=false;constructor(t,r,n={}){this.flow=t,this.handlers=new Map(Object.entries(r)),this.store=n.store??new k,this.components=n.components??{},this.options=n,n.signal&&n.signal.addEventListener("abort",()=>{this.aborted=true;}),this.validateFlow();}validateFlow(){let{settings:t,steps:r,terminal_states:n}=this.flow;if(!t?.start_step)throw new Error("Flow must have settings.start_step defined");if(!r||Object.keys(r).length===0)throw new Error("Flow must have at least one step defined");if(!n||Object.keys(n).length===0)throw new Error("Flow must have at least one terminal_state defined");if(!r[t.start_step]&&!n[t.start_step])throw new Error(`Start step "${t.start_step}" not found`);for(let[s,o]of Object.entries(r)){for(let[a,i]of Object.entries(o.transitions))if(!r[i]&&!n[i])throw new Error(`Step "${s}" transition "${a}" points to unknown step "${i}"`);if(o.failure_transitions){for(let[a,i]of Object.entries(o.failure_transitions))if(!r[i]&&!n[i])throw new Error(`Step "${s}" failure_transition "${a}" points to unknown step "${i}"`)}}}on(t,r){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(r);}off(t,r){this.listeners.get(t)?.delete(r);}emit(t){let r=this.listeners.get(t.type);if(r)for(let n of r)try{n(t);}catch{}}sleep(t){return new Promise(r=>setTimeout(r,t))}async run(t){let r=Ft(),n=q(this.flow,r);if(await this.store.saveRunState(r,n),t)for(let[s,o]of Object.entries(t))await this.store.setData(r,s,o);this.emit({type:"flow:start",runId:r,timestamp:n.startedAt,data:{initialData:t??{}}});try{return await this.executeLoop(r,n)}catch(s){let o=s instanceof Error?s:new Error(String(s));return this.emit({type:"flow:error",runId:r,timestamp:Date.now(),data:{error:o.message}}),this.options.onError?.(o),n={...n,status:"failed",updatedAt:Date.now()},await this.store.saveRunState(r,n),{runId:r,status:"failed",data:await this.store.getAllData(r),finalStep:n.currentStep,stepHistory:n.stepHistory,durationMs:Date.now()-n.startedAt,error:o}}}async resume(t){let r=await this.store.loadRunState(t);if(!r)throw new Error(`No run found with ID: ${t}`);if(r.status==="completed"||r.status==="failed")throw new Error(`Cannot resume a ${r.status} run`);let n={...r,status:"running",pausedAt:void 0,updatedAt:Date.now()};return await this.store.saveRunState(t,n),this.emit({type:"flow:resumed",runId:t,timestamp:Date.now(),data:{currentStep:n.currentStep}}),this.executeLoop(t,n)}async pause(t){let r=await this.store.loadRunState(t);if(!r)throw new Error(`No run found with ID: ${t}`);let n={...r,status:"paused",pausedAt:Date.now(),updatedAt:Date.now()};await this.store.saveRunState(t,n),this.emit({type:"flow:paused",runId:t,timestamp:Date.now(),data:{currentStep:n.currentStep}});}async executeLoop(t,r){let n=this.flow.settings.max_total_steps??100,s=this.flow.settings.timeout_ms,o=r,a=0;for(;a<n;){if(this.aborted)return o={...o,status:"cancelled",updatedAt:Date.now()},await this.store.saveRunState(t,o),{runId:t,status:"cancelled",data:await this.store.getAllData(t),finalStep:o.currentStep,stepHistory:o.stepHistory,durationMs:Date.now()-o.startedAt};if(s&&Date.now()-o.startedAt>s)return o={...o,status:"completed",updatedAt:Date.now()},await this.store.saveRunState(t,o),{runId:t,status:"timeout",intent:"timeout",data:await this.store.getAllData(t),finalStep:o.currentStep,stepHistory:o.stepHistory,durationMs:Date.now()-o.startedAt};let i=o.currentStep,l=this.flow.terminal_states[i];if(l){o={...o,status:"completed",updatedAt:Date.now()},await this.store.saveRunState(t,o);let p=await this.store.getAllData(t),g={runId:t,status:"completed",intent:l.return_intent,data:K(l.return_artifacts,p),finalStep:i,stepHistory:o.stepHistory,durationMs:Date.now()-o.startedAt};return this.emit({type:"flow:complete",runId:t,timestamp:Date.now(),data:{...g}}),this.options.onComplete?.(g),g}let c=N(this.flow,o,i);if(c.broken){o=c.newState,await this.store.saveRunState(t,o),a++;continue}o=c.newState;let d=await this.store.getAllData(t),u=L(this.flow,i,d),f={runId:t,stepName:i,components:this.components,store:this.store,signal:this.options.signal,emit:(p,g)=>{this.emit({type:"step:complete",runId:t,timestamp:Date.now(),data:{event:p,payload:g}});}};this.emit({type:"step:start",runId:t,timestamp:Date.now(),data:{step:i,input:u}});let h;try{let p=this.handlers.get(i);if(!p)throw new Error(`No handler registered for step "${i}"`);h=await p(u,f);}catch(p){let g=p instanceof Error?p:new Error(String(p));this.emit({type:"step:error",runId:t,timestamp:Date.now(),data:{step:i,error:g.message}}),h={result:"failure",data:{error:g.message}};}if(h.data)for(let[p,g]of Object.entries(h.data))await this.store.setData(t,p,g);this.emit({type:"step:complete",runId:t,timestamp:Date.now(),data:{step:i,result:h.result}}),this.options.onStep?.(i,h);let w=I(this.flow,o,i,h);if(o=w.newState,w.shouldRetry){await this.store.saveRunState(t,o);let p=this.flow.steps[i];if(p.retry?.delay_ms){let g=o.retryCounts[i]??0,x=p.retry.backoff_multiplier?p.retry.delay_ms*Math.pow(p.retry.backoff_multiplier,g-1):p.retry.delay_ms;await this.sleep(x);}a++;continue}await this.store.saveRunState(t,o),this.emit({type:"transition",runId:t,timestamp:Date.now(),data:{from:i,to:o.currentStep,result:h.result}}),this.options.onTransition?.(i,o.currentStep),a++;}return o={...o,status:"completed",updatedAt:Date.now()},await this.store.saveRunState(t,o),{runId:t,status:"max_iterations",intent:"max_iterations",data:await this.store.getAllData(t),finalStep:o.currentStep,stepHistory:o.stepHistory,durationMs:Date.now()-o.startedAt}}};function _(e,t,r){return new O(e,t,r)}async function U(e){return (await import('yaml')).parse(e)}async function Tt(e){let t=await fetch(e);if(!t.ok)throw new Error(`Failed to load flow from ${e}: ${t.statusText}`);let r=t.headers.get("content-type")??"",n=await t.text();return r.includes("json")||e.endsWith(".json")?JSON.parse(n):U(n)}async function Pt(e){let r=await(await import('fs/promises')).readFile(e,"utf-8");return e.endsWith(".json")?JSON.parse(r):U(r)}function tt(e){let t=[];if(!e||typeof e!="object")return ["Flow must be an object"];let r=e;if(!r.settings||typeof r.settings!="object"?t.push('Flow must have a "settings" object'):typeof r.settings.start_step!="string"&&t.push("settings.start_step must be a string"),!r.steps||typeof r.steps!="object")t.push('Flow must have a "steps" object');else {let n=r.steps;for(let[s,o]of Object.entries(n)){if(!o||typeof o!="object"){t.push(`Step "${s}" must be an object`);continue}let a=o;(!a.transitions||typeof a.transitions!="object")&&t.push(`Step "${s}" must have a "transitions" object`),a.failure_transitions!==void 0&&typeof a.failure_transitions!="object"&&t.push(`Step "${s}" failure_transitions must be an object when provided`);}}if(!r.terminal_states||typeof r.terminal_states!="object")t.push('Flow must have a "terminal_states" object');else {let n=r.terminal_states;for(let[s,o]of Object.entries(n)){if(!o||typeof o!="object"){t.push(`Terminal state "${s}" must be an object`);continue}typeof o.return_intent!="string"&&t.push(`Terminal state "${s}" must have a "return_intent" string`);}}return t}async function H(e){let t;typeof e=="string"?e.startsWith("http://")||e.startsWith("https://")?t=await Tt(e):e.includes("{")?t=JSON.parse(e):t=await Pt(e):t=e;let r=tt(t);if(r.length>0)throw new Error(`Invalid step flow configuration:
|
|
2
|
-
- ${
|
|
3
|
-
- `)}`);return t}function V(e){return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function et(e){return atob(e.replace(/-/g,"+").replace(/_/g,"/"))}var j=class{constructor(t){this.kv=t;}kv;stateKey(t){return `state_${V(t)}`}dataPrefix(t){return `data_${V(t)}_`}dataKey(t,r){return `${this.dataPrefix(t)}${V(r)}`}async saveRunState(t,r){this.kv.write(this.stateKey(t),r);}async loadRunState(t){let r=this.kv.read(this.stateKey(t));return r!=null&&typeof r=="object"?r:null}async deleteRunState(t){this.kv.delete(this.stateKey(t));for(let r of this.kv.listKeys(this.dataPrefix(t)))this.kv.delete(r);}async setData(t,r,n){this.kv.write(this.dataKey(t,r),n);}async getData(t,r){return this.kv.read(this.dataKey(t,r))}async getAllData(t){let r=this.dataPrefix(t),n={};for(let s of this.kv.listKeys(r))n[et(s.slice(r.length))]=this.kv.read(s);return n}async clearData(t){for(let r of this.kv.listKeys(this.dataPrefix(t)))this.kv.delete(r);}async listRuns(){return this.kv.listKeys("state_").map(t=>et(t.slice(6)))}};var nt=dirname(fileURLToPath(import.meta.url)),Jt=createRequire(import.meta.url);function Dt(){let e=resolve(nt,"./jsonata-sync.cjs");return existsSync(e)?e:resolve(nt,"../card-compute/jsonata-sync.cjs")}var $=Jt(Dt());function st(e,t){if(!e||typeof e!="object")throw new Error(`[step-machine-public] Step "${t}" returned a non-object result.`);let r=e,n=r.result??r.status;if(typeof n=="string"&&n.trim().length>0){let s=r.data&&typeof r.data=="object"&&!Array.isArray(r.data)?{...r.data}:{},o=typeof r.error=="string"?r.error:void 0;return o&&!("error"in s)&&(s.error=o),{result:n,data:s,...o?{error:o}:{}}}return {result:"success",data:{...r}}}function ot(e,t){if(!t||t.length===0)return e;let r={};for(let n of t)Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}function z(e,t){return async(r,n)=>{let s=await e(r,n),o=st(s,n?.stepName??"unknown");return {result:o.result,data:ot(o.data,t),...o.error?{error:o.error}:{}}}}function it(e,t,r){if(!t||t.length===0)return null;for(let n of t)try{if(!$(n).evaluate(e))return {result:"failure",data:{error:`[${r}] input validation failed: ${n}`}}}catch(s){let o=s instanceof Error?s.message:String(s);return {result:"failure",data:{error:`[${r}] input validation error on "${n}": ${o}`}}}return null}function W(e,t,r){return !t||t.length===0?e:async(n,s)=>{let o=it(n,t,r);return o||e(n,s)}}var ct=dirname(fileURLToPath(import.meta.url)),Kt=createRequire(import.meta.url);function qt(){let e=resolve(ct,"./jsonata-sync.cjs");return existsSync(e)?e:resolve(ct,"../../card-compute/jsonata-sync.cjs")}var v=Kt(qt());function ut(e,t,r){if(!e||typeof e!="object")return {};let n={};if(Array.isArray(e.cmdTemplate)){let s=[];for(let o of e.cmdTemplate)try{s.push(String(v(o).evaluate(t)));}catch(a){let i=a instanceof Error?a.message:String(a);throw new Error(`[${r}] argsMassaging.cmdTemplate failed on "${o}": ${i}`)}n.cmdArgs=s;}if(typeof e.stdinTemplate=="string")try{n.stdin=v(e.stdinTemplate).evaluate(t);}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[${r}] argsMassaging.stdinTemplate failed: ${o}`)}if(typeof e.urlTemplate=="string")try{n.url=String(v(e.urlTemplate).evaluate(t));}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[${r}] argsMassaging.urlTemplate failed: ${o}`)}if(typeof e.headerTemplate=="string")try{let s=v(e.headerTemplate).evaluate(t);if(typeof s!="object"||s===null)throw new Error(`headerTemplate must produce an object, got: ${JSON.stringify(s)}`);n.headers=s;}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[${r}] argsMassaging.headerTemplate failed: ${o}`)}if(typeof e.bodyTemplate=="string")try{n.body=v(e.bodyTemplate).evaluate(t);}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[${r}] argsMassaging.bodyTemplate failed: ${o}`)}return n}function lt(e,t,r){if(!e||typeof e!="object")return t;let n={output:t},s=t.result,o=t.data,a=t.error;if(typeof e.resultExpr=="string")try{let i=v(e.resultExpr).evaluate(n);if(typeof i!="string"||!i.trim())throw new Error(`resultExpr did not produce a non-empty string (got ${JSON.stringify(i)})`);s=i;}catch(i){let l=i instanceof Error?i.message:String(i);throw new Error(`[${r}] outputTransforms.resultExpr failed: ${l}`)}if(typeof e.dataTemplate=="string")try{let i=v(e.dataTemplate).evaluate(n);if(!i||typeof i!="object"||Array.isArray(i))throw new Error(`dataTemplate did not produce an object (got ${JSON.stringify(i)})`);o=i;}catch(i){let l=i instanceof Error?i.message:String(i);throw new Error(`[${r}] outputTransforms.dataTemplate failed: ${l}`)}if(typeof e.errorExpr=="string")try{let i=v(e.errorExpr).evaluate(n);a=i!=null?String(i):void 0;}catch(i){let l=i instanceof Error?i.message:String(i);throw new Error(`[${r}] outputTransforms.errorExpr failed: ${l}`)}return a!==void 0?{result:s,data:o,error:a}:{result:s,data:o}}var J="b64:";function Ut(e){let t=new TextEncoder().encode(e),r=globalThis.Buffer,n;if(r)n=r.from(t).toString("base64");else if(typeof btoa=="function"){let s="";for(let o of t)s+=String.fromCharCode(o);n=btoa(s);}else throw new Error("No base64 encoder available in this runtime");return n.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function Vt(e){let t=e.replace(/-/g,"+").replace(/_/g,"/")+"=".repeat((4-e.length%4)%4),r=globalThis.Buffer;if(r)return r.from(t,"base64").toString("utf8");if(typeof atob=="function"){let n=atob(t),s=new Uint8Array(n.length);for(let o=0;o<n.length;o+=1)s[o]=n.charCodeAt(o);return new TextDecoder().decode(s)}throw new Error("No base64 decoder available in this runtime")}function D(e){return `${J}${Ut(JSON.stringify(e))}`}function G(e){if(!e.startsWith(J))throw new Error(`Invalid ref format (expected ${J}<base64url(json)>): ${e}`);let t;try{t=JSON.parse(Vt(e.slice(J.length)));}catch{throw new Error(`Invalid ref format (malformed base64url/json): ${e}`)}if(!t||typeof t!="object")throw new Error(`Invalid ref format (expected object payload): ${e}`);let r=t;if(typeof r.kind!="string"||typeof r.value!="string")throw new Error(`Invalid ref format (payload must contain string kind/value): ${e}`);return {kind:r.kind,value:r.value}}function pt(e){return !!e&&typeof e=="object"&&e.type==="compute-jsonata"&&Array.isArray(e.expr)&&e.expr.length>0}function dt(e){if(!e||typeof e!="object")return false;let t=e;if(t.type!=="ref"||typeof t.howToRun!="string")return false;if(typeof t.whatToRun=="string")return true;if(t.whatToRun&&typeof t.whatToRun=="object"){let r=t.whatToRun;return typeof r.kind=="string"&&typeof r.value=="string"}return false}function zt(e){if(typeof e=="string"){let t=e.indexOf("=");if(t<1)throw new Error(`[step-machine-public] Invalid compute expression (missing "="): "${e}"`);return {bindTo:e.slice(0,t).trim(),expr:e.slice(t+1).trim()}}if(e&&typeof e=="object"&&typeof e.bindTo=="string"&&typeof e.expr=="string")return e;throw new Error(`[step-machine-public] Invalid compute step: ${JSON.stringify(e)}`)}function Wt(e,t,r){let n=t.split("."),s=e;for(let o=0;o<n.length-1;o++){let a=n[o];(s[a]==null||typeof s[a]!="object")&&(s[a]={}),s=s[a];}s[n[n.length-1]]=r;}function ft(e,t,r){let n=e.expr.map(zt);return async s=>{let o=s&&typeof s=="object"&&!Array.isArray(s)?{...s}:{},a={},i={expects_data:o,data:a,...r?{config:r}:{}},l,c;for(let d of n)try{let u=$(d.expr).evaluate(i);if(d.bindTo==="result")l=u!=null?String(u):"success";else if(d.bindTo==="error")c=u!=null?String(u):void 0;else if(d.bindTo.startsWith("data."))Wt(a,d.bindTo.slice(5),u);else return {result:"failure",data:{},error:`[${t}] invalid bindTo "${d.bindTo}": must be "result", "error", or start with "data."`}}catch(u){let f=u instanceof Error?u.message:String(u);return {result:"failure",data:{},error:`[${t}] compute "${d.bindTo}" failed: ${f}`}}return l===void 0?{result:"failure",data:{},error:`[${t}] compute-jsonata: no "result" binding declared \u2014 add '- result = "success"' to expr`}:c?{result:l,data:a,error:c}:{result:l,data:a}}}function gt(e,t,r,n){let{type:s,...o}=e,a={...o,whatToRun:typeof o.whatToRun=="object"?D(o.whatToRun):o.whatToRun};return async i=>{let l=i&&typeof i=="object"&&!Array.isArray(i)?{...i}:{};n&&(l.config=n);try{let c=await r(a,l);if(!e.outputTransforms)return c;try{return lt(e.outputTransforms,c,t)}catch(d){let u=d instanceof Error?d.message:String(d);return {result:"failure",data:{},error:u}}}catch(c){let d=c instanceof Error?c.message:String(c);return {result:"failure",data:{error:`[step-machine-public] step "${t}" invoke threw: ${d}`}}}}}function mt(){return async e=>({result:"success",data:e&&typeof e=="object"&&!Array.isArray(e)?e:{}})}function Gt(e,t,r){return async(n,s)=>{let o=n?.[t.items];if(!Array.isArray(o))return {result:"failure",data:{},error:`[${r}] forEach: "${t.items}" is not an array (got ${typeof o})`};let a=o,i=t.collectAs??`${t.items}_results`;if(a.length===0)return {result:"success",data:{[i]:[]}};let{[t.items]:l,...c}=n,d=Math.max(1,t.concurrency??1),u=new Array(a.length),f=0,h=0,w=0;return await new Promise(p=>{function g(){for(;f<d&&h<a.length;){let x=h++;f++;let P={...c,[t.as]:a[x]};e(P,s).then(S=>{u[x]=S;}).catch(S=>{let y=S instanceof Error?S.message:String(S);u[x]={result:"failure",data:{},error:y};}).finally(()=>{f--,u[x]?.result==="failure"&&w++,h>=a.length&&f===0?p():g();});}f===0&&h>=a.length&&p();}g();}),w>0?{result:"failure",data:{errors:u.filter(g=>g.result==="failure").map(g=>g.error)},error:`[${r}] forEach: ${w}/${a.length} items failed`}:{result:"success",data:{[i]:u.map(p=>p.data)}}}}function ht(e,t,r){let n=Array.isArray(t?.produces_data)?t?.produces_data:void 0,s=Array.isArray(t?.input_validations)?t?.input_validations:void 0,o=t?.config??void 0,a=t?.handler,i;return pt(a)?i=ft(a,e,o):dt(a)?i=gt(a,e,r.invoke,o):i=mt(),t?.forEach&&(i=Gt(i,t.forEach,e)),W(z(i,n),s,e)}function X(e,t){let r={};for(let[n,s]of Object.entries(e.steps??{}))r[n]=ht(n,s,t);return r}function Zt(e){if(typeof e=="object"&&e!==null){let{command:r,args:n=[],...s}=e,o=wt(r,n);return {...s,command:o.command,args:o.args}}let t=xt(e);if(t.length===0)throw new Error(`Empty command spec: ${JSON.stringify(e)}`);return wt(t[0],t.slice(1))}function wt(e,t){return /^(node|node\.exe)$/i.test(e)?{command:process.execPath,args:t}:/\.m?js$/i.test(e)?{command:process.execPath,args:[e,...t]}:{command:e,args:t}}function xt(e){let t=[],r="",n=null;for(let s of e.trim()){if(n){s===n?n=null:r+=s;continue}if(s==='"'||s==="'"){n=s;continue}if(/\s/.test(s)){r&&(t.push(r),r="");continue}r+=s;}if(n)throw new Error(`Unterminated quote in command: ${e}`);return r&&t.push(r),t}function Y(e){return process.platform==="win32"&&/\.(cmd|bat)$/i.test(e)}function vt(e,t){let{command:r,args:n=[],cwd:s,env:o,timeoutMs:a}=e;return execFileSync(r,n,{shell:Y(r),timeout:a,encoding:t?.encoding??"utf-8",cwd:s,windowsHide:true,env:o?{...process.env,...o}:void 0,input:t?.input})}function te(e,t){let{command:r,args:n=[],cwd:s,env:o,timeoutMs:a=3e4}=e;execFile(r,n,{shell:Y(r),encoding:"utf8",windowsHide:true,timeout:a,maxBuffer:10*1024*1024,cwd:s,env:o?{...process.env,...o}:void 0},(i,l,c)=>t(i??null,l,c));}R.join(St.tmpdir(),".board-live-cards-git-bash-cache.json");function Rt(e){let{command:t,args:r=[]}=e;if(process.platform==="win32"){spawn(t,r,{detached:true,stdio:"ignore",windowsHide:true,shell:Y(t)}).unref();return}spawn(t,r,{detached:true,stdio:"ignore"}).unref();}function kt(e,t,r){let n=R.join(e,"board-live-cards-cli.js");if(m.existsSync(n))return {cmd:process.execPath,args:[n,t,...r]};let s=R.join(e,"board-live-cards-cli.ts"),o=R.join(e,"..","..","node_modules","tsx","dist","cli.mjs"),a=R.join(e,"..","..","node_modules",".bin","tsx"),i=m.existsSync(o)?o:a;return m.existsSync(s)&&m.existsSync(i)?{cmd:process.execPath,args:[i,s,t,...r]}:{cmd:process.platform==="win32"?"npx.cmd":"npx",args:["tsx",s,t,...r]}}function bt(){return {executeSync(e,t,r){return vt({command:e,args:t,cwd:r?.cwd,timeoutMs:r?.timeout,env:r?.env},{encoding:r?.encoding,input:r?.input})},executeAsync(e,t,r){te({command:e,args:t},r);},resolveInvocation(e,t){let r=Zt({command:e,args:t});return {cmd:r.command,args:r.args??[]}},splitCommand:xt,spawnDetached(e,t){Rt({command:e,args:t});}}}function ee(e,t){let r=typeof e=="object"?e.value:G(e).value;switch(r){case "source-cli-task-executor":{let n=R.join(t,"source-cli-task-executor.js");if(m.existsSync(n))return {command:process.execPath,args:[n]};let s=R.join(t,"source-cli-task-executor.ts"),o=R.join(t,"..","..","node_modules","tsx","dist","cli.mjs"),a=R.join(t,"..","..","node_modules",".bin","tsx"),i=m.existsSync(o)?o:a;return m.existsSync(s)&&m.existsSync(i)?{command:process.execPath,args:[i,s]}:{command:process.execPath,args:[n]}}case "board-live-cards":{let{cmd:n,args:s}=kt(t,"_",[]);return {command:n,args:s}}default:throw new Error(`resolveBuiltIn: unknown built-in name "${r}". Supported: source-cli-task-executor, board-live-cards`)}}function re(e,t){if(e.howToRun==="built-in"){let{command:n,args:s}=ee(e.whatToRun,t);return {command:n,baseArgs:s}}let r=typeof e.whatToRun=="object"?e.whatToRun.value:G(e.whatToRun).value;switch(e.howToRun){case "local-node":return {command:process.execPath,baseArgs:[r]};case "local-python":return {command:process.platform==="win32"?"python":"python3",baseArgs:[r]};case "local-process":return {command:r,baseArgs:[]};default:throw new Error(`resolveBaseInvocation: howToRun "${e.howToRun}" is not a local transport`)}}function ne(e,t){return re(e,t)}function se(e){let t=e.trim();if(!t)throw new Error("empty stdout");try{return JSON.parse(t)}catch{let r=t.split(/\r?\n/).filter(Boolean),n=r[r.length-1];return JSON.parse(n)}}function Et(e,t,r){let n=r?.label??"invokeRefSync",s=r?.cliDir??r?.cwd??process.cwd(),o;try{o=ut(e.argsMassaging,t,n);}catch(u){return {result:"failure",data:{error:u instanceof Error?u.message:String(u)}}}let a;try{a=ne(e,s);}catch(u){let f=u instanceof Error?u.message:String(u);return {result:"failure",data:{error:`[${n}] ref resolution failed: ${f}`}}}let i=[...a.baseArgs,...o.cmdArgs??[]],l=JSON.stringify(o.stdin??t),c=bt(),d;try{d=c.executeSync(a.command,i,{timeout:r?.timeoutMs??3e4,encoding:"utf-8",cwd:r?.cwd,input:l});}catch(u){let f=u,h=(f.stderr?String(f.stderr):"").trim(),w=typeof f.status=="number"?f.status:"unknown",p=h||f.message;return {result:"failure",data:{error:`[${n}] ref exited with status ${w}${p?`: ${p}`:""}`}}}try{let u=se(d);return {result:"success",data:u&&typeof u=="object"&&!Array.isArray(u)?u:{stdout:u}}}catch{return {result:"success",data:{stdout:d.trim()}}}}function ie(e,t){if(process.platform!=="win32"){m.renameSync(e,t);return}let r=[10,20,40,80,160];for(let n=0;n<=r.length;n++)try{m.renameSync(e,t);return}catch(s){let o=s.code;if((o==="EPERM"||o==="EBUSY")&&n<r.length){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,r[n]);continue}throw s}}function At(e){function t(n){return R.join(e,...n.split("/"))+".json"}function r(n,s,o,a){if(m.existsSync(n))for(let i of m.readdirSync(n,{withFileTypes:true})){let l=s?`${s}/${i.name}`:i.name;if(i.isDirectory()){r(R.join(n,i.name),l,o,a);continue}if(!i.isFile()||!i.name.endsWith(".json"))continue;let c=l.replace(/\.json$/,"");(!o||c.startsWith(o))&&a.push(c);}}return {read(n){let s=t(n);if(!m.existsSync(s))return null;try{return JSON.parse(m.readFileSync(s,"utf-8"))}catch{return null}},write(n,s){let o=t(n),a=`${o}.${process.pid}.${randomUUID()}.tmp`;m.mkdirSync(R.dirname(o),{recursive:true}),m.writeFileSync(a,JSON.stringify(s,null,2),"utf-8"),ie(a,o);},delete(n){let s=t(n);try{m.existsSync(s)&&m.unlinkSync(s);}catch{}},listKeys(n){let s=[];return r(e,"",n,s),s.sort()}}}var B=class extends Error{constructor(r,n){super(n);this.code=r;this.name="CliExitError";}code},ae=".pause";async function Hr(e){let t=ce(e);if(t.help||e.length===0)throw he(),new B(e.length===0?1:0);let{flowArg:r,dataArg:n,storeArg:s,storeDirArg:o,resumeRequested:a,pauseRequested:i,statusRequested:l}=t;if((i||l)&&(n||a||r))throw new Error("[step-machine-cli] --pause and --status are store-level operations. Do not provide flow, data, or --resume.");if(a&&n)throw new Error("[step-machine-cli] --initial-data cannot be combined with --resume.");let c=ue(s,o);if(l){await de(c);return}if(i){await le(c);return}if(!r)throw new Error("[step-machine-cli] Flow path is required for run/resume operations.");let d=$t(r),u=R.dirname(d),f=fe(n),{store:h}=c,w=await H(d),p=me(w,u);jt(c);let g=new AbortController,x=false,P=_(w,p,{store:h,signal:g.signal,onStep:()=>{!x&&Z(c)&&(x=true,g.abort());}}),S;if(a){if(S=await Ct(c),!S){console.warn("[step-machine-cli] No paused run found in store directory."),console.log(JSON.stringify({status:"noop",reason:"no-paused-run"},null,2));return}}else c.storeType==="file"&&!f&&(S=await Ct(c));let y=S?await P.resume(S):await P.run(f);if(x&&y.status==="cancelled"){let M=await pe(h,y.runId);jt(c),console.log(JSON.stringify({runId:y.runId,status:"paused",currentStep:M?.currentStep,pausedAt:M?.pausedAt,stepHistory:y.stepHistory,data:y.data},null,2));return}if(y.status!=="completed"){let M=y.error?.message??y.intent??y.status;throw console.error(`[step-machine-cli] Run failed: ${M}`),new B(1)}console.log(JSON.stringify({runId:y.runId,status:y.status,intent:y.intent,finalStep:y.finalStep,stepHistory:y.stepHistory,data:y.data},null,2));}function ce(e){let t=new Set(["--initial-data","--store","--store-dir"]),r={},n=[],s=false,o=false,a=false,i=false;for(let l=0;l<e.length;l++){let c=e[l];if(c==="-h"||c==="--help"){s=true;continue}if(c==="--resume"){o=true;continue}if(c==="--pause"){a=true;continue}if(c==="--status"){i=true;continue}if(t.has(c)){let d=e[l+1];if(!d||d.startsWith("--"))throw new Error(`[step-machine-cli] Missing value for ${c}.`);r[c]=d,l++;continue}if(c.startsWith("--"))throw new Error(`[step-machine-cli] Unknown flag: ${c}`);n.push(c);}if([o,a,i].filter(Boolean).length>1)throw new Error("[step-machine-cli] Use only one of --resume, --pause, or --status at a time.");return {help:s,flowArg:n[0],dataArg:r["--initial-data"],storeArg:String(r["--store"]??"memory").toLowerCase(),storeDirArg:r["--store-dir"],resumeRequested:o,pauseRequested:a,statusRequested:i}}function $t(e){return R.isAbsolute(e)?e:R.resolve(process.cwd(),e)}function ue(e,t){if(e!=="memory"&&e!=="file")throw new Error(`[step-machine-cli] Invalid --store value "${e}". Expected "memory" or "file".`);if(e==="memory")return {storeType:e,storeDir:void 0,pauseFilePath:void 0,store:new k};if(!t||t.trim().length===0)throw new Error("[step-machine-cli] --store file requires --store-dir <directory>.");let r=$t(t);return {storeType:e,storeDir:r,pauseFilePath:R.join(r,ae),store:new j(At(r))}}async function Q(e){if(!e.listRuns)return [];let t=await e.listRuns(),r=[];for(let n of t){let s=await e.loadRunState(n);s&&r.push(s);}return r.sort((n,s)=>(s.updatedAt??s.startedAt??0)-(n.updatedAt??n.startedAt??0)),r}function Z(e){return e.storeType!=="file"||!e.pauseFilePath?false:m.existsSync(e.pauseFilePath)}function jt(e){Z(e)&&m.unlinkSync(e.pauseFilePath);}async function le(e){if(e.storeType!=="file"||!e.pauseFilePath)throw new Error("[step-machine-cli] --pause requires --store file --store-dir <directory>.");let t=await Q(e.store);if(t.length===0){console.warn("[step-machine-cli] No runs found in store directory. Pause is a no-op."),console.log(JSON.stringify({status:"noop",reason:"no-runs"},null,2));return}if(!t.find(n=>n.status==="running")){console.warn("[step-machine-cli] No running run found. Pause is a no-op."),console.log(JSON.stringify({status:"noop",reason:"no-running-run"},null,2));return}m.mkdirSync(e.storeDir,{recursive:true}),m.writeFileSync(e.pauseFilePath,JSON.stringify({requestedAt:Date.now()}),"utf-8"),console.log(JSON.stringify({status:"pause-requested",storeDir:e.storeDir},null,2));}async function Ct(e){let r=(await Q(e.store)).filter(n=>n.status==="paused");if(r.length!==0)return r.length>1&&console.warn("[step-machine-cli] Multiple paused runs found; resuming the most recently updated run."),r[0].runId}async function pe(e,t){let r=await e.loadRunState(t);if(!r)return null;let n={...r,status:"paused",pausedAt:Date.now(),updatedAt:Date.now()};return await e.saveRunState(t,n),n}async function de(e){if(e.storeType!=="file")throw new Error("[step-machine-cli] --status requires --store file --store-dir <directory>.");let t=await Q(e.store),r={store:"file",storeDir:e.storeDir,pauseRequested:Z(e),totalRuns:t.length,runs:t.map(n=>({runId:n.runId,status:n.status,currentStep:n.currentStep,startedAt:n.startedAt,updatedAt:n.updatedAt,pausedAt:n.pausedAt}))};console.log(JSON.stringify(r,null,2));}function fe(e){if(e)try{let t=JSON.parse(e);if(!t||typeof t!="object"||Array.isArray(t))throw new Error("Initial data must be a JSON object.");return t}catch(t){let r=t instanceof Error?t.message:String(t);throw new Error(`[step-machine-cli] Invalid --initial-data value: ${r}`)}}function ge(e){if(!e||typeof e!="object")return e;let t=e;if(typeof t.whatToRun!="string"||!t.whatToRun.startsWith("b64:"))return e;try{let r=t.whatToRun.slice(4),n=r+"=".repeat((4-r.length%4)%4),s=Buffer.from(n.replace(/-/g,"+").replace(/_/g,"/"),"base64").toString("utf8"),o=JSON.parse(s);return !o||typeof o!="object"||typeof o.value!="string"?e:{...t,whatToRun:o}}catch{return e}}function me(e,t){return X(e,{invoke:(n,s)=>Et(ge(n),s,{cliDir:t,cwd:t})})}function he(){console.error("Usage: step-machine-cli <step-flow.yaml> [--initial-data <json>] [--store <memory|file>] [--store-dir <directory>] [--resume]"),console.error(" step-machine-cli --store file --store-dir <directory> --pause"),console.error(" step-machine-cli --store file --store-dir <directory> --status"),console.error(""),console.error("Example:"),console.error(' step-machine-cli examples/cli/step-machine-demo/two-step-math.flow.yaml --initial-data "{"a":3,"b":4}"'),console.error(" step-machine-cli ./flow.yaml --store file --store-dir ./.runs"),console.error(" step-machine-cli ./flow.yaml --store file --store-dir ./.runs --resume"),console.error(" step-machine-cli --store file --store-dir ./.runs --pause"),console.error(" step-machine-cli --store file --store-dir ./.runs --status");}
|
|
4
|
-
export{
|
|
1
|
+
import*as m from'fs';import {existsSync}from'fs';import*as v from'path';import {dirname,resolve}from'path';import'ajv-formats';import {createRequire}from'module';import {fileURLToPath}from'url';import {spawn,execFile,execFileSync}from'child_process';import*as Rt from'os';import'net';import {randomUUID}from'crypto';import'proper-lockfile';function K(e,t,n,r){let s=e.steps[n];if(!s)throw new Error(`Step "${n}" not found in flow configuration`);if(r.result==="failure"&&s.retry){let i=t.retryCounts[n]??0;if(i<s.retry.max_attempts)return {newState:{...t,retryCounts:{...t.retryCounts,[n]:i+1},updatedAt:Date.now()},nextStep:n,isTerminal:false,isCircuitBroken:false,shouldRetry:true}}let o=s.failure_transitions?.[r.result]??s.transitions[r.result];if(!o)throw new Error(`No transition defined for result "${r.result}" in step "${n}"`);let a=!!e.terminal_states[o];return {newState:{...t,currentStep:o,stepHistory:[...t.stepHistory,n],retryCounts:{...t.retryCounts,[n]:0},updatedAt:Date.now()},nextStep:o,isTerminal:a,isCircuitBroken:false,shouldRetry:false}}function U(e,t,n){let r=e.steps[n];if(!r?.circuit_breaker)return {broken:false,newState:{...t,iterationCounts:{...t.iterationCounts,[n]:(t.iterationCounts[n]??0)+1},updatedAt:Date.now()}};let s=t.iterationCounts[n]??0;return s>=r.circuit_breaker.max_iterations?{broken:true,redirectStep:r.circuit_breaker.on_open,newState:{...t,currentStep:r.circuit_breaker.on_open,updatedAt:Date.now()}}:{broken:false,newState:{...t,iterationCounts:{...t.iterationCounts,[n]:s+1},updatedAt:Date.now()}}}function q(e,t,n){let r=e.steps[t];if(!r)throw new Error(`Step "${t}" not found`);if(r.expects_data){let s={};for(let o of r.expects_data)s[o]=n[o];return s}return {...n}}function z(e,t){if(e===false||e===void 0)return {};if(typeof e=="string")return {[e]:t[e]};if(Array.isArray(e)){let n={};for(let r of e)n[r]=t[r];return n}return {}}function V(e,t){let n=Date.now();return {runId:t,flowId:e.id??"unnamed",currentStep:e.settings.start_step,status:"running",stepHistory:[],iterationCounts:{},retryCounts:{},startedAt:n,updatedAt:n}}var k=class{runs=new Map;data=new Map;async saveRunState(t,n){this.runs.set(t,{...n});}async loadRunState(t){let n=this.runs.get(t);return n?{...n}:null}async deleteRunState(t){this.runs.delete(t),this.data.delete(t);}async setData(t,n,r){this.data.has(t)||this.data.set(t,{});let s=this.data.get(t);s[n]=r;}async getData(t,n){return this.data.get(t)?.[n]}async getAllData(t){return {...this.data.get(t)??{}}}async clearData(t){this.data.delete(t);}async listRuns(){return Array.from(this.runs.keys())}clear(){this.runs.clear(),this.data.clear();}};function Pt(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return (e==="x"?t:t&3|8).toString(16)})}var O=class{flow;handlers;store;components;options;listeners=new Map;aborted=false;constructor(t,n,r={}){this.flow=t,this.handlers=new Map(Object.entries(n)),this.store=r.store??new k,this.components=r.components??{},this.options=r,r.signal&&r.signal.addEventListener("abort",()=>{this.aborted=true;}),this.validateFlow();}validateFlow(){let{settings:t,steps:n,terminal_states:r}=this.flow;if(!t?.start_step)throw new Error("Flow must have settings.start_step defined");if(!n||Object.keys(n).length===0)throw new Error("Flow must have at least one step defined");if(!r||Object.keys(r).length===0)throw new Error("Flow must have at least one terminal_state defined");if(!n[t.start_step]&&!r[t.start_step])throw new Error(`Start step "${t.start_step}" not found`);for(let[s,o]of Object.entries(n)){for(let[a,i]of Object.entries(o.transitions))if(!n[i]&&!r[i])throw new Error(`Step "${s}" transition "${a}" points to unknown step "${i}"`);if(o.failure_transitions){for(let[a,i]of Object.entries(o.failure_transitions))if(!n[i]&&!r[i])throw new Error(`Step "${s}" failure_transition "${a}" points to unknown step "${i}"`)}}}on(t,n){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n);}off(t,n){this.listeners.get(t)?.delete(n);}emit(t){let n=this.listeners.get(t.type);if(n)for(let r of n)try{r(t);}catch{}}sleep(t){return new Promise(n=>setTimeout(n,t))}async run(t){let n=Pt(),r=V(this.flow,n);if(await this.store.saveRunState(n,r),t)for(let[s,o]of Object.entries(t))await this.store.setData(n,s,o);this.emit({type:"flow:start",runId:n,timestamp:r.startedAt,data:{initialData:t??{}}});try{return await this.executeLoop(n,r)}catch(s){let o=s instanceof Error?s:new Error(String(s));return this.emit({type:"flow:error",runId:n,timestamp:Date.now(),data:{error:o.message}}),this.options.onError?.(o),r={...r,status:"failed",updatedAt:Date.now()},await this.store.saveRunState(n,r),{runId:n,status:"failed",data:await this.store.getAllData(n),finalStep:r.currentStep,stepHistory:r.stepHistory,durationMs:Date.now()-r.startedAt,error:o}}}async resume(t){let n=await this.store.loadRunState(t);if(!n)throw new Error(`No run found with ID: ${t}`);if(n.status==="completed"||n.status==="failed")throw new Error(`Cannot resume a ${n.status} run`);let r={...n,status:"running",pausedAt:void 0,updatedAt:Date.now()};return await this.store.saveRunState(t,r),this.emit({type:"flow:resumed",runId:t,timestamp:Date.now(),data:{currentStep:r.currentStep}}),this.executeLoop(t,r)}async pause(t){let n=await this.store.loadRunState(t);if(!n)throw new Error(`No run found with ID: ${t}`);let r={...n,status:"paused",pausedAt:Date.now(),updatedAt:Date.now()};await this.store.saveRunState(t,r),this.emit({type:"flow:paused",runId:t,timestamp:Date.now(),data:{currentStep:r.currentStep}});}async executeLoop(t,n){let r=this.flow.settings.max_total_steps??100,s=this.flow.settings.timeout_ms,o=n,a=0;for(;a<r;){if(this.aborted)return o={...o,status:"cancelled",updatedAt:Date.now()},await this.store.saveRunState(t,o),{runId:t,status:"cancelled",data:await this.store.getAllData(t),finalStep:o.currentStep,stepHistory:o.stepHistory,durationMs:Date.now()-o.startedAt};if(s&&Date.now()-o.startedAt>s)return o={...o,status:"completed",updatedAt:Date.now()},await this.store.saveRunState(t,o),{runId:t,status:"timeout",intent:"timeout",data:await this.store.getAllData(t),finalStep:o.currentStep,stepHistory:o.stepHistory,durationMs:Date.now()-o.startedAt};let i=o.currentStep,u=this.flow.terminal_states[i];if(u){o={...o,status:"completed",updatedAt:Date.now()},await this.store.saveRunState(t,o);let p=await this.store.getAllData(t),g={runId:t,status:"completed",intent:u.return_intent,data:z(u.return_artifacts,p),finalStep:i,stepHistory:o.stepHistory,durationMs:Date.now()-o.startedAt};return this.emit({type:"flow:complete",runId:t,timestamp:Date.now(),data:{...g}}),this.options.onComplete?.(g),g}let c=U(this.flow,o,i);if(c.broken){o=c.newState,await this.store.saveRunState(t,o),a++;continue}o=c.newState;let d=await this.store.getAllData(t),l=q(this.flow,i,d),f={runId:t,stepName:i,components:this.components,store:this.store,signal:this.options.signal,emit:(p,g)=>{this.emit({type:"step:complete",runId:t,timestamp:Date.now(),data:{event:p,payload:g}});}};this.emit({type:"step:start",runId:t,timestamp:Date.now(),data:{step:i,input:l}});let h;try{let p=this.handlers.get(i);if(!p)throw new Error(`No handler registered for step "${i}"`);h=await p(l,f);}catch(p){let g=p instanceof Error?p:new Error(String(p));this.emit({type:"step:error",runId:t,timestamp:Date.now(),data:{step:i,error:g.message}}),h={result:"failure",data:{error:g.message}};}if(h.data)for(let[p,g]of Object.entries(h.data))await this.store.setData(t,p,g);this.emit({type:"step:complete",runId:t,timestamp:Date.now(),data:{step:i,result:h.result}}),this.options.onStep?.(i,h);let w=K(this.flow,o,i,h);if(o=w.newState,w.shouldRetry){await this.store.saveRunState(t,o);let p=this.flow.steps[i];if(p.retry?.delay_ms){let g=o.retryCounts[i]??0,x=p.retry.backoff_multiplier?p.retry.delay_ms*Math.pow(p.retry.backoff_multiplier,g-1):p.retry.delay_ms;await this.sleep(x);}a++;continue}await this.store.saveRunState(t,o),this.emit({type:"transition",runId:t,timestamp:Date.now(),data:{from:i,to:o.currentStep,result:h.result}}),this.options.onTransition?.(i,o.currentStep),a++;}return o={...o,status:"completed",updatedAt:Date.now()},await this.store.saveRunState(t,o),{runId:t,status:"max_iterations",intent:"max_iterations",data:await this.store.getAllData(t),finalStep:o.currentStep,stepHistory:o.stepHistory,durationMs:Date.now()-o.startedAt}}};function H(e,t,n){return new O(e,t,n)}async function W(e){return (await import('yaml')).parse(e)}async function Mt(e){let t=await fetch(e);if(!t.ok)throw new Error(`Failed to load flow from ${e}: ${t.statusText}`);let n=t.headers.get("content-type")??"",r=await t.text();return n.includes("json")||e.endsWith(".json")?JSON.parse(r):W(r)}async function Ot(e){let n=await(await import('fs/promises')).readFile(e,"utf-8");return e.endsWith(".json")?JSON.parse(n):W(n)}function nt(e){let t=[];if(!e||typeof e!="object")return ["Flow must be an object"];let n=e;if(!n.settings||typeof n.settings!="object"?t.push('Flow must have a "settings" object'):typeof n.settings.start_step!="string"&&t.push("settings.start_step must be a string"),!n.steps||typeof n.steps!="object")t.push('Flow must have a "steps" object');else {let r=n.steps;for(let[s,o]of Object.entries(r)){if(!o||typeof o!="object"){t.push(`Step "${s}" must be an object`);continue}let a=o;(!a.transitions||typeof a.transitions!="object")&&t.push(`Step "${s}" must have a "transitions" object`),a.failure_transitions!==void 0&&typeof a.failure_transitions!="object"&&t.push(`Step "${s}" failure_transitions must be an object when provided`);}}if(!n.terminal_states||typeof n.terminal_states!="object")t.push('Flow must have a "terminal_states" object');else {let r=n.terminal_states;for(let[s,o]of Object.entries(r)){if(!o||typeof o!="object"){t.push(`Terminal state "${s}" must be an object`);continue}typeof o.return_intent!="string"&&t.push(`Terminal state "${s}" must have a "return_intent" string`);}}return t}async function I(e){let t;typeof e=="string"?e.startsWith("http://")||e.startsWith("https://")?t=await Mt(e):e.includes("{")?t=JSON.parse(e):t=await Ot(e):t=e;let n=nt(t);if(n.length>0)throw new Error(`Invalid step flow configuration:
|
|
2
|
+
- ${n.join(`
|
|
3
|
+
- `)}`);return t}function G(e){return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function rt(e){return atob(e.replace(/-/g,"+").replace(/_/g,"/"))}var j=class{constructor(t){this.kv=t;}kv;stateKey(t){return `state_${G(t)}`}dataPrefix(t){return `data_${G(t)}_`}dataKey(t,n){return `${this.dataPrefix(t)}${G(n)}`}async saveRunState(t,n){this.kv.write(this.stateKey(t),n);}async loadRunState(t){let n=this.kv.read(this.stateKey(t));return n!=null&&typeof n=="object"?n:null}async deleteRunState(t){this.kv.delete(this.stateKey(t));for(let n of this.kv.listKeys(this.dataPrefix(t)))this.kv.delete(n);}async setData(t,n,r){this.kv.write(this.dataKey(t,n),r);}async getData(t,n){return this.kv.read(this.dataKey(t,n))}async getAllData(t){let n=this.dataPrefix(t),r={};for(let s of this.kv.listKeys(n))r[rt(s.slice(n.length))]=this.kv.read(s);return r}async clearData(t){for(let n of this.kv.listKeys(this.dataPrefix(t)))this.kv.delete(n);}async listRuns(){return this.kv.listKeys("state_").map(t=>rt(t.slice(6)))}};var ot=dirname(fileURLToPath(import.meta.url)),Dt=createRequire(import.meta.url);function Bt(){let e=resolve(ot,"./jsonata-sync.cjs");return existsSync(e)?e:resolve(ot,"../card-compute/jsonata-sync.cjs")}var $=Dt(Bt());function it(e,t){if(!e||typeof e!="object")throw new Error(`[step-machine-public] Step "${t}" returned a non-object result.`);let n=e,r=n.result??n.status;if(typeof r=="string"&&r.trim().length>0){let s=n.data&&typeof n.data=="object"&&!Array.isArray(n.data)?{...n.data}:{},o=typeof n.error=="string"?n.error:void 0;return o&&!("error"in s)&&(s.error=o),{result:r,data:s,...o?{error:o}:{}}}return {result:"success",data:{...n}}}function at(e,t){if(!t||t.length===0)return e;let n={};for(let r of t)Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function X(e,t){return async(n,r)=>{let s=await e(n,r),o=it(s,r?.stepName??"unknown");return {result:o.result,data:at(o.data,t),...o.error?{error:o.error}:{}}}}function ct(e,t,n){if(!t||t.length===0)return null;for(let r of t)try{if(!$(r).evaluate(e))return {result:"failure",data:{error:`[${n}] input validation failed: ${r}`}}}catch(s){let o=s instanceof Error?s.message:String(s);return {result:"failure",data:{error:`[${n}] input validation error on "${r}": ${o}`}}}return null}function Y(e,t,n){return !t||t.length===0?e:async(r,s)=>{let o=ct(r,t,n);return o||e(r,s)}}var lt=dirname(fileURLToPath(import.meta.url)),qt=createRequire(import.meta.url);function zt(){let e=resolve(lt,"./jsonata-sync.cjs");return existsSync(e)?e:resolve(lt,"../../card-compute/jsonata-sync.cjs")}var R=qt(zt());function pt(e,t,n){if(!e||typeof e!="object")return {};let r={};if(Array.isArray(e.cmdTemplate)){let s=[];for(let o of e.cmdTemplate)try{s.push(String(R(o).evaluate(t)));}catch(a){let i=a instanceof Error?a.message:String(a);throw new Error(`[${n}] argsMassaging.cmdTemplate failed on "${o}": ${i}`)}r.cmdArgs=s;}if(typeof e.stdinTemplate=="string")try{r.stdin=R(e.stdinTemplate).evaluate(t);}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[${n}] argsMassaging.stdinTemplate failed: ${o}`)}if(typeof e.urlTemplate=="string")try{r.url=String(R(e.urlTemplate).evaluate(t));}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[${n}] argsMassaging.urlTemplate failed: ${o}`)}if(typeof e.headerTemplate=="string")try{let s=R(e.headerTemplate).evaluate(t);if(typeof s!="object"||s===null)throw new Error(`headerTemplate must produce an object, got: ${JSON.stringify(s)}`);r.headers=s;}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[${n}] argsMassaging.headerTemplate failed: ${o}`)}if(typeof e.bodyTemplate=="string")try{r.body=R(e.bodyTemplate).evaluate(t);}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`[${n}] argsMassaging.bodyTemplate failed: ${o}`)}return r}function dt(e,t,n){if(!e||typeof e!="object")return t;let r={output:t},s=t.result,o=t.data,a=t.error;if(typeof e.resultExpr=="string")try{let i=R(e.resultExpr).evaluate(r);if(typeof i!="string"||!i.trim())throw new Error(`resultExpr did not produce a non-empty string (got ${JSON.stringify(i)})`);s=i;}catch(i){let u=i instanceof Error?i.message:String(i);throw new Error(`[${n}] outputTransforms.resultExpr failed: ${u}`)}if(typeof e.dataTemplate=="string")try{let i=R(e.dataTemplate).evaluate(r);if(!i||typeof i!="object"||Array.isArray(i))throw new Error(`dataTemplate did not produce an object (got ${JSON.stringify(i)})`);o=i;}catch(i){let u=i instanceof Error?i.message:String(i);throw new Error(`[${n}] outputTransforms.dataTemplate failed: ${u}`)}if(typeof e.errorExpr=="string")try{let i=R(e.errorExpr).evaluate(r);a=i!=null?String(i):void 0;}catch(i){let u=i instanceof Error?i.message:String(i);throw new Error(`[${n}] outputTransforms.errorExpr failed: ${u}`)}return a!==void 0?{result:s,data:o,error:a}:{result:s,data:o}}var _="b64:";function Vt(e){let t=new TextEncoder().encode(e),n=globalThis.Buffer,r;if(n)r=n.from(t).toString("base64");else if(typeof btoa=="function"){let s="";for(let o of t)s+=String.fromCharCode(o);r=btoa(s);}else throw new Error("No base64 encoder available in this runtime");return r.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function Wt(e){let t=e.replace(/-/g,"+").replace(/_/g,"/")+"=".repeat((4-e.length%4)%4),n=globalThis.Buffer;if(n)return n.from(t,"base64").toString("utf8");if(typeof atob=="function"){let r=atob(t),s=new Uint8Array(r.length);for(let o=0;o<r.length;o+=1)s[o]=r.charCodeAt(o);return new TextDecoder().decode(s)}throw new Error("No base64 decoder available in this runtime")}function J(e){return `${_}${Vt(JSON.stringify(e))}`}function D(e){if(!e.startsWith(_))throw new Error(`Invalid ref format (expected ${_}<base64url(json)>): ${e}`);let t;try{t=JSON.parse(Wt(e.slice(_.length)));}catch{throw new Error(`Invalid ref format (malformed base64url/json): ${e}`)}if(!t||typeof t!="object")throw new Error(`Invalid ref format (expected object payload): ${e}`);let n=t;if(typeof n.kind!="string"||typeof n.value!="string")throw new Error(`Invalid ref format (payload must contain string kind/value): ${e}`);return {kind:n.kind,value:n.value}}function ft(e){return !!e&&typeof e=="object"&&e.type==="compute-jsonata"&&Array.isArray(e.expr)&&e.expr.length>0}function gt(e){if(!e||typeof e!="object")return false;let t=e;if(t.type!=="ref"||typeof t.howToRun!="string")return false;if(typeof t.whatToRun=="string")return true;if(t.whatToRun&&typeof t.whatToRun=="object"){let n=t.whatToRun;return typeof n.kind=="string"&&typeof n.value=="string"}return false}function Gt(e){if(typeof e=="string"){let t=e.indexOf("=");if(t<1)throw new Error(`[step-machine-public] Invalid compute expression (missing "="): "${e}"`);return {bindTo:e.slice(0,t).trim(),expr:e.slice(t+1).trim()}}if(e&&typeof e=="object"&&typeof e.bindTo=="string"&&typeof e.expr=="string")return e;throw new Error(`[step-machine-public] Invalid compute step: ${JSON.stringify(e)}`)}function Xt(e,t,n){let r=t.split("."),s=e;for(let o=0;o<r.length-1;o++){let a=r[o];(s[a]==null||typeof s[a]!="object")&&(s[a]={}),s=s[a];}s[r[r.length-1]]=n;}function mt(e,t,n){let r=e.expr.map(Gt);return async s=>{let o=s&&typeof s=="object"&&!Array.isArray(s)?{...s}:{},a={},i={expects_data:o,data:a,...n?{config:n}:{}},u,c;for(let d of r)try{let l=$(d.expr).evaluate(i);if(d.bindTo==="result")u=l!=null?String(l):"success";else if(d.bindTo==="error")c=l!=null?String(l):void 0;else if(d.bindTo.startsWith("data."))Xt(a,d.bindTo.slice(5),l);else return {result:"failure",data:{},error:`[${t}] invalid bindTo "${d.bindTo}": must be "result", "error", or start with "data."`}}catch(l){let f=l instanceof Error?l.message:String(l);return {result:"failure",data:{},error:`[${t}] compute "${d.bindTo}" failed: ${f}`}}return u===void 0?{result:"failure",data:{},error:`[${t}] compute-jsonata: no "result" binding declared \u2014 add '- result = "success"' to expr`}:c?{result:u,data:a,error:c}:{result:u,data:a}}}function ht(e,t,n,r){let{type:s,...o}=e,a={...o,whatToRun:typeof o.whatToRun=="object"?J(o.whatToRun):o.whatToRun};return async i=>{let u=i&&typeof i=="object"&&!Array.isArray(i)?{...i}:{};r&&(u.config=r);try{let c=await n(a,u);if(!e.outputTransforms)return c;try{return dt(e.outputTransforms,c,t)}catch(d){let l=d instanceof Error?d.message:String(d);return {result:"failure",data:{},error:l}}}catch(c){let d=c instanceof Error?c.message:String(c);return {result:"failure",data:{error:`[step-machine-public] step "${t}" invoke threw: ${d}`}}}}}function yt(){return async e=>({result:"success",data:e&&typeof e=="object"&&!Array.isArray(e)?e:{}})}function Yt(e,t,n){return async(r,s)=>{let o=r?.[t.items];if(!Array.isArray(o))return {result:"failure",data:{},error:`[${n}] forEach: "${t.items}" is not an array (got ${typeof o})`};let a=o,i=t.collectAs??`${t.items}_results`;if(a.length===0)return {result:"success",data:{[i]:[]}};let{[t.items]:u,...c}=r,d=Math.max(1,t.concurrency??1),l=new Array(a.length),f=0,h=0,w=0;return await new Promise(p=>{function g(){for(;f<d&&h<a.length;){let x=h++;f++;let P={...c,[t.as]:a[x]};e(P,s).then(S=>{l[x]=S;}).catch(S=>{let y=S instanceof Error?S.message:String(S);l[x]={result:"failure",data:{},error:y};}).finally(()=>{f--,l[x]?.result==="failure"&&w++,h>=a.length&&f===0?p():g();});}f===0&&h>=a.length&&p();}g();}),w>0?{result:"failure",data:{errors:l.filter(g=>g.result==="failure").map(g=>g.error)},error:`[${n}] forEach: ${w}/${a.length} items failed`}:{result:"success",data:{[i]:l.map(p=>p.data)}}}}function wt(e,t,n){let r=Array.isArray(t?.produces_data)?t?.produces_data:void 0,s=Array.isArray(t?.input_validations)?t?.input_validations:void 0,o=t?.config??void 0,a=t?.handler,i;return ft(a)?i=mt(a,e,o):gt(a)?i=ht(a,e,n.invoke,o):i=yt(),t?.forEach&&(i=Yt(i,t.forEach,e)),Y(X(i,r),s,e)}function Q(e,t){let n={};for(let[r,s]of Object.entries(e.steps??{}))n[r]=wt(r,s,t);return n}function ee(e){if(typeof e=="object"&&e!==null){let{command:n,args:r=[],...s}=e,o=xt(n,r);return {...s,command:o.command,args:o.args}}let t=vt(e);if(t.length===0)throw new Error(`Empty command spec: ${JSON.stringify(e)}`);return xt(t[0],t.slice(1))}function xt(e,t){return /^(node|node\.exe)$/i.test(e)?{command:process.execPath,args:t}:/\.m?js$/i.test(e)?{command:process.execPath,args:[e,...t]}:{command:e,args:t}}function vt(e){let t=[],n="",r=null;for(let s of e.trim()){if(r){s===r?r=null:n+=s;continue}if(s==='"'||s==="'"){r=s;continue}if(/\s/.test(s)){n&&(t.push(n),n="");continue}n+=s;}if(r)throw new Error(`Unterminated quote in command: ${e}`);return n&&t.push(n),t}function Z(e){return process.platform==="win32"&&/\.(cmd|bat)$/i.test(e)}function kt(e,t){let{command:n,args:r=[],cwd:s,env:o,timeoutMs:a}=e;return execFileSync(n,r,{shell:Z(n),timeout:a,encoding:t?.encoding??"utf-8",cwd:s,windowsHide:true,env:o?{...process.env,...o}:void 0,input:t?.input})}function ne(e,t){let{command:n,args:r=[],cwd:s,env:o,timeoutMs:a=3e4}=e;execFile(n,r,{shell:Z(n),encoding:"utf8",windowsHide:true,timeout:a,maxBuffer:10*1024*1024,cwd:s,env:o?{...process.env,...o}:void 0},(i,u,c)=>t(i??null,u,c));}v.join(Rt.tmpdir(),".board-live-cards-git-bash-cache.json");function bt(e){let{command:t,args:n=[]}=e;if(process.platform==="win32"){spawn(t,n,{detached:true,stdio:"ignore",windowsHide:true,shell:Z(t)}).unref();return}spawn(t,n,{detached:true,stdio:"ignore"}).unref();}function Et(e,t,n){let r=v.join(e,"board-live-cards-cli.js");if(m.existsSync(r))return {cmd:process.execPath,args:[r,t,...n]};let s=v.join(e,"board-live-cards-cli.ts"),o=v.join(e,"..","..","node_modules","tsx","dist","cli.mjs"),a=v.join(e,"..","..","node_modules",".bin","tsx"),i=m.existsSync(o)?o:a;return m.existsSync(s)&&m.existsSync(i)?{cmd:process.execPath,args:[i,s,t,...n]}:{cmd:process.platform==="win32"?"npx.cmd":"npx",args:["tsx",s,t,...n]}}function At(){return {executeSync(e,t,n){return kt({command:e,args:t,cwd:n?.cwd,timeoutMs:n?.timeout,env:n?.env},{encoding:n?.encoding,input:n?.input})},executeAsync(e,t,n){ne({command:e,args:t},n);},resolveInvocation(e,t){let n=ee({command:e,args:t});return {cmd:n.command,args:n.args??[]}},splitCommand:vt,spawnDetached(e,t){bt({command:e,args:t});}}}function re(e,t){let n=typeof e=="object"?e.value:D(e).value;switch(n){case "source-cli-task-executor":{let r=v.join(t,"source-cli-task-executor.js");if(m.existsSync(r))return {command:process.execPath,args:[r]};let s=v.join(t,"source-cli-task-executor.ts"),o=v.join(t,"..","..","node_modules","tsx","dist","cli.mjs"),a=v.join(t,"..","..","node_modules",".bin","tsx"),i=m.existsSync(o)?o:a;return m.existsSync(s)&&m.existsSync(i)?{command:process.execPath,args:[i,s]}:{command:process.execPath,args:[r]}}case "board-live-cards":{let{cmd:r,args:s}=Et(t,"_",[]);return {command:r,args:s}}default:throw new Error(`resolveBuiltIn: unknown built-in name "${n}". Supported: source-cli-task-executor, board-live-cards`)}}function se(e,t){if(e.howToRun==="built-in"){let{command:r,args:s}=re(e.whatToRun,t);return {command:r,baseArgs:s}}let n=typeof e.whatToRun=="object"?e.whatToRun.value:D(e.whatToRun).value;switch(e.howToRun){case "local-node":return {command:process.execPath,baseArgs:[n]};case "local-python":return {command:process.platform==="win32"?"python":"python3",baseArgs:[n]};case "local-process":return {command:n,baseArgs:[]};default:throw new Error(`resolveBaseInvocation: howToRun "${e.howToRun}" is not a local transport`)}}function oe(e,t){return se(e,t)}function ie(e){let t=e.trim();if(!t)throw new Error("empty stdout");try{return JSON.parse(t)}catch{let n=t.split(/\r?\n/).filter(Boolean),r=n[n.length-1];return JSON.parse(r)}}function ae(e){let t=e.whatToRun;return typeof t=="object"?t.value:D(t).value}function ce(e,t){return {...t,whatToRun:ae(e),...e.extra?{extra:e.extra}:{}}}function ue(e,t,n="invokeExecutionRef"){return pt(e,t,n)}function le(e){return e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.result=="string"&&e.data&&typeof e.data=="object"&&!Array.isArray(e.data)?e:{result:"success",data:e&&typeof e=="object"&&!Array.isArray(e)?e:{stdout:e}}}function N(e){return {result:"failure",data:{error:e}}}function B(e,t,n){let r=n?.label??"invokeExecutionRefSync",s=n?.cliDir??n?.cwd??process.cwd(),o;try{o=ue(e.argsMassaging,ce(e,t),r);}catch(l){let f=l instanceof Error?l.message:String(l);return N(f)}let a;try{a=oe(e,s);}catch(l){let f=l instanceof Error?l.message:String(l);return N(`[${r}] ref resolution failed: ${f}`)}let i=[...a.baseArgs,...o.cmdArgs??[]],u=JSON.stringify(o.stdin??t),c=At(),d;try{d=c.executeSync(a.command,i,{timeout:n?.timeoutMs??3e4,encoding:"utf-8",cwd:n?.cwd,input:u});}catch(l){let f=l,h=(f.stderr?String(f.stderr):"").trim(),w=typeof f.status=="number"?f.status:"unknown",p=h||f.message;return N(`[${r}] ref exited with status ${w}${p?`: ${p}`:""}`)}try{return le(ie(d))}catch{return {result:"success",data:{stdout:d.trim()}}}}var pe={"local-node":B,"local-python":B,"local-process":B,"built-in":B};function de(e,t,n){let r=n?.syncTransports?.[e.howToRun]??pe[e.howToRun];return r?r(e,t,n):N(`[${n?.label??"invokeExecutionRefSync"}] unsupported sync howToRun: ${e.howToRun}`)}function jt(e,t,n){return de(e,t,n)}function ge(e,t){if(process.platform!=="win32"){m.renameSync(e,t);return}let n=[10,20,40,80,160];for(let r=0;r<=n.length;r++)try{m.renameSync(e,t);return}catch(s){let o=s.code;if((o==="EPERM"||o==="EBUSY")&&r<n.length){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,n[r]);continue}throw s}}function Tt(e){function t(r){return v.join(e,...r.split("/"))+".json"}function n(r,s,o,a){if(m.existsSync(r))for(let i of m.readdirSync(r,{withFileTypes:true})){let u=s?`${s}/${i.name}`:i.name;if(i.isDirectory()){n(v.join(r,i.name),u,o,a);continue}if(!i.isFile()||!i.name.endsWith(".json"))continue;let c=u.replace(/\.json$/,"");(!o||c.startsWith(o))&&a.push(c);}}return {read(r){let s=t(r);if(!m.existsSync(s))return null;try{return JSON.parse(m.readFileSync(s,"utf-8"))}catch{return null}},write(r,s){let o=t(r),a=`${o}.${process.pid}.${randomUUID()}.tmp`;m.mkdirSync(v.dirname(o),{recursive:true}),m.writeFileSync(a,JSON.stringify(s,null,2),"utf-8"),ge(a,o);},delete(r){let s=t(r);try{m.existsSync(s)&&m.unlinkSync(s);}catch{}},listKeys(r){let s=[];return n(e,"",r,s),s.sort()}}}var L=class extends Error{constructor(n,r){super(r);this.code=n;this.name="CliExitError";}code},me=".pause";async function qn(e){let t=he(e);if(t.help||e.length===0)throw be(),new L(e.length===0?1:0);let{flowArg:n,dataArg:r,storeArg:s,storeDirArg:o,resumeRequested:a,pauseRequested:i,statusRequested:u}=t;if((i||u)&&(r||a||n))throw new Error("[step-machine-cli] --pause and --status are store-level operations. Do not provide flow, data, or --resume.");if(a&&r)throw new Error("[step-machine-cli] --initial-data cannot be combined with --resume.");let c=ye(s,o);if(u){await xe(c);return}if(i){await we(c);return}if(!n)throw new Error("[step-machine-cli] Flow path is required for run/resume operations.");let d=Ft(n),l=v.dirname(d),f=Re(r),{store:h}=c,w=await I(d),p=ke(w,l);$t(c);let g=new AbortController,x=false,P=H(w,p,{store:h,signal:g.signal,onStep:()=>{!x&&et(c)&&(x=true,g.abort());}}),S;if(a){if(S=await Ct(c),!S){console.warn("[step-machine-cli] No paused run found in store directory."),console.log(JSON.stringify({status:"noop",reason:"no-paused-run"},null,2));return}}else c.storeType==="file"&&!f&&(S=await Ct(c));let y=S?await P.resume(S):await P.run(f);if(x&&y.status==="cancelled"){let M=await Se(h,y.runId);$t(c),console.log(JSON.stringify({runId:y.runId,status:"paused",currentStep:M?.currentStep,pausedAt:M?.pausedAt,stepHistory:y.stepHistory,data:y.data},null,2));return}if(y.status!=="completed"){let M=y.error?.message??y.intent??y.status;throw console.error(`[step-machine-cli] Run failed: ${M}`),new L(1)}console.log(JSON.stringify({runId:y.runId,status:y.status,intent:y.intent,finalStep:y.finalStep,stepHistory:y.stepHistory,data:y.data},null,2));}function he(e){let t=new Set(["--initial-data","--store","--store-dir"]),n={},r=[],s=false,o=false,a=false,i=false;for(let u=0;u<e.length;u++){let c=e[u];if(c==="-h"||c==="--help"){s=true;continue}if(c==="--resume"){o=true;continue}if(c==="--pause"){a=true;continue}if(c==="--status"){i=true;continue}if(t.has(c)){let d=e[u+1];if(!d||d.startsWith("--"))throw new Error(`[step-machine-cli] Missing value for ${c}.`);n[c]=d,u++;continue}if(c.startsWith("--"))throw new Error(`[step-machine-cli] Unknown flag: ${c}`);r.push(c);}if([o,a,i].filter(Boolean).length>1)throw new Error("[step-machine-cli] Use only one of --resume, --pause, or --status at a time.");return {help:s,flowArg:r[0],dataArg:n["--initial-data"],storeArg:String(n["--store"]??"memory").toLowerCase(),storeDirArg:n["--store-dir"],resumeRequested:o,pauseRequested:a,statusRequested:i}}function Ft(e){return v.isAbsolute(e)?e:v.resolve(process.cwd(),e)}function ye(e,t){if(e!=="memory"&&e!=="file")throw new Error(`[step-machine-cli] Invalid --store value "${e}". Expected "memory" or "file".`);if(e==="memory")return {storeType:e,storeDir:void 0,pauseFilePath:void 0,store:new k};if(!t||t.trim().length===0)throw new Error("[step-machine-cli] --store file requires --store-dir <directory>.");let n=Ft(t);return {storeType:e,storeDir:n,pauseFilePath:v.join(n,me),store:new j(Tt(n))}}async function tt(e){if(!e.listRuns)return [];let t=await e.listRuns(),n=[];for(let r of t){let s=await e.loadRunState(r);s&&n.push(s);}return n.sort((r,s)=>(s.updatedAt??s.startedAt??0)-(r.updatedAt??r.startedAt??0)),n}function et(e){return e.storeType!=="file"||!e.pauseFilePath?false:m.existsSync(e.pauseFilePath)}function $t(e){et(e)&&m.unlinkSync(e.pauseFilePath);}async function we(e){if(e.storeType!=="file"||!e.pauseFilePath)throw new Error("[step-machine-cli] --pause requires --store file --store-dir <directory>.");let t=await tt(e.store);if(t.length===0){console.warn("[step-machine-cli] No runs found in store directory. Pause is a no-op."),console.log(JSON.stringify({status:"noop",reason:"no-runs"},null,2));return}if(!t.find(r=>r.status==="running")){console.warn("[step-machine-cli] No running run found. Pause is a no-op."),console.log(JSON.stringify({status:"noop",reason:"no-running-run"},null,2));return}m.mkdirSync(e.storeDir,{recursive:true}),m.writeFileSync(e.pauseFilePath,JSON.stringify({requestedAt:Date.now()}),"utf-8"),console.log(JSON.stringify({status:"pause-requested",storeDir:e.storeDir},null,2));}async function Ct(e){let n=(await tt(e.store)).filter(r=>r.status==="paused");if(n.length!==0)return n.length>1&&console.warn("[step-machine-cli] Multiple paused runs found; resuming the most recently updated run."),n[0].runId}async function Se(e,t){let n=await e.loadRunState(t);if(!n)return null;let r={...n,status:"paused",pausedAt:Date.now(),updatedAt:Date.now()};return await e.saveRunState(t,r),r}async function xe(e){if(e.storeType!=="file")throw new Error("[step-machine-cli] --status requires --store file --store-dir <directory>.");let t=await tt(e.store),n={store:"file",storeDir:e.storeDir,pauseRequested:et(e),totalRuns:t.length,runs:t.map(r=>({runId:r.runId,status:r.status,currentStep:r.currentStep,startedAt:r.startedAt,updatedAt:r.updatedAt,pausedAt:r.pausedAt}))};console.log(JSON.stringify(n,null,2));}function Re(e){if(e)try{let t=JSON.parse(e);if(!t||typeof t!="object"||Array.isArray(t))throw new Error("Initial data must be a JSON object.");return t}catch(t){let n=t instanceof Error?t.message:String(t);throw new Error(`[step-machine-cli] Invalid --initial-data value: ${n}`)}}function ve(e){if(!e||typeof e!="object")return e;let t=e;if(typeof t.whatToRun!="string"||!t.whatToRun.startsWith("b64:"))return e;try{let n=t.whatToRun.slice(4),r=n+"=".repeat((4-n.length%4)%4),s=Buffer.from(r.replace(/-/g,"+").replace(/_/g,"/"),"base64").toString("utf8"),o=JSON.parse(s);return !o||typeof o!="object"||typeof o.value!="string"?e:{...t,whatToRun:o}}catch{return e}}function ke(e,t){return Q(e,{invoke:(r,s)=>jt(ve(r),s,{cliDir:t,cwd:t})})}function be(){console.error("Usage: step-machine-cli <step-flow.yaml> [--initial-data <json>] [--store <memory|file>] [--store-dir <directory>] [--resume]"),console.error(" step-machine-cli --store file --store-dir <directory> --pause"),console.error(" step-machine-cli --store file --store-dir <directory> --status"),console.error(""),console.error("Example:"),console.error(' step-machine-cli examples/cli/step-machine-demo/two-step-math.flow.yaml --initial-data "{"a":3,"b":4}"'),console.error(" step-machine-cli ./flow.yaml --store file --store-dir ./.runs"),console.error(" step-machine-cli ./flow.yaml --store file --store-dir ./.runs --resume"),console.error(" step-machine-cli --store file --store-dir ./.runs --pause"),console.error(" step-machine-cli --store file --store-dir ./.runs --status");}
|
|
4
|
+
export{L as CliExitError,qn as cli};//# sourceMappingURL=step-machine-cli.js.map
|
|
5
5
|
//# sourceMappingURL=step-machine-cli.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as ExecutionRef } from './execution-interface-
|
|
1
|
+
import { E as ExecutionRef } from './execution-interface-ftO1W7Po.js';
|
|
2
2
|
import { L as LiveCard, B as BoardStatusObject, J as JournalStorageAdapter, O as OutputStoreEvent } from './board-live-cards-lib-tjYsPt5U.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# yaml-flow — Architecture & Reuse Guide
|
|
2
|
+
|
|
3
|
+
A quick reference for anyone integrating yaml-flow into their own stack,
|
|
4
|
+
or adapting it to different infrastructure.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Package exports — layered architecture
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
12
|
+
│ YOUR HOST SHIM (thin, you write — a few dozen lines) │
|
|
13
|
+
│ Adapt your platform's request/response → RuntimeRequest/ │
|
|
14
|
+
│ RuntimeResponse. Wire SSE streaming. Construct adapters. │
|
|
15
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
16
|
+
│ PLATFORM ADAPTER → board-live-cards-node │
|
|
17
|
+
│ FS + local-process reference implementation. │
|
|
18
|
+
│ Replace this layer for different storage/invocation infra. │
|
|
19
|
+
│ Implement: BoardPlatformAdapter, CardStore, ArtifactsStore, ... │
|
|
20
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
21
|
+
│ SERVER ORCHESTRATION → board-live-cards-server-runtime │
|
|
22
|
+
│ Routes, board lifecycle, chat/file orchestration. │
|
|
23
|
+
│ Platform-free — all I/O injected via adapters. │
|
|
24
|
+
│ Works on Node HTTP, Azure Functions, Cloudflare Workers, etc. │
|
|
25
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
26
|
+
│ STORAGE CONTRACTS → card-store-public, artifacts-store-public │
|
|
27
|
+
│ Interfaces only. No implementations. Code against these. │
|
|
28
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
29
|
+
│ CORE LOGIC → . / board-live-cards-public, step-machine-public │
|
|
30
|
+
│ Pure computation — card graph, event engine, step machine. │
|
|
31
|
+
│ No FS, no Node, no HTTP. Works anywhere (browser included). │
|
|
32
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Reuse everything except the platform adapter layer.**
|
|
36
|
+
To move from Node+FS to Azure Functions+Cosmos DB:
|
|
37
|
+
- Implement your own `BoardPlatformAdapter` backed by Cosmos/Azure Blob
|
|
38
|
+
- Write a thin host shim that maps Azure `HttpRequest` → `RuntimeRequest`
|
|
39
|
+
- Extend ref dispatch for new `howToRun` kinds via `createExecutionRefInvoker({ transports, syncTransports })` — no layer replacement needed
|
|
40
|
+
- Everything else — server runtime, core logic, client bundles — is unchanged
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Worker side — board-worker-adapter
|
|
45
|
+
|
|
46
|
+
Used by any **external process** the board dispatches work to:
|
|
47
|
+
task executors, chat handlers, inference workers, etc.
|
|
48
|
+
|
|
49
|
+
Provides the full worker contract in a single zero-dependency file:
|
|
50
|
+
- `parseRef` / `serializeRef` — wire-format ref decoding
|
|
51
|
+
- `blobStorageForRef` — resolve a ref to its storage backend (`read`/`write`)
|
|
52
|
+
- `reportComplete` / `reportFailed` — call back to the board on completion
|
|
53
|
+
- `ExecutionRef`, `TaskCallback`, `BlobStorage` — the stable protocol types
|
|
54
|
+
|
|
55
|
+
**Adapting to different infrastructure:**
|
|
56
|
+
|
|
57
|
+
| What changes | What to do |
|
|
58
|
+
|---|---|
|
|
59
|
+
| Storage backend (Cosmos, Azure Blob, S3) | Add a new `case` in `blobStorageForRef()` for the new `KindValueRef.kind` |
|
|
60
|
+
| Callback transport (Service Bus, queue) | Add a new `case` in `reportComplete`/`reportFailed` for the new `howToRun` value |
|
|
61
|
+
| Worker hosting (Azure Functions, Lambda) | Nothing here — the worker reads its inRef and calls `reportComplete`; hosting is transparent |
|
|
62
|
+
|
|
63
|
+
Tip: copy this file into your worker project and extend locally.
|
|
64
|
+
The interfaces are the stable contract; backends are just switch cases.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Browser bundles
|
|
69
|
+
|
|
70
|
+
All browser bundles are standalone IIFE files — no bundler required.
|
|
71
|
+
Include them with a `<script>` tag. Each sets a global on `window`.
|
|
72
|
+
|
|
73
|
+
### compute-jsonata.js → `window.jsonataSync`
|
|
74
|
+
|
|
75
|
+
Vendored jsonata engine for in-browser card computation.
|
|
76
|
+
**Must be loaded before** `board-livecards-localstorage.js`.
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<script src="compute-jsonata.js"></script>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
No public API — just sets `window.jsonataSync` for the other bundles to pick up.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### board-livecards-localstorage.js → `window.BoardLiveCardsLocalStorage`
|
|
87
|
+
|
|
88
|
+
**Drop-in full board engine for the browser** — no server required.
|
|
89
|
+
|
|
90
|
+
Bundles: board engine + card computation + localStorage adapter.
|
|
91
|
+
Ideal for demos, offline use, or prototyping before wiring up a server.
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<script src="compute-jsonata.js"></script>
|
|
95
|
+
<script src="board-livecards-localstorage.js"></script>
|
|
96
|
+
<script>
|
|
97
|
+
const app = BoardLiveCardsLocalStorage.create('my-board', {
|
|
98
|
+
cards: [ /* card JSON objects */ ],
|
|
99
|
+
taskExecutor: async (ref, args) => { /* your async task handler */ },
|
|
100
|
+
});
|
|
101
|
+
await app.bootstrap();
|
|
102
|
+
const state = app.getState();
|
|
103
|
+
// → feed to live-cards.js via LiveCard.init(...)
|
|
104
|
+
</script>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Key exports: `create()`, `selectLiveCardModel()`, `selectAllLiveCardModels()`
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### board-livecards-client.js → `window.BoardLiveCardsClient`
|
|
112
|
+
|
|
113
|
+
**Client for a yaml-flow HTTP/SSE server.** Two modes in one bundle:
|
|
114
|
+
|
|
115
|
+
1. **SSE/HTTP mode** — connects to a `board-live-cards-server-runtime` host:
|
|
116
|
+
```html
|
|
117
|
+
<script src="board-livecards-client.js"></script>
|
|
118
|
+
<script>
|
|
119
|
+
const client = BoardLiveCardsClient.createBoardRuntimeClient({
|
|
120
|
+
fetchServer,
|
|
121
|
+
boardPaths: BoardLiveCardsClient.defaultBoardPaths('my-board'),
|
|
122
|
+
getServerOrigin,
|
|
123
|
+
});
|
|
124
|
+
await client.bootstrapBoard({ boardId: 'my-board', rootElement: el });
|
|
125
|
+
</script>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
2. **Custom transport mode** — use the platform-free state layer with Firebase,
|
|
129
|
+
WebSocket, or any other transport. Import `buildBoardState`, `applyNotification`,
|
|
130
|
+
`selectAllLiveCardModels` and drive them yourself.
|
|
131
|
+
|
|
132
|
+
Key exports: `createBoardRuntimeClient`, `defaultBoardPaths`, `singleBoardPaths`,
|
|
133
|
+
`buildBoardState`, `applyNotification`, `selectLiveCardModel`, `selectAllLiveCardModels`
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### live-cards.js → `window.LiveCard`
|
|
138
|
+
|
|
139
|
+
**UI rendering engine** — turns board state (card models) into interactive HTML.
|
|
140
|
+
|
|
141
|
+
Renders card view definitions (tables, metrics, charts, markdown, forms, badges, etc.)
|
|
142
|
+
into DOM elements. Handles card-to-card `requires`/`provides` token wiring,
|
|
143
|
+
action buttons, chat panels, file attachments, and editable fields.
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<script src="live-cards.js"></script>
|
|
147
|
+
<script>
|
|
148
|
+
const engine = LiveCard.init({
|
|
149
|
+
resolve, // (nodeId) → card model
|
|
150
|
+
onPatch, // called when user edits a field
|
|
151
|
+
onAction, // called on action button click
|
|
152
|
+
getChatMessages, // () → messages for chat panel
|
|
153
|
+
markdown, // optional: (text) → html (e.g. marked.parse)
|
|
154
|
+
sanitize, // optional: (html) → safe html (e.g. DOMPurify.sanitize)
|
|
155
|
+
chartLib, // optional: Chart.js constructor
|
|
156
|
+
});
|
|
157
|
+
</script>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Optional external JS** — all injected, none bundled:
|
|
161
|
+
|
|
162
|
+
| Library | Purpose | Inject via |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| [Bootstrap 5](https://getbootstrap.com/) | Layout, forms, badges | `<link>` + `<script>` (CSS + JS) |
|
|
165
|
+
| [Chart.js](https://www.chartjs.org/) | Chart rendering in cards | `chartLib` config param |
|
|
166
|
+
| [marked](https://marked.js.org/) | Markdown → HTML | `markdown` config param |
|
|
167
|
+
| [DOMPurify](https://github.com/cure53/DOMPurify) | Sanitize markdown output | `sanitize` config param |
|
|
168
|
+
|
|
169
|
+
Bootstrap CSS is required for layout. Everything else is optional — if not provided,
|
|
170
|
+
charts fall back to tables and markdown renders as escaped plain text.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Typical script load order (full browser setup)
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<!-- 1. Optional external libs -->
|
|
178
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">
|
|
179
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js"></script>
|
|
180
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
181
|
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
182
|
+
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
|
183
|
+
|
|
184
|
+
<!-- 2. yaml-flow bundles — order matters -->
|
|
185
|
+
<script src="compute-jsonata.js"></script> <!-- sets window.jsonataSync -->
|
|
186
|
+
<script src="board-livecards-localstorage.js"></script> <!-- or board-livecards-client.js -->
|
|
187
|
+
<script src="live-cards.js"></script> <!-- sets window.LiveCard -->
|
|
188
|
+
```
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
20
20
|
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
|
21
21
|
<script src="https://cdn.jsdelivr.net/npm/leader-line/leader-line.min.js"></script>
|
|
22
|
-
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.
|
|
23
|
-
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.
|
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.3/browser/live-cards.js"></script>
|
|
23
|
+
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.3/browser/board-livecards-client.js"></script>
|
|
24
24
|
</head>
|
|
25
25
|
<body class="bg-light">
|
|
26
26
|
<div class="container-fluid py-3">
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
<title>Example Board Demo (LocalStorage Runtime)</title>
|
|
7
7
|
<link rel="icon" type="image/svg+xml" href="../../browser/favicon.svg" />
|
|
8
8
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
|
|
9
|
-
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.
|
|
9
|
+
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.3/browser/compute-jsonata.js"></script>
|
|
10
10
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
11
11
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
12
12
|
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
|
13
13
|
<script src="https://cdn.jsdelivr.net/npm/leader-line/leader-line.min.js"></script>
|
|
14
|
-
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.
|
|
15
|
-
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.
|
|
14
|
+
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.3/browser/live-cards.js"></script>
|
|
15
|
+
<script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.0.3/browser/board-livecards-localstorage.js"></script>
|
|
16
16
|
</head>
|
|
17
17
|
<body class="bg-light">
|
|
18
18
|
<div class="container-fluid py-3">
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Ref handler: reads JSON from stdin, doubles the "sum" field, writes JSON to stdout.
|
|
3
|
+
|
|
4
|
+
let raw = '';
|
|
5
|
+
process.stdin.setEncoding('utf-8');
|
|
6
|
+
process.stdin.on('data', (chunk) => { raw += chunk; });
|
|
7
|
+
process.stdin.on('end', () => {
|
|
8
|
+
try {
|
|
9
|
+
const input = JSON.parse(raw || '{}');
|
|
10
|
+
const sum = Number(input.sum);
|
|
11
|
+
if (!Number.isFinite(sum)) {
|
|
12
|
+
process.stderr.write('double-handler requires numeric sum\n');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
process.stdout.write(JSON.stringify({ doubled: sum * 2 }));
|
|
16
|
+
} catch (err) {
|
|
17
|
+
process.stderr.write(String(err) + '\n');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "mixed-handlers-demo",
|
|
3
|
+
"settings": {
|
|
4
|
+
"start_step": "step1_compute",
|
|
5
|
+
"max_total_steps": 15,
|
|
6
|
+
"timeout_ms": 15000
|
|
7
|
+
},
|
|
8
|
+
"steps": {
|
|
9
|
+
"step1_compute": {
|
|
10
|
+
"description": "Inline compute — adds two numbers using JSONata",
|
|
11
|
+
"expects_data": ["a", "b"],
|
|
12
|
+
"produces_data": ["sum"],
|
|
13
|
+
"input_validations": [
|
|
14
|
+
"$type(a) = \"number\"",
|
|
15
|
+
"$type(b) = \"number\""
|
|
16
|
+
],
|
|
17
|
+
"handler": {
|
|
18
|
+
"type": "compute-jsonata",
|
|
19
|
+
"expr": [
|
|
20
|
+
"data.sum = expects_data.a + expects_data.b",
|
|
21
|
+
"result = \"success\""
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"transitions": {
|
|
25
|
+
"success": "step2_ref",
|
|
26
|
+
"failure": "failed_state"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"step2_ref": {
|
|
30
|
+
"description": "Ref handler — doubles the sum via an external Node script",
|
|
31
|
+
"expects_data": ["sum"],
|
|
32
|
+
"produces_data": ["doubled"],
|
|
33
|
+
"handler": {
|
|
34
|
+
"type": "ref",
|
|
35
|
+
"howToRun": "local-node",
|
|
36
|
+
"whatToRun": { "kind": "fs-path", "value": "./double-handler.js" }
|
|
37
|
+
},
|
|
38
|
+
"transitions": {
|
|
39
|
+
"success": "step3_scale_each",
|
|
40
|
+
"failure": "failed_state"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"step3_scale_each": {
|
|
44
|
+
"description": "forEach + compute-jsonata — scale doubled value by each multiplier",
|
|
45
|
+
"expects_data": ["doubled", "multipliers"],
|
|
46
|
+
"produces_data": ["scaled_values"],
|
|
47
|
+
"forEach": {
|
|
48
|
+
"items": "multipliers",
|
|
49
|
+
"as": "multiplier",
|
|
50
|
+
"concurrency": 3,
|
|
51
|
+
"collectAs": "scaled_values"
|
|
52
|
+
},
|
|
53
|
+
"handler": {
|
|
54
|
+
"type": "compute-jsonata",
|
|
55
|
+
"expr": [
|
|
56
|
+
"data.value = expects_data.doubled * expects_data.multiplier",
|
|
57
|
+
"result = \"success\""
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"transitions": {
|
|
61
|
+
"success": "step4_ref_each",
|
|
62
|
+
"failure": "failed_state"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"step4_ref_each": {
|
|
66
|
+
"description": "forEach + ref — re-scale each value via an external Node script",
|
|
67
|
+
"expects_data": ["scaled_values"],
|
|
68
|
+
"produces_data": ["final_values"],
|
|
69
|
+
"forEach": {
|
|
70
|
+
"items": "scaled_values",
|
|
71
|
+
"as": "item",
|
|
72
|
+
"concurrency": 3,
|
|
73
|
+
"collectAs": "final_values"
|
|
74
|
+
},
|
|
75
|
+
"handler": {
|
|
76
|
+
"type": "ref",
|
|
77
|
+
"howToRun": "local-node",
|
|
78
|
+
"whatToRun": { "kind": "fs-path", "value": "./scale-handler.js" },
|
|
79
|
+
"argsMassaging": {
|
|
80
|
+
"cmdTemplate": [
|
|
81
|
+
"$string(item.value)",
|
|
82
|
+
"'2'"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"transitions": {
|
|
87
|
+
"success": "step5_grade",
|
|
88
|
+
"failure": "failed_state"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"step5_grade": {
|
|
92
|
+
"description": "Inline compute — classifies whether any final value exceeds 200",
|
|
93
|
+
"expects_data": ["final_values"],
|
|
94
|
+
"produces_data": ["grade"],
|
|
95
|
+
"handler": {
|
|
96
|
+
"type": "compute-jsonata",
|
|
97
|
+
"expr": [
|
|
98
|
+
"data.grade = $max(expects_data.final_values.scaled) >= 200 ? \"large\" : \"small\"",
|
|
99
|
+
"result = \"success\""
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"transitions": {
|
|
103
|
+
"success": "success_state",
|
|
104
|
+
"failure": "failed_state"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"terminal_states": {
|
|
109
|
+
"success_state": {
|
|
110
|
+
"return_intent": "success",
|
|
111
|
+
"return_artifacts": ["a", "b", "sum", "doubled", "scaled_values", "final_values", "grade"]
|
|
112
|
+
},
|
|
113
|
+
"failed_state": {
|
|
114
|
+
"return_intent": "failure",
|
|
115
|
+
"return_artifacts": ["error"]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Ref handler: takes value and multiplier as CLI args, writes JSON to stdout.
|
|
3
|
+
// Usage: node scale-handler.js <value> <multiplier>
|
|
4
|
+
|
|
5
|
+
const value = Number(process.argv[2]);
|
|
6
|
+
const multiplier = Number(process.argv[3]);
|
|
7
|
+
|
|
8
|
+
if (!Number.isFinite(value) || !Number.isFinite(multiplier)) {
|
|
9
|
+
process.stderr.write('scale-handler requires numeric value and multiplier as CLI args\n');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
process.stdout.write(JSON.stringify({ scaled: value * multiplier }));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Ref handler: reads JSON from stdin, doubles the "sum" field, writes JSON to stdout.
|
|
3
|
+
|
|
4
|
+
let raw = '';
|
|
5
|
+
process.stdin.setEncoding('utf-8');
|
|
6
|
+
process.stdin.on('data', (chunk) => { raw += chunk; });
|
|
7
|
+
process.stdin.on('end', () => {
|
|
8
|
+
try {
|
|
9
|
+
const input = JSON.parse(raw || '{}');
|
|
10
|
+
const sum = Number(input.sum);
|
|
11
|
+
if (!Number.isFinite(sum)) {
|
|
12
|
+
process.stderr.write('double-handler requires numeric sum\n');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
process.stdout.write(JSON.stringify({ doubled: sum * 2 }));
|
|
16
|
+
} catch (err) {
|
|
17
|
+
process.stderr.write(String(err) + '\n');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
});
|