@flowriki/agent 0.1.0-beta.4 → 0.1.0-beta.6
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/dist/accname.global.js +1 -0
- package/dist/{chunk-7JLRYAQ2.js → chunk-ZUHML4TR.js} +141 -74
- package/dist/cli.js +1 -1
- package/dist/engine.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(()=>{function U(e,t,i){function u(r){let o=e.__flAccName;if(o){let a=o(r);return a||t(r.placeholder)||t(r.title)||""}return b(r)}function b(r){let o=r.getAttribute&&r.getAttribute("aria-labelledby");if(o){let d=o.split(/\s+/).map(g=>document.getElementById(g)).filter(Boolean).map(g=>t(g.textContent)).filter(Boolean).join(" ");if(d)return d}let a=t(r.getAttribute&&r.getAttribute("aria-label"));if(a)return a;if(r.id){let d=document.querySelector('label[for="'+CSS.escape(r.id)+'"]');if(d&&t(d.textContent))return t(d.textContent)}let l=r.closest&&r.closest("label");if(l&&t(l.textContent))return t(l.textContent);let s=r.tagName.toLowerCase(),p=(r.type||"").toLowerCase();return(["button","a","summary"].includes(s)||r.getAttribute&&r.getAttribute("role")==="button")&&t(r.textContent)?t(r.textContent):s==="input"&&["submit","button","reset"].includes(p)&&r.value?t(r.value):t(r.placeholder)||t(r.title)||""}let S="input,select,textarea,button,meter,output,progress,[contenteditable]";function y(r){let o=r.parentElement;if(!o||!r.matches?.(S))return"";for(let a of Array.from(o.querySelectorAll("label"))){let l=a.getAttribute("for");if(l&&document.getElementById(l))continue;let s=t(a.textContent);if(s)return s}return""}let E=new WeakMap,P=crypto.randomUUID(),_=0;function T(r){let o=E.get(r);o||(o=`${P}:${++_}`,E.set(r,o));let a={__targetId:o},l=r.getRootNode(),s=r.getAttribute("data-testid");if(s&&l.querySelectorAll(`[data-testid=${JSON.stringify(s)}]`).length===1)return{...a,name:u(r),locator:{strategy:"testId",value:s}};if(r.type==="password"&&u(r))return{...a,name:u(r),locator:{strategy:"label",value:u(r)}};let p=r.getAttribute("title");if(p&&l.querySelectorAll(`[title=${JSON.stringify(p)}]`).length===1)return{...a,name:u(r),locator:{strategy:"title",value:p}};let d=u(r);if(d)return{...a,name:d};let g=y(r);if(g)return{...a,name:g,nameFrom:"adjacent-label"};let v=t(Array.from(r.childNodes||[]).filter(A=>A.nodeType===3).map(A=>A.textContent).join(" "));return v&&v.length<=80?{...a,name:v,nameFrom:"text"}:r.id&&!/:r|[a-f0-9]{12}|\d{6}/i.test(r.id)&&l.querySelectorAll(`#${CSS.escape(r.id)}`).length===1?{...a,name:"",locator:{strategy:"css",value:`#${CSS.escape(r.id)}`}}:{...a,name:""}}function N(r){let o=r.getAttribute&&r.getAttribute("role");if(o)return o;let a=r.tagName.toLowerCase(),l=(r.type||"").toLowerCase();return a==="button"?"button":a==="a"&&r.href?"link":a==="select"?r.multiple||r.size>1?"listbox":"combobox":a==="textarea"?"textbox":a==="input"?["submit","button","reset","file"].includes(l)?"button":l==="checkbox"||l==="radio"?l:l==="number"?"spinbutton":l==="search"?"searchbox":l==="range"?"slider":"textbox":r.isContentEditable?"textbox":H[a]??""}let H={li:"listitem",ul:"list",ol:"list",tr:"row",td:"cell",th:"columnheader",table:"table",nav:"navigation",main:"main",header:"banner",footer:"contentinfo",aside:"complementary",form:"form",dialog:"dialog",details:"group",fieldset:"group",img:"img",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading"};function j(r){let o=r,a=null;for(let l=0;o&&l<6&&!(o===document.body||o===document.documentElement);l++){let s="";try{s=getComputedStyle(o).cursor}catch{break}if(s==="pointer")a=o;else if(a)break;o=o.parentElement}return a}let C="tr, [role=row], li, [role=listitem], [role=article], [role=gridcell]";function L(r){try{let o=r.cloneNode(!0);for(let a of Array.from(o.querySelectorAll(i+", input, select, textarea")))a.remove();for(let a of Array.from(o.querySelectorAll("td, th, li, p, div, span, tr")))a.append(" ");return t(o.textContent).slice(0,120)}catch{return""}}function D(r,o,a){if(!a)return!1;let l;try{l=Array.from(document.querySelectorAll(i+", input, select, textarea"))}catch{return!1}if(l.length>400)return!0;let s=0;for(let p of l)if(p!==r&&N(p)===o&&T(p).name===a&&++s>0)return!0;return!1}function V(r,o,a){if(!D(r,o,a))return null;let l=r.closest&&r.closest(C);if(!l)return null;let s=L(l);if(!s)return null;let p=l.getAttribute&&l.getAttribute("role"),d=l.tagName.toLowerCase();return{scope:s,scopeRole:p||(d==="tr"?"row":d==="li"?"listitem":d)}}function x(r,o,a){let l=V(r,o,a);return l||{scope:n(r)}}function n(r){let o=r.closest&&r.closest("form, fieldset, section, [role=group], [role=region], [role=dialog], [role=alertdialog], dialog");return o?u(o):""}function f(r){if(r.id)return"#"+CSS.escape(r.id);let o=[],a=r;for(;a&&a.nodeType===1&&a!==document.body;){let l=Array.from(a.parentNode?a.parentNode.children:[]).filter(s=>s.tagName===a.tagName);o.unshift(l.length>1?`${a.tagName.toLowerCase()}:nth-of-type(${l.indexOf(a)+1})`:a.tagName.toLowerCase()),a=a.parentElement}return"body > "+o.join(" > ")}return{accName:u,identify:T,roleOf:N,pointerTarget:j,scoping:x,cssPath:f}}var ge=Object.prototype.toString;function Q(e){return typeof e=="function"||ge.call(e)==="[object Function]"}function ye(e){var t=Number(e);return isNaN(t)?0:t===0||!isFinite(t)?t:(t>0?1:-1)*Math.floor(Math.abs(t))}var he=Math.pow(2,53)-1;function ve(e){var t=ye(e);return Math.min(Math.max(t,0),he)}function h(e,t){var i=Array,u=Object(e);if(e==null)throw new TypeError("Array.from requires an array-like object - not null or undefined");if(typeof t<"u"&&!Q(t))throw new TypeError("Array.from: when provided, the second argument must be a function");for(var b=ve(u.length),S=Q(i)?Object(new i(b)):new Array(b),y=0,E;y<b;)E=u[y],t?S[y]=t(E,y):S[y]=E,y+=1;return S.length=b,S}function I(e){"@babel/helpers - typeof";return I=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},I(e)}function we(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function X(e,t){for(var i=0;i<t.length;i++){var u=t[i];u.enumerable=u.enumerable||!1,u.configurable=!0,"value"in u&&(u.writable=!0),Object.defineProperty(e,Y(u.key),u)}}function Se(e,t,i){return t&&X(e.prototype,t),i&&X(e,i),Object.defineProperty(e,"prototype",{writable:!1}),e}function Ee(e,t,i){return t=Y(t),t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function Y(e){var t=xe(e,"string");return I(t)=="symbol"?t:t+""}function xe(e,t){if(I(e)!="object"||!e)return e;var i=e[Symbol.toPrimitive];if(i!==void 0){var u=i.call(e,t||"default");if(I(u)!="object")return u;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var Ae=(function(){function e(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[];we(this,e),Ee(this,"items",void 0),this.items=t}return Se(e,[{key:"add",value:function(i){return this.has(i)===!1&&this.items.push(i),this}},{key:"clear",value:function(){this.items=[]}},{key:"delete",value:function(i){var u=this.items.length;return this.items=this.items.filter(function(b){return b!==i}),u!==this.items.length}},{key:"forEach",value:function(i){var u=this;this.items.forEach(function(b){i(b,b,u)})}},{key:"has",value:function(i){return this.items.indexOf(i)!==-1}},{key:"size",get:function(){return this.items.length}}])})(),Z=typeof Set>"u"?Set:Ae;function m(e){var t;return(t=e.localName)!==null&&t!==void 0?t:e.tagName.toLowerCase()}var Le={article:"article",aside:"complementary",button:"button",datalist:"listbox",dd:"definition",details:"group",dialog:"dialog",dt:"term",fieldset:"group",figure:"figure",form:"form",footer:"contentinfo",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading",header:"banner",hr:"separator",html:"document",legend:"legend",li:"listitem",math:"math",main:"main",menu:"list",nav:"navigation",ol:"list",optgroup:"group",option:"option",output:"status",progress:"progressbar",section:"region",summary:"button",table:"table",tbody:"rowgroup",textarea:"textbox",tfoot:"rowgroup",td:"cell",th:"columnheader",thead:"rowgroup",tr:"row",ul:"list"},Te={caption:new Set(["aria-label","aria-labelledby"]),code:new Set(["aria-label","aria-labelledby"]),deletion:new Set(["aria-label","aria-labelledby"]),emphasis:new Set(["aria-label","aria-labelledby"]),generic:new Set(["aria-label","aria-labelledby","aria-roledescription"]),insertion:new Set(["aria-label","aria-labelledby"]),none:new Set(["aria-label","aria-labelledby"]),paragraph:new Set(["aria-label","aria-labelledby"]),presentation:new Set(["aria-label","aria-labelledby"]),strong:new Set(["aria-label","aria-labelledby"]),subscript:new Set(["aria-label","aria-labelledby"]),superscript:new Set(["aria-label","aria-labelledby"])};function Ne(e,t){return["aria-atomic","aria-busy","aria-controls","aria-current","aria-description","aria-describedby","aria-details","aria-dropeffect","aria-flowto","aria-grabbed","aria-hidden","aria-keyshortcuts","aria-label","aria-labelledby","aria-live","aria-owns","aria-relevant","aria-roledescription"].some(function(i){var u;return e.hasAttribute(i)&&!((u=Te[t])!==null&&u!==void 0&&u.has(i))})}function ee(e,t){return Ne(e,t)}function k(e){var t=Ie(e);if(t===null||O.indexOf(t)!==-1){var i=Ce(e);if(O.indexOf(t||"")===-1||ee(e,i||""))return i}return t}function Ce(e){var t=Le[m(e)];if(t!==void 0)return t;switch(m(e)){case"a":case"area":case"link":if(e.hasAttribute("href"))return"link";break;case"img":return e.getAttribute("alt")===""&&!ee(e,"img")?"presentation":"img";case"input":{var i=e,u=i.type;switch(u){case"button":case"image":case"reset":case"submit":return"button";case"checkbox":case"radio":return u;case"range":return"slider";case"email":case"tel":case"text":case"url":return e.hasAttribute("list")?"combobox":"textbox";case"search":return e.hasAttribute("list")?"combobox":"searchbox";case"number":return"spinbutton";default:return null}}case"select":return e.hasAttribute("multiple")||e.size>1?"listbox":"combobox"}return null}function Ie(e){var t=e.getAttribute("role");if(t!==null){var i=t.trim().split(" ")[0];if(i.length>0)return i}return null}var O=["presentation","none"];function c(e){return e!==null&&e.nodeType===e.ELEMENT_NODE}function G(e){return c(e)&&m(e)==="caption"}function R(e){return c(e)&&m(e)==="input"}function te(e){return c(e)&&m(e)==="optgroup"}function re(e){return c(e)&&m(e)==="select"}function ne(e){return c(e)&&m(e)==="table"}function ie(e){return c(e)&&m(e)==="textarea"}function ae(e){var t=e.ownerDocument===null?e:e.ownerDocument,i=t.defaultView;if(i===null)throw new TypeError("no window available");return i}function oe(e){return c(e)&&m(e)==="fieldset"}function ue(e){return c(e)&&m(e)==="legend"}function le(e){return c(e)&&m(e)==="slot"}function Oe(e){return c(e)&&e.ownerSVGElement!==void 0}function se(e){return c(e)&&m(e)==="svg"}function fe(e){return Oe(e)&&m(e)==="title"}function M(e,t){if(c(e)&&e.hasAttribute(t)){var i=e.getAttribute(t).split(" "),u=e.getRootNode?e.getRootNode():e.ownerDocument;return i.map(function(b){return u.getElementById(b)}).filter(function(b){return b!==null})}return[]}function w(e,t){return c(e)?t.indexOf(k(e))!==-1:!1}function Re(e){return e.trim().replace(/\s\s+/g," ")}function Me(e,t){if(!c(e))return!1;if(e.hasAttribute("hidden")||e.getAttribute("aria-hidden")==="true")return!0;var i=t(e);return i.getPropertyValue("display")==="none"||i.getPropertyValue("visibility")==="hidden"}function Pe(e){return w(e,["button","combobox","listbox","textbox"])||me(e,"range")}function me(e,t){if(!c(e))return!1;if(t==="range")return w(e,["meter","progressbar","scrollbar","slider","spinbutton"]);throw new TypeError("No knowledge about abstract role '".concat(t,"'. This is likely a bug :("))}function ce(e,t){var i=h(e.querySelectorAll(t));return M(e,"aria-owns").forEach(function(u){i.push.apply(i,h(u.querySelectorAll(t)))}),i}function _e(e){return re(e)?e.selectedOptions||ce(e,"[selected]"):ce(e,'[aria-selected="true"]')}function je(e){return w(e,O)}function ke(e){return G(e)}function He(e){return w(e,["button","cell","checkbox","columnheader","gridcell","heading","label","legend","link","menuitem","menuitemcheckbox","menuitemradio","option","radio","row","rowheader","switch","tab","tooltip","treeitem"])}function De(e){return!1}function Ve(e){return R(e)||ie(e)?e.value:e.textContent||""}function de(e){var t=e.getPropertyValue("content");return/^["'].*["']$/.test(t)?t.slice(1,-1):""}function pe(e){var t=m(e);return t==="button"||t==="input"&&e.getAttribute("type")!=="hidden"||t==="meter"||t==="output"||t==="progress"||t==="select"||t==="textarea"}function be(e){if(pe(e))return e;var t=null;return e.childNodes.forEach(function(i){if(t===null&&c(i)){var u=be(i);u!==null&&(t=u)}}),t}function $e(e){if(e.control!==void 0)return e.control;var t=e.getAttribute("for");return t!==null?e.ownerDocument.getElementById(t):be(e)}function qe(e){var t=e.labels;if(t===null)return t;if(t!==void 0)return h(t);if(!pe(e))return null;var i=e.ownerDocument;return h(i.querySelectorAll("label")).filter(function(u){return $e(u)===e})}function Ge(e){var t=e.assignedNodes();return t.length===0?h(e.childNodes):t}function B(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=new Z,u=typeof Map>"u"?void 0:new Map,b=ae(e),S=t.compute,y=S===void 0?"name":S,E=t.computedStyleSupportsPseudoElements,P=E===void 0?t.getComputedStyle!==void 0:E,_=t.getComputedStyle,T=_===void 0?b.getComputedStyle.bind(b):_,N=t.hidden,H=N===void 0?!1:N,j=function(f,r){if(r!==void 0)throw new Error("use uncachedGetComputedStyle directly for pseudo elements");if(u===void 0)return T(f);var o=u.get(f);if(o)return o;var a=T(f,r);return u.set(f,a),a};function C(n,f){var r="";if(c(n)&&P){var o=T(n,"::before"),a=de(o);r="".concat(a," ").concat(r)}var l=le(n)?Ge(n):h(n.childNodes).concat(M(n,"aria-owns"));if(l.forEach(function(d){var g=x(d,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0}),v=c(d)?j(d).getPropertyValue("display"):"inline",A=v!=="inline"?" ":"";r+="".concat(A).concat(g).concat(A)}),c(n)&&P){var s=T(n,"::after"),p=de(s);r="".concat(r," ").concat(p)}return r.trim()}function L(n,f){var r=n.getAttributeNode(f);return r!==null&&!i.has(r)&&r.value.trim()!==""?(i.add(r),r.value):null}function D(n){return c(n)?L(n,"title"):null}function V(n){if(!c(n))return null;if(oe(n)){i.add(n);for(var f=h(n.childNodes),r=0;r<f.length;r+=1){var o=f[r];if(ue(o))return x(o,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(ne(n)){i.add(n);for(var a=h(n.childNodes),l=0;l<a.length;l+=1){var s=a[l];if(G(s))return x(s,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(se(n)){i.add(n);for(var p=h(n.childNodes),d=0;d<p.length;d+=1){var g=p[d];if(fe(g))return g.textContent}return null}else if(m(n)==="img"||m(n)==="area"){var v=L(n,"alt");if(v!==null)return v}else if(te(n)){var A=L(n,"label");if(A!==null)return A}if(R(n)&&(n.type==="button"||n.type==="submit"||n.type==="reset")){var z=L(n,"value");if(z!==null)return z;if(n.type==="submit")return"Submit";if(n.type==="reset")return"Reset"}var $=qe(n);if($!==null&&$.length!==0)return i.add(n),h($).map(function(q){return x(q,{isEmbeddedInLabel:!0,isReferenced:!1,recursion:!0})}).filter(function(q){return q.length>0}).join(" ");if(R(n)&&n.type==="image"){var F=L(n,"alt");if(F!==null)return F;var J=L(n,"title");return J!==null?J:"Submit Query"}if(w(n,["button"])){var K=C(n,{isEmbeddedInLabel:!1,isReferenced:!1});if(K!=="")return K}return null}function x(n,f){if(i.has(n))return"";if(!H&&Me(n,j)&&!f.isReferenced)return i.add(n),"";var r=c(n)?n.getAttributeNode("aria-labelledby"):null,o=r!==null&&!i.has(r)?M(n,"aria-labelledby"):[];if(y==="name"&&!f.isReferenced&&o.length>0)return i.add(r),o.map(function(v){return x(v,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!0,recursion:!1})}).join(" ");var a=f.recursion&&Pe(n)&&y==="name";if(!a){var l=(c(n)&&n.getAttribute("aria-label")||"").trim();if(l!==""&&y==="name")return i.add(n),l;if(!je(n)){var s=V(n);if(s!==null)return i.add(n),s}}if(w(n,["menu"]))return i.add(n),"";if(a||f.isEmbeddedInLabel||f.isReferenced){if(w(n,["combobox","listbox"])){i.add(n);var p=_e(n);return p.length===0?R(n)?n.value:"":h(p).map(function(v){return x(v,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0})}).join(" ")}if(me(n,"range"))return i.add(n),n.hasAttribute("aria-valuetext")?n.getAttribute("aria-valuetext"):n.hasAttribute("aria-valuenow")?n.getAttribute("aria-valuenow"):n.getAttribute("value")||"";if(w(n,["textbox"]))return i.add(n),Ve(n)}if(He(n)||c(n)&&f.isReferenced||ke(n)||De(n)){var d=C(n,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});if(d!=="")return i.add(n),d}if(n.nodeType===n.TEXT_NODE)return i.add(n),n.textContent||"";if(f.recursion)return i.add(n),C(n,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});var g=D(n);return g!==null?(i.add(n),g):(i.add(n),"")}return Re(x(e,{isEmbeddedInLabel:!1,isReferenced:y==="description",recursion:!1}))}function Be(e){return w(e,["caption","code","deletion","emphasis","generic","insertion","none","paragraph","presentation","strong","subscript","superscript"])}function W(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};return Be(e)?"":B(e,t)}window.__flAccName=e=>{try{return W(e).replace(/\s+/g," ").trim()}catch{return""}};window.__flCreateTargetIdentity=U;})();
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
// src/engine.ts
|
|
4
4
|
import os5 from "os";
|
|
5
|
-
import { createRequire as
|
|
5
|
+
import { createRequire as createRequire3 } from "module";
|
|
6
6
|
import { spawnSync } from "child_process";
|
|
7
7
|
|
|
8
8
|
// ../agent-core/dist/index.js
|
|
9
9
|
import { z as z4 } from "zod";
|
|
10
10
|
import fs5 from "fs";
|
|
11
|
-
import
|
|
11
|
+
import path5 from "path";
|
|
12
12
|
import crypto22 from "crypto";
|
|
13
|
-
import { createRequire as
|
|
13
|
+
import { createRequire as createRequire2 } from "module";
|
|
14
14
|
import { WebSocket, WebSocketServer } from "ws";
|
|
15
15
|
import fs23 from "fs";
|
|
16
16
|
import os2 from "os";
|
|
@@ -18,7 +18,7 @@ import path23 from "path";
|
|
|
18
18
|
|
|
19
19
|
// ../orchestrator/dist/index.js
|
|
20
20
|
import fs4 from "fs";
|
|
21
|
-
import
|
|
21
|
+
import path4 from "path";
|
|
22
22
|
|
|
23
23
|
// ../protocol/dist/chunk-ROEX7TWK.js
|
|
24
24
|
import { z } from "zod";
|
|
@@ -882,8 +882,8 @@ function installRecorder() {
|
|
|
882
882
|
return Boolean(el?.closest?.("[data-flowriki],[data-flowriki-debug]"));
|
|
883
883
|
}
|
|
884
884
|
function actualTarget(e) {
|
|
885
|
-
const
|
|
886
|
-
const first =
|
|
885
|
+
const path8 = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
886
|
+
const first = path8[0];
|
|
887
887
|
return (first && first.tagName ? first : e.target) ?? null;
|
|
888
888
|
}
|
|
889
889
|
w.__flSignature = () => {
|
|
@@ -2138,14 +2138,15 @@ function installDebugPanel() {
|
|
|
2138
2138
|
// ../runner/dist/index.js
|
|
2139
2139
|
import fs from "fs";
|
|
2140
2140
|
import { createRequire } from "module";
|
|
2141
|
+
import path from "path";
|
|
2142
|
+
import { fileURLToPath } from "url";
|
|
2141
2143
|
import "playwright";
|
|
2142
2144
|
import "playwright";
|
|
2143
2145
|
import { errors } from "playwright";
|
|
2144
2146
|
import fs2 from "fs";
|
|
2145
|
-
import
|
|
2147
|
+
import path2 from "path";
|
|
2146
2148
|
import "playwright";
|
|
2147
2149
|
import fs3 from "fs";
|
|
2148
|
-
import { createRequire as createRequire2 } from "module";
|
|
2149
2150
|
import "playwright";
|
|
2150
2151
|
var DEFAULT_ENVIRONMENT = {
|
|
2151
2152
|
browser: "chromium",
|
|
@@ -2409,15 +2410,24 @@ var COMMITS = /* @__PURE__ */ new Set([
|
|
|
2409
2410
|
"forward"
|
|
2410
2411
|
]);
|
|
2411
2412
|
var cachedAccNameScript = null;
|
|
2413
|
+
var ACCNAME_ASSET = "accname.global.js";
|
|
2412
2414
|
function accNameScript() {
|
|
2413
2415
|
if (cachedAccNameScript === null) {
|
|
2414
|
-
|
|
2415
|
-
"@flowriki/recorder/accname.global.js"
|
|
2416
|
-
);
|
|
2417
|
-
cachedAccNameScript = fs.readFileSync(path24, "utf8");
|
|
2416
|
+
cachedAccNameScript = fs.readFileSync(accNameScriptPath(), "utf8");
|
|
2418
2417
|
}
|
|
2419
2418
|
return cachedAccNameScript;
|
|
2420
2419
|
}
|
|
2420
|
+
function accNameScriptPath() {
|
|
2421
|
+
try {
|
|
2422
|
+
return createRequire(import.meta.url).resolve("@flowriki/recorder/accname.global.js");
|
|
2423
|
+
} catch {
|
|
2424
|
+
const beside = path.join(path.dirname(fileURLToPath(import.meta.url)), ACCNAME_ASSET);
|
|
2425
|
+
if (fs.existsSync(beside)) return beside;
|
|
2426
|
+
throw new Error(
|
|
2427
|
+
`this build is missing ${ACCNAME_ASSET}, which recording injects into the page \u2014 reinstall the agent`
|
|
2428
|
+
);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2421
2431
|
async function startRecordingSession(opts) {
|
|
2422
2432
|
const { headless = false, onStep, onState, onDisconnect, onStopRequested } = opts;
|
|
2423
2433
|
const url = normalizeStartUrl(opts.url);
|
|
@@ -2874,7 +2884,7 @@ async function captureFailure(opts) {
|
|
|
2874
2884
|
const file = `step-${String(ordinal).padStart(2, "0")}.png`;
|
|
2875
2885
|
fs2.mkdirSync(dir, { recursive: true });
|
|
2876
2886
|
await page.screenshot({
|
|
2877
|
-
path:
|
|
2887
|
+
path: path2.join(dir, file),
|
|
2878
2888
|
// The viewport, not the full page: a failure is about what was on screen,
|
|
2879
2889
|
// and a full-page capture of an infinite-scroll app is a 20MB image.
|
|
2880
2890
|
fullPage: false,
|
|
@@ -2925,7 +2935,7 @@ async function finaliseArtifacts(opts) {
|
|
|
2925
2935
|
const out = [];
|
|
2926
2936
|
const size = (file) => {
|
|
2927
2937
|
try {
|
|
2928
|
-
return fs2.statSync(
|
|
2938
|
+
return fs2.statSync(path2.join(dir, file)).size;
|
|
2929
2939
|
} catch {
|
|
2930
2940
|
return null;
|
|
2931
2941
|
}
|
|
@@ -2935,7 +2945,7 @@ async function finaliseArtifacts(opts) {
|
|
|
2935
2945
|
try {
|
|
2936
2946
|
if (keep(artifacts.trace)) {
|
|
2937
2947
|
fs2.mkdirSync(dir, { recursive: true });
|
|
2938
|
-
await context.tracing.stop({ path:
|
|
2948
|
+
await context.tracing.stop({ path: path2.join(dir, file) });
|
|
2939
2949
|
out.push({ kind: "trace", relPath: file, bytes: size(file) });
|
|
2940
2950
|
} else {
|
|
2941
2951
|
await context.tracing.stop();
|
|
@@ -2947,7 +2957,7 @@ async function finaliseArtifacts(opts) {
|
|
|
2947
2957
|
const file = "console.log";
|
|
2948
2958
|
try {
|
|
2949
2959
|
fs2.mkdirSync(dir, { recursive: true });
|
|
2950
|
-
fs2.writeFileSync(
|
|
2960
|
+
fs2.writeFileSync(path2.join(dir, file), consoleLines.join("\n") + "\n");
|
|
2951
2961
|
out.push({ kind: "console", relPath: file, bytes: size(file) });
|
|
2952
2962
|
} catch {
|
|
2953
2963
|
}
|
|
@@ -2962,13 +2972,13 @@ function collectVideos(dir, keep) {
|
|
|
2962
2972
|
return [];
|
|
2963
2973
|
}
|
|
2964
2974
|
if (!keep) {
|
|
2965
|
-
for (const f of files) fs2.rmSync(
|
|
2975
|
+
for (const f of files) fs2.rmSync(path2.join(dir, f), { force: true });
|
|
2966
2976
|
return [];
|
|
2967
2977
|
}
|
|
2968
2978
|
return files.map((f) => {
|
|
2969
2979
|
let bytes = null;
|
|
2970
2980
|
try {
|
|
2971
|
-
bytes = fs2.statSync(
|
|
2981
|
+
bytes = fs2.statSync(path2.join(dir, f)).size;
|
|
2972
2982
|
} catch {
|
|
2973
2983
|
bytes = null;
|
|
2974
2984
|
}
|
|
@@ -3611,8 +3621,8 @@ async function verifySteps(opts) {
|
|
|
3611
3621
|
var cachedAccName = null;
|
|
3612
3622
|
function accNameScript2() {
|
|
3613
3623
|
if (cachedAccName === null) {
|
|
3614
|
-
const
|
|
3615
|
-
cachedAccName = fs3.readFileSync(
|
|
3624
|
+
const path33 = accNameScriptPath();
|
|
3625
|
+
cachedAccName = fs3.readFileSync(path33, "utf8");
|
|
3616
3626
|
}
|
|
3617
3627
|
return cachedAccName;
|
|
3618
3628
|
}
|
|
@@ -3994,7 +4004,7 @@ async function startDebugSession(opts) {
|
|
|
3994
4004
|
|
|
3995
4005
|
// ../rundb/dist/index.js
|
|
3996
4006
|
import os from "os";
|
|
3997
|
-
import
|
|
4007
|
+
import path3 from "path";
|
|
3998
4008
|
var MIGRATIONS2 = [
|
|
3999
4009
|
// 1 — initial
|
|
4000
4010
|
`
|
|
@@ -4141,10 +4151,10 @@ var MIGRATIONS2 = [
|
|
|
4141
4151
|
];
|
|
4142
4152
|
var SCHEMA_VERSION = MIGRATIONS2.length;
|
|
4143
4153
|
function flowrikiHome() {
|
|
4144
|
-
return process.env.FLOWRIKI_HOME ||
|
|
4154
|
+
return process.env.FLOWRIKI_HOME || path3.join(os.homedir(), ".flowriki");
|
|
4145
4155
|
}
|
|
4146
4156
|
function runArtifactsDir(runId, projectId = "default") {
|
|
4147
|
-
return
|
|
4157
|
+
return path3.join(flowrikiHome(), "projects", safe(projectId), "runs", safe(runId));
|
|
4148
4158
|
}
|
|
4149
4159
|
function safe(segment) {
|
|
4150
4160
|
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(segment) || segment.includes("..")) {
|
|
@@ -4153,9 +4163,9 @@ function safe(segment) {
|
|
|
4153
4163
|
return segment;
|
|
4154
4164
|
}
|
|
4155
4165
|
function isInside(root, candidate) {
|
|
4156
|
-
const base =
|
|
4157
|
-
const target =
|
|
4158
|
-
return target === base || target.startsWith(base +
|
|
4166
|
+
const base = path3.resolve(root);
|
|
4167
|
+
const target = path3.resolve(candidate);
|
|
4168
|
+
return target === base || target.startsWith(base + path3.sep);
|
|
4159
4169
|
}
|
|
4160
4170
|
|
|
4161
4171
|
// ../orchestrator/dist/index.js
|
|
@@ -4227,7 +4237,7 @@ var Store = class {
|
|
|
4227
4237
|
}
|
|
4228
4238
|
root;
|
|
4229
4239
|
get featuresDir() {
|
|
4230
|
-
return
|
|
4240
|
+
return path4.join(this.root, ".flowriki", "features");
|
|
4231
4241
|
}
|
|
4232
4242
|
/** Every path that comes from a request goes through this. A slug is a
|
|
4233
4243
|
* single path segment and nothing else — no traversal, no absolute paths. */
|
|
@@ -4240,13 +4250,13 @@ var Store = class {
|
|
|
4240
4250
|
const out = [];
|
|
4241
4251
|
for (const entry of fs4.readdirSync(this.featuresDir, { withFileTypes: true })) {
|
|
4242
4252
|
if (!entry.isDirectory()) continue;
|
|
4243
|
-
const file =
|
|
4253
|
+
const file = path4.join(this.featuresDir, entry.name, "feature.json");
|
|
4244
4254
|
if (!fs4.existsSync(file)) continue;
|
|
4245
4255
|
try {
|
|
4246
4256
|
out.push(FeatureSchema.parse(JSON.parse(fs4.readFileSync(file, "utf8"))));
|
|
4247
4257
|
} catch (err) {
|
|
4248
4258
|
this.featureProblems.push({
|
|
4249
|
-
path:
|
|
4259
|
+
path: path4.relative(this.root, file),
|
|
4250
4260
|
error: String(err instanceof Error ? err.message : err).split("\n")[0].trim(),
|
|
4251
4261
|
recoverable: false
|
|
4252
4262
|
});
|
|
@@ -4257,7 +4267,7 @@ var Store = class {
|
|
|
4257
4267
|
featureProblems = [];
|
|
4258
4268
|
createFeature(name, description = "") {
|
|
4259
4269
|
const slug = this.assertSlug(slugify(name));
|
|
4260
|
-
const dir =
|
|
4270
|
+
const dir = path4.join(this.featuresDir, slug);
|
|
4261
4271
|
if (fs4.existsSync(dir)) throw new Error(`a feature named "${name}" already exists`);
|
|
4262
4272
|
fs4.mkdirSync(dir, { recursive: true });
|
|
4263
4273
|
const doc = {
|
|
@@ -4268,11 +4278,11 @@ var Store = class {
|
|
|
4268
4278
|
description: description.trim(),
|
|
4269
4279
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4270
4280
|
};
|
|
4271
|
-
fs4.writeFileSync(
|
|
4281
|
+
fs4.writeFileSync(path4.join(dir, "feature.json"), JSON.stringify(doc, null, 2) + "\n");
|
|
4272
4282
|
return doc;
|
|
4273
4283
|
}
|
|
4274
4284
|
deleteFeature(slug) {
|
|
4275
|
-
fs4.rmSync(
|
|
4285
|
+
fs4.rmSync(path4.join(this.featuresDir, this.assertSlug(slug)), { recursive: true, force: true });
|
|
4276
4286
|
}
|
|
4277
4287
|
/** Every flow that could be read.
|
|
4278
4288
|
*
|
|
@@ -4286,16 +4296,16 @@ var Store = class {
|
|
|
4286
4296
|
const out = [];
|
|
4287
4297
|
this.problemsFound = [];
|
|
4288
4298
|
for (const slug of features) {
|
|
4289
|
-
const dir =
|
|
4299
|
+
const dir = path4.join(this.featuresDir, slug);
|
|
4290
4300
|
if (!fs4.existsSync(dir)) continue;
|
|
4291
4301
|
for (const file of fs4.readdirSync(dir)) {
|
|
4292
4302
|
if (!file.endsWith(".flow.json")) continue;
|
|
4293
|
-
const full =
|
|
4303
|
+
const full = path4.join(dir, file);
|
|
4294
4304
|
try {
|
|
4295
4305
|
out.push(parseFlowDocument(JSON.parse(fs4.readFileSync(full, "utf8"))));
|
|
4296
4306
|
} catch (err) {
|
|
4297
4307
|
this.problemsFound.push({
|
|
4298
|
-
path:
|
|
4308
|
+
path: path4.relative(this.root, full),
|
|
4299
4309
|
error: String(err instanceof Error ? err.message : err).split("\n")[0].trim(),
|
|
4300
4310
|
recoverable: fs4.existsSync(this.recoveryPathFor(full))
|
|
4301
4311
|
});
|
|
@@ -4320,12 +4330,12 @@ var Store = class {
|
|
|
4320
4330
|
* Outside `features/`, because that directory is the product and is
|
|
4321
4331
|
* committed; a recovery copy is machine-local noise. */
|
|
4322
4332
|
recoveryPathFor(file) {
|
|
4323
|
-
return
|
|
4333
|
+
return path4.join(this.root, ".flowriki", ".recovery", path4.basename(file) + ".bak");
|
|
4324
4334
|
}
|
|
4325
4335
|
/** Restore a flow file from its recovery copy. Returns false when there is
|
|
4326
4336
|
* none, which is the honest answer rather than an invented document. */
|
|
4327
4337
|
recoverFlowFile(relativePath) {
|
|
4328
|
-
const file =
|
|
4338
|
+
const file = path4.join(this.root, relativePath);
|
|
4329
4339
|
const backup = this.recoveryPathFor(file);
|
|
4330
4340
|
if (!fs4.existsSync(backup)) return false;
|
|
4331
4341
|
fs4.copyFileSync(backup, file);
|
|
@@ -4345,9 +4355,9 @@ var Store = class {
|
|
|
4345
4355
|
saveFlow(input, opts = {}) {
|
|
4346
4356
|
const doc = parseFlowDocument(input);
|
|
4347
4357
|
const featureSlug = this.assertSlug(doc.featureId.replace(/^f_/, ""));
|
|
4348
|
-
const dir =
|
|
4358
|
+
const dir = path4.join(this.featuresDir, featureSlug);
|
|
4349
4359
|
fs4.mkdirSync(dir, { recursive: true });
|
|
4350
|
-
const file =
|
|
4360
|
+
const file = path4.join(dir, `${this.assertSlug(doc.slug)}.flow.json`);
|
|
4351
4361
|
const destination = this.readFlowFile(file);
|
|
4352
4362
|
if (fs4.existsSync(file) && (!destination || destination.id !== doc.id)) {
|
|
4353
4363
|
throw new FlowConflict("the destination contains another flow or an unreadable document \u2014 choose another name or recover it first", destination?.revision ?? 0);
|
|
@@ -4366,16 +4376,16 @@ var Store = class {
|
|
|
4366
4376
|
}
|
|
4367
4377
|
}
|
|
4368
4378
|
doc.revision = (current?.revision ?? doc.revision ?? 0) + 1;
|
|
4369
|
-
const previousFile = current ?
|
|
4379
|
+
const previousFile = current ? path4.join(this.root, this.relPath(current)) : file;
|
|
4370
4380
|
if (fs4.existsSync(previousFile)) {
|
|
4371
4381
|
const backup = this.recoveryPathFor(previousFile);
|
|
4372
|
-
fs4.mkdirSync(
|
|
4382
|
+
fs4.mkdirSync(path4.dirname(backup), { recursive: true });
|
|
4373
4383
|
fs4.copyFileSync(previousFile, backup);
|
|
4374
4384
|
this.ensureRecoveryIgnored();
|
|
4375
4385
|
}
|
|
4376
4386
|
writeFileAtomic(file, JSON.stringify(doc, null, 2) + "\n");
|
|
4377
4387
|
if (previousFile !== file) fs4.rmSync(previousFile);
|
|
4378
|
-
return
|
|
4388
|
+
return path4.relative(this.root, file);
|
|
4379
4389
|
}
|
|
4380
4390
|
/** Read one flow file, or null when it is missing or unreadable. */
|
|
4381
4391
|
readFlowFile(file) {
|
|
@@ -4417,10 +4427,10 @@ var Store = class {
|
|
|
4417
4427
|
deleteFlowDocument(doc) {
|
|
4418
4428
|
const featureSlug = this.assertSlug(doc.featureId.replace(/^f_/, ""));
|
|
4419
4429
|
this.assertSlug(doc.slug);
|
|
4420
|
-
fs4.rmSync(
|
|
4430
|
+
fs4.rmSync(path4.join(this.featuresDir, featureSlug, `${doc.slug}.flow.json`), { force: true });
|
|
4421
4431
|
}
|
|
4422
4432
|
relPath(doc) {
|
|
4423
|
-
return
|
|
4433
|
+
return path4.join(".flowriki", "features", doc.featureId.replace(/^f_/, ""), `${doc.slug}.flow.json`);
|
|
4424
4434
|
}
|
|
4425
4435
|
/** The environment a flow actually executes in.
|
|
4426
4436
|
*
|
|
@@ -4446,7 +4456,7 @@ var Store = class {
|
|
|
4446
4456
|
* per flow, and it travels with the repo like everything else in
|
|
4447
4457
|
* `.flowriki/`. */
|
|
4448
4458
|
get configPath() {
|
|
4449
|
-
return
|
|
4459
|
+
return path4.join(this.root, ".flowriki", "config.json");
|
|
4450
4460
|
}
|
|
4451
4461
|
readConfig() {
|
|
4452
4462
|
try {
|
|
@@ -4479,7 +4489,7 @@ var Store = class {
|
|
|
4479
4489
|
artifacts: ArtifactPolicySchema.parse(input.artifacts ?? existing.artifacts),
|
|
4480
4490
|
flakinessRepeat: clampRepeat(input.flakinessRepeat ?? existing.flakinessRepeat)
|
|
4481
4491
|
};
|
|
4482
|
-
fs4.mkdirSync(
|
|
4492
|
+
fs4.mkdirSync(path4.dirname(this.configPath), { recursive: true });
|
|
4483
4493
|
fs4.writeFileSync(this.configPath, JSON.stringify(config, null, 2) + "\n");
|
|
4484
4494
|
return config;
|
|
4485
4495
|
}
|
|
@@ -4504,7 +4514,7 @@ var Store = class {
|
|
|
4504
4514
|
return resolveStartUrl(this.baseUrlFor(flow), flow.startPath);
|
|
4505
4515
|
}
|
|
4506
4516
|
get secretsPath() {
|
|
4507
|
-
return
|
|
4517
|
+
return path4.join(this.root, ".flowriki", "secrets.local.json");
|
|
4508
4518
|
}
|
|
4509
4519
|
readSecrets() {
|
|
4510
4520
|
try {
|
|
@@ -4554,7 +4564,7 @@ var Store = class {
|
|
|
4554
4564
|
changed = true;
|
|
4555
4565
|
}
|
|
4556
4566
|
if (changed) {
|
|
4557
|
-
fs4.mkdirSync(
|
|
4567
|
+
fs4.mkdirSync(path4.dirname(this.secretsPath), { recursive: true });
|
|
4558
4568
|
fs4.writeFileSync(this.secretsPath, JSON.stringify(secrets, null, 2) + "\n", { mode: 384 });
|
|
4559
4569
|
this.ensureSecretsIgnored();
|
|
4560
4570
|
}
|
|
@@ -4567,14 +4577,14 @@ var Store = class {
|
|
|
4567
4577
|
* file. Committed alongside the flows, because a test that needs a 2KB
|
|
4568
4578
|
* sample CSV should not need a side-channel to share it. */
|
|
4569
4579
|
get filesDir() {
|
|
4570
|
-
return
|
|
4580
|
+
return path4.join(this.root, ".flowriki", "files");
|
|
4571
4581
|
}
|
|
4572
4582
|
/** Resolve an upload step's file name to a path, or undefined when it is
|
|
4573
4583
|
* not on this machine. */
|
|
4574
4584
|
resolveFile(name) {
|
|
4575
4585
|
const first = name;
|
|
4576
4586
|
if (!first || first.includes("/") || first.includes("\\") || first.includes("..")) return void 0;
|
|
4577
|
-
const file =
|
|
4587
|
+
const file = path4.join(this.filesDir, first);
|
|
4578
4588
|
return fs4.existsSync(file) ? file : void 0;
|
|
4579
4589
|
}
|
|
4580
4590
|
/** The secret references a flow needs that this machine does not hold.
|
|
@@ -4602,7 +4612,7 @@ var Store = class {
|
|
|
4602
4612
|
if (!/^[a-z0-9][a-z0-9_-]*$/.test(ref)) throw new Error(`invalid secret name: ${ref}`);
|
|
4603
4613
|
const secrets = this.readSecrets();
|
|
4604
4614
|
secrets[ref] = value;
|
|
4605
|
-
fs4.mkdirSync(
|
|
4615
|
+
fs4.mkdirSync(path4.dirname(this.secretsPath), { recursive: true });
|
|
4606
4616
|
fs4.writeFileSync(this.secretsPath, JSON.stringify(secrets, null, 2) + "\n", { mode: 384 });
|
|
4607
4617
|
this.ensureSecretsIgnored();
|
|
4608
4618
|
}
|
|
@@ -4643,7 +4653,7 @@ var Store = class {
|
|
|
4643
4653
|
}
|
|
4644
4654
|
}
|
|
4645
4655
|
if (healed.length) {
|
|
4646
|
-
fs4.mkdirSync(
|
|
4656
|
+
fs4.mkdirSync(path4.dirname(this.secretsPath), { recursive: true });
|
|
4647
4657
|
fs4.writeFileSync(this.secretsPath, JSON.stringify(secrets, null, 2) + "\n", { mode: 384 });
|
|
4648
4658
|
this.ensureSecretsIgnored();
|
|
4649
4659
|
}
|
|
@@ -4660,10 +4670,10 @@ var Store = class {
|
|
|
4660
4670
|
}
|
|
4661
4671
|
/** Add one entry to .flowriki/.gitignore if it is not already there. */
|
|
4662
4672
|
ensureIgnored(line, why) {
|
|
4663
|
-
const ignore =
|
|
4673
|
+
const ignore = path4.join(this.root, ".flowriki", ".gitignore");
|
|
4664
4674
|
const existing = fs4.existsSync(ignore) ? fs4.readFileSync(ignore, "utf8") : "";
|
|
4665
4675
|
if (existing.split("\n").includes(line)) return;
|
|
4666
|
-
fs4.mkdirSync(
|
|
4676
|
+
fs4.mkdirSync(path4.dirname(ignore), { recursive: true });
|
|
4667
4677
|
fs4.writeFileSync(
|
|
4668
4678
|
ignore,
|
|
4669
4679
|
`${existing}${existing && !existing.endsWith("\n") ? "\n" : ""}# ${why}
|
|
@@ -4984,12 +4994,19 @@ function redactPanel(state) {
|
|
|
4984
4994
|
if (!Array.isArray(panel.steps)) return state;
|
|
4985
4995
|
return { ...panel, steps: panel.steps.map((s) => redact(s)) };
|
|
4986
4996
|
}
|
|
4997
|
+
var START_GRACE_MS = 3e3;
|
|
4987
4998
|
var RecordingCoordinator = class {
|
|
4988
4999
|
constructor(deps) {
|
|
4989
5000
|
this.deps = deps;
|
|
4990
5001
|
}
|
|
4991
5002
|
deps;
|
|
4992
5003
|
session = null;
|
|
5004
|
+
/** The browser opening, while `start` has already answered.
|
|
5005
|
+
*
|
|
5006
|
+
* Held so that stop and status can wait for it rather than racing it: a
|
|
5007
|
+
* recording asked to stop a second after it was asked to start must not find
|
|
5008
|
+
* a half-built session. */
|
|
5009
|
+
starting = null;
|
|
4993
5010
|
draft = null;
|
|
4994
5011
|
stopInFlight = null;
|
|
4995
5012
|
active() {
|
|
@@ -4998,7 +5015,10 @@ var RecordingCoordinator = class {
|
|
|
4998
5015
|
}
|
|
4999
5016
|
status() {
|
|
5000
5017
|
return {
|
|
5001
|
-
active:
|
|
5018
|
+
// A browser still opening counts as active: `start` has answered, the
|
|
5019
|
+
// draft exists, and telling the dashboard "nothing is recording" in that
|
|
5020
|
+
// window is what makes a Stop pressed early do nothing at all.
|
|
5021
|
+
active: Boolean(this.session) || Boolean(this.draft && this.starting),
|
|
5002
5022
|
featureId: this.draft?.featureId ?? null,
|
|
5003
5023
|
name: this.draft?.name ?? null,
|
|
5004
5024
|
steps: (this.draft?.steps ?? []).map(redact),
|
|
@@ -5037,6 +5057,30 @@ var RecordingCoordinator = class {
|
|
|
5037
5057
|
startPath: "/",
|
|
5038
5058
|
recordedBy: recordedBy?.trim() || ""
|
|
5039
5059
|
};
|
|
5060
|
+
let failure = null;
|
|
5061
|
+
this.starting = this.open(target, opts).catch((error) => {
|
|
5062
|
+
failure = error;
|
|
5063
|
+
this.draft = null;
|
|
5064
|
+
this.session = null;
|
|
5065
|
+
this.deps.lease?.release("recording");
|
|
5066
|
+
this.deps.emit({ type: "recording.interrupted", reason: String(error.message).split("\n")[0] });
|
|
5067
|
+
return null;
|
|
5068
|
+
});
|
|
5069
|
+
await Promise.race([this.starting, new Promise((r) => setTimeout(r, START_GRACE_MS).unref?.())]);
|
|
5070
|
+
if (failure) throw failure;
|
|
5071
|
+
return { url: target };
|
|
5072
|
+
}
|
|
5073
|
+
/** Resolves once the browser is open, or the attempt has failed.
|
|
5074
|
+
*
|
|
5075
|
+
* For callers that need the session itself rather than the answer — the
|
|
5076
|
+
* local server driving a panel, and every test that types into the page. */
|
|
5077
|
+
async ready() {
|
|
5078
|
+
await this.starting?.catch(() => null);
|
|
5079
|
+
}
|
|
5080
|
+
/** Everything that used to happen before `start` answered. */
|
|
5081
|
+
async open(target, opts) {
|
|
5082
|
+
const config = this.deps.store.readConfig();
|
|
5083
|
+
const configuredBase = config.baseUrl;
|
|
5040
5084
|
try {
|
|
5041
5085
|
this.session = await startRecordingSession({
|
|
5042
5086
|
url: target,
|
|
@@ -5067,12 +5111,13 @@ var RecordingCoordinator = class {
|
|
|
5067
5111
|
throw err;
|
|
5068
5112
|
}
|
|
5069
5113
|
this.deps.lease?.activate("recording");
|
|
5114
|
+
if (!this.session || !this.draft) return null;
|
|
5070
5115
|
const opened = new URL(this.session.startUrl);
|
|
5071
5116
|
const projectOrigin = configuredBase ? new URL(normalizeStartUrl(configuredBase)).origin : "";
|
|
5072
5117
|
this.draft.baseUrl = opened.origin === projectOrigin ? "" : opened.origin;
|
|
5073
5118
|
this.draft.startPath = opened.pathname + opened.search;
|
|
5074
5119
|
this.deps.emit({ type: "recording.started", name: this.draft.name, url: this.session.startUrl });
|
|
5075
|
-
return
|
|
5120
|
+
return null;
|
|
5076
5121
|
}
|
|
5077
5122
|
/** onStep fires more than once per step — again when the outcome window
|
|
5078
5123
|
* resolves and attaches assertions — so this is an upsert, not an append. */
|
|
@@ -5104,6 +5149,8 @@ var RecordingCoordinator = class {
|
|
|
5104
5149
|
return this.stopInFlight;
|
|
5105
5150
|
}
|
|
5106
5151
|
async doStop() {
|
|
5152
|
+
await this.starting?.catch(() => null);
|
|
5153
|
+
this.starting = null;
|
|
5107
5154
|
const draft = this.draft;
|
|
5108
5155
|
if (!draft) return { ok: false, error: "no recording in progress" };
|
|
5109
5156
|
const environment = this.session?.environment;
|
|
@@ -5526,8 +5573,26 @@ var CommandSchema = z4.object({
|
|
|
5526
5573
|
/** Wall-clock expiry. A command that surfaced from a queue an hour late must
|
|
5527
5574
|
* not start a browser on somebody's machine. */
|
|
5528
5575
|
expiresAt: z4.number().int().positive(),
|
|
5576
|
+
/** When the service stamped this, by the service's clock.
|
|
5577
|
+
*
|
|
5578
|
+
* Present so the agent never has to compare a timestamp minted on another
|
|
5579
|
+
* machine against its own clock. `expiresAt` alone made the deadline
|
|
5580
|
+
* meaningless the moment two clocks disagreed: a machine running five
|
|
5581
|
+
* minutes fast rejected every command the instant it arrived, saying it had
|
|
5582
|
+
* expired before it got there — which is true only if you believe both
|
|
5583
|
+
* clocks. Optional, so an older service that does not send it still works;
|
|
5584
|
+
* the agent falls back to the absolute deadline it has always used. */
|
|
5585
|
+
issuedAt: z4.number().int().positive().optional(),
|
|
5529
5586
|
args: z4.record(z4.unknown()).default({})
|
|
5530
|
-
|
|
5587
|
+
// UNKNOWN FIELDS ARE IGNORED, not refused.
|
|
5588
|
+
//
|
|
5589
|
+
// Strictness here looked like caution and was a compatibility trap: the day
|
|
5590
|
+
// the service added `issuedAt`, every agent older than that release began
|
|
5591
|
+
// refusing every command as unrecognised — over a field it had no need to
|
|
5592
|
+
// read. The allowlist on command NAMES is the security boundary and is
|
|
5593
|
+
// unchanged; which extra keys ride along is not, because nothing acts on a
|
|
5594
|
+
// field it does not know about.
|
|
5595
|
+
});
|
|
5531
5596
|
var AckSchema = z4.object({
|
|
5532
5597
|
type: z4.literal("ack"),
|
|
5533
5598
|
commandId: z4.string().min(1),
|
|
@@ -5652,13 +5717,13 @@ var EventJournal = class {
|
|
|
5652
5717
|
return this.state ? this.state.size() : this.events.length;
|
|
5653
5718
|
}
|
|
5654
5719
|
};
|
|
5655
|
-
var { DatabaseSync } =
|
|
5720
|
+
var { DatabaseSync } = createRequire2(import.meta.url)("node:sqlite");
|
|
5656
5721
|
var AgentState = class {
|
|
5657
5722
|
constructor(file, deviceId, commandLimit = 1e5) {
|
|
5658
5723
|
this.commandLimit = commandLimit;
|
|
5659
5724
|
if (!file || file === ":memory:") throw new Error("a persistent agent state file is required");
|
|
5660
5725
|
if (!Number.isInteger(commandLimit) || commandLimit < 1) throw new Error("invalid command capacity");
|
|
5661
|
-
fs5.mkdirSync(
|
|
5726
|
+
fs5.mkdirSync(path5.dirname(file), { recursive: true, mode: 448 });
|
|
5662
5727
|
const fd = fs5.openSync(file, "a", 384);
|
|
5663
5728
|
fs5.closeSync(fd);
|
|
5664
5729
|
fs5.chmodSync(file, 384);
|
|
@@ -5708,7 +5773,7 @@ var AgentState = class {
|
|
|
5708
5773
|
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, v]) => [key, canonical(v)]));
|
|
5709
5774
|
return value;
|
|
5710
5775
|
};
|
|
5711
|
-
const { expiresAt: _expiry, ...work } = command;
|
|
5776
|
+
const { expiresAt: _expiry, issuedAt: _issued, ...work } = command;
|
|
5712
5777
|
const text = JSON.stringify(canonical(work));
|
|
5713
5778
|
if (Buffer.byteLength(text) > 256 * 1024) throw new Error("command exceeds local size limit");
|
|
5714
5779
|
return crypto22.createHash("sha256").update(text).digest("hex");
|
|
@@ -5872,7 +5937,9 @@ var Agent = class {
|
|
|
5872
5937
|
}
|
|
5873
5938
|
const previous = this.previous(command.id, fingerprint2);
|
|
5874
5939
|
if (previous) return previous;
|
|
5875
|
-
|
|
5940
|
+
const ttl = command.issuedAt === void 0 ? null : command.expiresAt - command.issuedAt;
|
|
5941
|
+
const expired = ttl === null ? command.expiresAt <= this.now() : ttl <= 0;
|
|
5942
|
+
if (expired) {
|
|
5876
5943
|
return this.refuse(command.id, "expired", "the command expired before it reached this machine");
|
|
5877
5944
|
}
|
|
5878
5945
|
const handler = this.opts.handlers[command.name];
|
|
@@ -6781,7 +6848,7 @@ function createAgentRuntime(options) {
|
|
|
6781
6848
|
|
|
6782
6849
|
// src/engine.ts
|
|
6783
6850
|
import fs9 from "fs";
|
|
6784
|
-
import
|
|
6851
|
+
import path7 from "path";
|
|
6785
6852
|
|
|
6786
6853
|
// src/credentials.ts
|
|
6787
6854
|
import crypto3 from "crypto";
|
|
@@ -6790,19 +6857,19 @@ import fs7 from "fs";
|
|
|
6790
6857
|
// src/paths.ts
|
|
6791
6858
|
import fs6 from "fs";
|
|
6792
6859
|
import os3 from "os";
|
|
6793
|
-
import
|
|
6860
|
+
import path6 from "path";
|
|
6794
6861
|
function dataDir(env = process.env) {
|
|
6795
6862
|
const override = env["FLOWRIKI_HOME"]?.trim();
|
|
6796
|
-
if (override) return
|
|
6863
|
+
if (override) return path6.resolve(override);
|
|
6797
6864
|
if (process.platform === "win32") {
|
|
6798
6865
|
const appData = env["APPDATA"]?.trim();
|
|
6799
|
-
return appData ?
|
|
6866
|
+
return appData ? path6.join(appData, "Flowriki") : path6.join(os3.homedir(), "AppData", "Roaming", "Flowriki");
|
|
6800
6867
|
}
|
|
6801
6868
|
if (process.platform === "darwin") {
|
|
6802
|
-
return
|
|
6869
|
+
return path6.join(os3.homedir(), "Library", "Application Support", "Flowriki");
|
|
6803
6870
|
}
|
|
6804
6871
|
const state = env["XDG_STATE_HOME"]?.trim();
|
|
6805
|
-
return state ?
|
|
6872
|
+
return state ? path6.join(state, "flowriki") : path6.join(os3.homedir(), ".local", "state", "flowriki");
|
|
6806
6873
|
}
|
|
6807
6874
|
function ensureDataDir(env = process.env) {
|
|
6808
6875
|
const dir = dataDir(env);
|
|
@@ -6815,9 +6882,9 @@ function ensureDataDir(env = process.env) {
|
|
|
6815
6882
|
}
|
|
6816
6883
|
return dir;
|
|
6817
6884
|
}
|
|
6818
|
-
var credentialsPath = (env = process.env) =>
|
|
6819
|
-
var lockPath = (env = process.env) =>
|
|
6820
|
-
var ledgerPath = (deviceId, env = process.env) =>
|
|
6885
|
+
var credentialsPath = (env = process.env) => path6.join(dataDir(env), "credentials.json");
|
|
6886
|
+
var lockPath = (env = process.env) => path6.join(dataDir(env), "agent.lock");
|
|
6887
|
+
var ledgerPath = (deviceId, env = process.env) => path6.join(dataDir(env), `ledger-${deviceId}.sqlite`);
|
|
6821
6888
|
|
|
6822
6889
|
// src/credentials.ts
|
|
6823
6890
|
function readFile(env = process.env) {
|
|
@@ -6971,7 +7038,7 @@ function openBrowser(url, env = process.env) {
|
|
|
6971
7038
|
}
|
|
6972
7039
|
|
|
6973
7040
|
// src/engine.ts
|
|
6974
|
-
var AGENT_VERSION = "0.1.0-beta.
|
|
7041
|
+
var AGENT_VERSION = "0.1.0-beta.6";
|
|
6975
7042
|
async function connect(options = {}) {
|
|
6976
7043
|
let service;
|
|
6977
7044
|
try {
|
|
@@ -7054,7 +7121,7 @@ function resolveCheckouts(dirs) {
|
|
|
7054
7121
|
const projects = [];
|
|
7055
7122
|
const skipped = [];
|
|
7056
7123
|
for (const dir of dirs) {
|
|
7057
|
-
const root =
|
|
7124
|
+
const root = path7.resolve(dir);
|
|
7058
7125
|
if (!fs9.existsSync(root)) {
|
|
7059
7126
|
skipped.push({ root, why: "no such folder" });
|
|
7060
7127
|
continue;
|
|
@@ -7154,7 +7221,7 @@ async function prepareBrowsers() {
|
|
|
7154
7221
|
}
|
|
7155
7222
|
let cli;
|
|
7156
7223
|
try {
|
|
7157
|
-
cli =
|
|
7224
|
+
cli = createRequire3(import.meta.url).resolve("playwright/cli.js");
|
|
7158
7225
|
} catch {
|
|
7159
7226
|
return {
|
|
7160
7227
|
ok: false,
|
|
@@ -7239,7 +7306,7 @@ async function doctor(serviceArg) {
|
|
|
7239
7306
|
}
|
|
7240
7307
|
function inspectPlaywright() {
|
|
7241
7308
|
try {
|
|
7242
|
-
const browsersPath = process.env["PLAYWRIGHT_BROWSERS_PATH"] ?? (process.platform === "win32" ?
|
|
7309
|
+
const browsersPath = process.env["PLAYWRIGHT_BROWSERS_PATH"] ?? (process.platform === "win32" ? path7.join(os5.homedir(), "AppData", "Local", "ms-playwright") : process.platform === "darwin" ? path7.join(os5.homedir(), "Library", "Caches", "ms-playwright") : path7.join(os5.homedir(), ".cache", "ms-playwright"));
|
|
7243
7310
|
const chromiumInstalled = fs9.existsSync(browsersPath) ? fs9.readdirSync(browsersPath).some((d) => d.startsWith("chromium")) : false;
|
|
7244
7311
|
return { version: null, browsersPath, chromiumInstalled };
|
|
7245
7312
|
} catch {
|
package/dist/cli.js
CHANGED
package/dist/engine.js
CHANGED
package/package.json
CHANGED